Event handling customisationpull/1240/head
@ -0,0 +1,74 @@ | |||||
using System.Threading.Tasks; | |||||
using System; | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; | |||||
using System.Net; | |||||
using System.IO; | |||||
using System.Net.Http; | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; | |||||
using Microsoft.Extensions.Logging; | |||||
namespace Ordering.API.Application.IntegrationEvents.EventHandling | |||||
{ | |||||
public abstract class AbstractIntegrationEventHandler<IIntegrationEvent> | |||||
{ | |||||
private static String url = @"http://tenantmanager/"; | |||||
private readonly IEventBus _eventBus; | |||||
//private readonly ILogger<AbstractIntegrationEventHandler<IIntegrationEvent>> _logger; | |||||
protected AbstractIntegrationEventHandler(IEventBus eventBus) | |||||
{ | |||||
_eventBus = eventBus ?? throw new ArgumentNullException(nameof(eventBus)); | |||||
} | |||||
public async Task<bool> CheckIfCustomised(IntegrationEvent @event) | |||||
{ | |||||
if (!@event.CheckForCustomisation) | |||||
{ | |||||
return false; | |||||
} | |||||
Boolean result = Get(@event); | |||||
if (result) | |||||
{ | |||||
CustomisationEvent customisationEvent = new CustomisationEvent(1, @event); | |||||
try | |||||
{ | |||||
//_logger.LogInformation("----- Publishing integration event: {IntegrationEventId} from {AppName} - ({@IntegrationEvent})", eventMessage.Id, Program.AppName, eventMessage); | |||||
_eventBus.Publish(customisationEvent); | |||||
} | |||||
catch (Exception ex) | |||||
{ | |||||
//_logger.LogError(ex, "ERROR Publishing integration event: {IntegrationEventId} from {AppName}", eventMessage.Id, Program.AppName); | |||||
throw; | |||||
} | |||||
} | |||||
return result; | |||||
} | |||||
private Boolean Get(IntegrationEvent @event) | |||||
{ | |||||
//TODO return true/false | |||||
Console.WriteLine("Making API Call..."); | |||||
using (var client = new HttpClient(new HttpClientHandler { AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate })) | |||||
{ | |||||
client.BaseAddress = new Uri(url); | |||||
try | |||||
{ | |||||
HttpResponseMessage response = client.GetAsync("api/tenants").Result; | |||||
response.EnsureSuccessStatusCode(); | |||||
string result = response.Content.ReadAsStringAsync().Result; | |||||
Console.WriteLine("Result: " + result); | |||||
} | |||||
catch(Exception e) | |||||
{ | |||||
Console.WriteLine(e); | |||||
} | |||||
} | |||||
return true; | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,20 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events | |||||
{ | |||||
public class CustomisationEvent : IntegrationEvent | |||||
{ | |||||
public CustomisationEvent(int tenantId, IntegrationEvent @event) | |||||
{ | |||||
TenantId = tenantId; | |||||
this.@event = @event; | |||||
eventType = @event.GetType().Name; | |||||
} | |||||
public int TenantId { get; set; } | |||||
public IntegrationEvent @event { get; set; } | |||||
public String eventType { get; set; } | |||||
} | |||||
} |
@ -0,0 +1,183 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
using Microsoft.AspNetCore.Http; | |||||
using Microsoft.AspNetCore.Mvc; | |||||
using Microsoft.EntityFrameworkCore; | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; | |||||
using Microsoft.Extensions.Logging; | |||||
using Newtonsoft.Json; | |||||
using TenantACustomisations.Database; | |||||
using TenantACustomisations.IntegrationEvents.Events; | |||||
namespace TenantACustomisations.Controllers | |||||
{ | |||||
[Route("api/[controller]")] | |||||
[ApiController] | |||||
public class SavedEventsController : ControllerBase | |||||
{ | |||||
private readonly TenantAContext _context; | |||||
private readonly ILogger<SavedEventsController> _logger; | |||||
private readonly IEventBus _eventBus; | |||||
private List<Type> types = new List<Type>() | |||||
{ | |||||
typeof(OrderStatusChangedToSubmittedIntegrationEvent), | |||||
typeof(OrderStatusChangedToAwaitingValidationIntegrationEvent) | |||||
}; | |||||
public SavedEventsController(TenantAContext context, ILogger<SavedEventsController> logger, IEventBus eventBus) | |||||
{ | |||||
_context = context; | |||||
_logger = logger; | |||||
_eventBus = eventBus; | |||||
} | |||||
// GET: api/SavedEvents | |||||
[HttpGet] | |||||
public async Task<ActionResult<IEnumerable<SavedEvent>>> GetSavedEvent(String orderId) | |||||
{ | |||||
if (String.IsNullOrEmpty(orderId)) | |||||
{ | |||||
return await _context.SavedEvent.ToListAsync(); | |||||
} | |||||
//Getting saved events | |||||
var savedEvents = await _context.SavedEvent.ToListAsync(); | |||||
//Returning if list is empty | |||||
if (savedEvents.Count == 0) | |||||
{ | |||||
return NotFound(); | |||||
} | |||||
List<IntegrationEvent> events = new List<IntegrationEvent>(); | |||||
//Converting events to actual type | |||||
savedEvents.ForEach(e => | |||||
{ | |||||
var integrationEvent =JsonConvert.DeserializeObject(e.Content, GetEventTypeByName(e.EventName)); | |||||
IntegrationEvent evt = (IntegrationEvent)integrationEvent; | |||||
events.Add(evt); | |||||
}); | |||||
bool found = false; | |||||
//Casting to class to check the orderId | |||||
events.ForEach(e => | |||||
{ | |||||
if(e is OrderStatusChangedToAwaitingValidationIntegrationEvent) | |||||
{ | |||||
OrderStatusChangedToAwaitingValidationIntegrationEvent evt = (OrderStatusChangedToAwaitingValidationIntegrationEvent)e; | |||||
if (evt.OrderId == Int32.Parse(orderId)) | |||||
{ | |||||
found = true; | |||||
} | |||||
} | |||||
else if(e is OrderStatusChangedToSubmittedIntegrationEvent) | |||||
{ | |||||
OrderStatusChangedToSubmittedIntegrationEvent evt = (OrderStatusChangedToSubmittedIntegrationEvent)e; | |||||
if (evt.OrderId == Int32.Parse(orderId)) | |||||
{ | |||||
found = true; | |||||
} | |||||
} | |||||
}); | |||||
if (!found) | |||||
{ | |||||
return NotFound(); | |||||
} | |||||
return savedEvents; | |||||
} | |||||
// PUT: api/SavedEvents/5 | |||||
[HttpPut("{id}")] | |||||
public async Task<IActionResult> PutSavedEvent(string id, SavedEvent savedEvent) | |||||
{ | |||||
if (id != savedEvent.SavedEventId) | |||||
{ | |||||
return BadRequest(); | |||||
} | |||||
_context.Entry(savedEvent).State = EntityState.Modified; | |||||
try | |||||
{ | |||||
await _context.SaveChangesAsync(); | |||||
} | |||||
catch (DbUpdateConcurrencyException) | |||||
{ | |||||
if (!SavedEventExists(id)) | |||||
{ | |||||
return NotFound(); | |||||
} | |||||
else | |||||
{ | |||||
throw; | |||||
} | |||||
} | |||||
return NoContent(); | |||||
} | |||||
// POST: api/SavedEvents | |||||
[HttpPost] | |||||
public async Task<ActionResult<SavedEvent>> PostSavedEvent(SavedEvent savedEvent) | |||||
{ | |||||
_context.SavedEvent.Add(savedEvent); | |||||
await _context.SaveChangesAsync(); | |||||
return CreatedAtAction("GetSavedEvent", new {id = savedEvent.SavedEventId}, savedEvent); | |||||
} | |||||
// DELETE: api/SavedEvents/5 | |||||
[HttpDelete("{id}")] | |||||
public async Task<ActionResult<SavedEvent>> DeleteSavedEvent(string id) | |||||
{ | |||||
var savedEvent = await _context.SavedEvent.FindAsync(id); | |||||
if (savedEvent == null) | |||||
{ | |||||
return NotFound(); | |||||
} | |||||
var integrationEvent = | |||||
JsonConvert.DeserializeObject(savedEvent.Content, GetEventTypeByName(savedEvent.EventName)); | |||||
IntegrationEvent evt = (IntegrationEvent) integrationEvent; | |||||
try | |||||
{ | |||||
_logger.LogInformation( | |||||
"----- Publishing integration event: {IntegrationEventId} from OrderStatusChangedToSubmittedIntegrationEventsController - ({@IntegrationEvent})", | |||||
evt.Id, evt); | |||||
evt.CheckForCustomisation = false; | |||||
_eventBus.Publish(evt); | |||||
_context.SavedEvent.Remove(savedEvent); | |||||
await _context.SaveChangesAsync(); | |||||
return savedEvent; | |||||
} | |||||
catch (Exception ex) | |||||
{ | |||||
_logger.LogError(ex, | |||||
"ERROR Publishing integration event: {IntegrationEventId} from OrderStatusChangedToSubmittedIntegrationEventsController", | |||||
evt.Id); | |||||
throw; | |||||
} | |||||
_context.SavedEvent.Remove(savedEvent); | |||||
await _context.SaveChangesAsync(); | |||||
return savedEvent; | |||||
} | |||||
private bool SavedEventExists(string id) | |||||
{ | |||||
return _context.SavedEvent.Any(e => e.SavedEventId == id); | |||||
} | |||||
private Type GetEventTypeByName(string eventName) => types.SingleOrDefault(t => t.Name == eventName); | |||||
} | |||||
} |
@ -0,0 +1,106 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
using Microsoft.AspNetCore.Http; | |||||
using Microsoft.AspNetCore.Mvc; | |||||
using Microsoft.EntityFrameworkCore; | |||||
using TenantACustomisations.Database; | |||||
using TenantACustomisations.ExternalServices; | |||||
namespace TenantACustomisations.Controllers | |||||
{ | |||||
[Route("api/[controller]")] | |||||
[ApiController] | |||||
public class ShippingInformationsController : ControllerBase | |||||
{ | |||||
private readonly TenantAContext _context; | |||||
public ShippingInformationsController(TenantAContext context) | |||||
{ | |||||
_context = context; | |||||
} | |||||
// GET: api/ShippingInformations | |||||
[HttpGet] | |||||
public async Task<ActionResult<IEnumerable<ShippingInformation>>> GetShippingInformation() | |||||
{ | |||||
return await _context.ShippingInformation.ToListAsync(); | |||||
} | |||||
// GET: api/ShippingInformations/5 | |||||
[HttpGet("{id}")] | |||||
public async Task<ActionResult<ShippingInformation>> GetShippingInformation(int id) | |||||
{ | |||||
var shippingInformation = await _context.ShippingInformation.FindAsync(id); | |||||
if (shippingInformation == null) | |||||
{ | |||||
return NotFound(); | |||||
} | |||||
return shippingInformation; | |||||
} | |||||
// PUT: api/ShippingInformations/5 | |||||
[HttpPut("{id}")] | |||||
public async Task<IActionResult> PutShippingInformation(int id, ShippingInformation shippingInformation) | |||||
{ | |||||
if (id != shippingInformation.ShippingInformationId) | |||||
{ | |||||
return BadRequest(); | |||||
} | |||||
_context.Entry(shippingInformation).State = EntityState.Modified; | |||||
try | |||||
{ | |||||
await _context.SaveChangesAsync(); | |||||
} | |||||
catch (DbUpdateConcurrencyException) | |||||
{ | |||||
if (!ShippingInformationExists(id)) | |||||
{ | |||||
return NotFound(); | |||||
} | |||||
else | |||||
{ | |||||
throw; | |||||
} | |||||
} | |||||
return NoContent(); | |||||
} | |||||
// POST: api/ShippingInformations | |||||
[HttpPost] | |||||
public async Task<ActionResult<ShippingInformation>> PostShippingInformation(ShippingInformation shippingInformation) | |||||
{ | |||||
_context.ShippingInformation.Add(shippingInformation); | |||||
await _context.SaveChangesAsync(); | |||||
return CreatedAtAction("GetShippingInformation", new { id = shippingInformation.ShippingInformationId }, shippingInformation); | |||||
} | |||||
// DELETE: api/ShippingInformations/5 | |||||
[HttpDelete("{id}")] | |||||
public async Task<ActionResult<ShippingInformation>> DeleteShippingInformation(int id) | |||||
{ | |||||
var shippingInformation = await _context.ShippingInformation.FindAsync(id); | |||||
if (shippingInformation == null) | |||||
{ | |||||
return NotFound(); | |||||
} | |||||
_context.ShippingInformation.Remove(shippingInformation); | |||||
await _context.SaveChangesAsync(); | |||||
return shippingInformation; | |||||
} | |||||
private bool ShippingInformationExists(int id) | |||||
{ | |||||
return _context.ShippingInformation.Any(e => e.ShippingInformationId == id); | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,58 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
using Microsoft.AspNetCore.Mvc; | |||||
using Microsoft.EntityFrameworkCore; | |||||
using TenantACustomisations.Database; | |||||
using TenantACustomisations.ExternalServices; | |||||
namespace TenantACustomisations.Controllers | |||||
{ | |||||
[Route("api/[controller]")] | |||||
[ApiController] | |||||
public class ValuesController : ControllerBase | |||||
{ | |||||
private readonly TenantAContext _context; | |||||
public ValuesController(TenantAContext context) | |||||
{ | |||||
_context = context ?? throw new ArgumentNullException(nameof(context)); | |||||
} | |||||
// GET api/values | |||||
[HttpGet] | |||||
public async Task<ActionResult<IEnumerable<ShippingInformation>>> GetShippingInformation() | |||||
{ | |||||
return await _context.ShippingInformation.ToListAsync(); | |||||
} | |||||
// GET api/values/5 | |||||
[HttpGet("{id}")] | |||||
public ActionResult<string> Get(int id) | |||||
{ | |||||
return "value"; | |||||
} | |||||
// POST api/values | |||||
[HttpPost] | |||||
public void Post([FromBody] string value) | |||||
{ | |||||
} | |||||
// PUT api/values/5 | |||||
[HttpPut("{id}")] | |||||
public void Put(int id, [FromBody] string value) | |||||
{ | |||||
} | |||||
// DELETE api/values/5 | |||||
[HttpDelete("{id}")] | |||||
public void Delete(int id) | |||||
{ | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,34 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
using TenantACustomisations.ExternalServices; | |||||
namespace TenantACustomisations.Database | |||||
{ | |||||
public class DbInitializer | |||||
{ | |||||
public void Initialize(TenantAContext context) | |||||
{ | |||||
context.Database.EnsureCreated(); | |||||
if (context.ShippingInformation.Any()) | |||||
{ | |||||
return; | |||||
} | |||||
ShippingInformation shippingInformation = new ShippingInformation(); | |||||
shippingInformation.ShippingTime = DateTime.Today; | |||||
shippingInformation.ArrivalTime = DateTime.Today.AddDays(2); | |||||
shippingInformation.FragilityLevel = Fragility.Medium; | |||||
shippingInformation.PriorityLevel = Priority.High; | |||||
shippingInformation.ShippingInformationId = 1; | |||||
shippingInformation.OrderNumber = "1"; | |||||
context.ShippingInformation.Add(shippingInformation); | |||||
context.SaveChanges(); | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,11 @@ | |||||
using System; | |||||
namespace TenantACustomisations.Database | |||||
{ | |||||
public class SavedEvent | |||||
{ | |||||
public string SavedEventId { get; set; } | |||||
public string Content { get; set; } | |||||
public String EventName { get; set; } | |||||
} | |||||
} |
@ -0,0 +1,38 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
using Microsoft.EntityFrameworkCore; | |||||
using Microsoft.EntityFrameworkCore.Design; | |||||
using TenantACustomisations.ExternalServices; | |||||
using TenantACustomisations.IntegrationEvents.Events; | |||||
namespace TenantACustomisations.Database | |||||
{ | |||||
public class TenantAContext : DbContext | |||||
{ | |||||
public TenantAContext(DbContextOptions<TenantAContext> options) | |||||
: base(options) | |||||
{ | |||||
} | |||||
public DbSet<ShippingInformation> ShippingInformation { get; set; } | |||||
public DbSet<SavedEvent> SavedEvent | |||||
{ | |||||
get; | |||||
set; | |||||
} | |||||
} | |||||
public class TenantAContextDesignFactory : IDesignTimeDbContextFactory<TenantAContext> | |||||
{ | |||||
public TenantAContext CreateDbContext(string[] args) | |||||
{ | |||||
var optionsBuilder = new DbContextOptionsBuilder<TenantAContext>() | |||||
.UseSqlServer("Server=.;Initial Catalog=Microsoft.eShopOnContainers.Services.TenantADb;Integrated Security=true"); | |||||
return new TenantAContext(optionsBuilder.Options); | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,59 @@ | |||||
FROM mcr.microsoft.com/dotnet/core/aspnet:2.2-stretch-slim AS base | |||||
WORKDIR /app | |||||
EXPOSE 80 | |||||
FROM mcr.microsoft.com/dotnet/core/sdk:2.2-stretch AS build | |||||
WORKDIR /src | |||||
# Keep the project list and command dotnet restore identical in all Dockfiles to maximize image cache utilization | |||||
COPY eShopOnContainers-ServicesAndWebApps.sln . | |||||
COPY docker-compose.dcproj /src/ | |||||
COPY src/ApiGateways/ApiGw-Base/OcelotApiGw.csproj src/ApiGateways/ApiGw-Base/ | |||||
COPY src/ApiGateways/Mobile.Bff.Shopping/aggregator/Mobile.Shopping.HttpAggregator.csproj src/ApiGateways/Mobile.Bff.Shopping/aggregator/ | |||||
COPY src/ApiGateways/Web.Bff.Shopping/aggregator/Web.Shopping.HttpAggregator.csproj src/ApiGateways/Web.Bff.Shopping/aggregator/ | |||||
COPY src/BuildingBlocks/Devspaces.Support/Devspaces.Support.csproj src/BuildingBlocks/Devspaces.Support/ | |||||
COPY src/BuildingBlocks/EventBus/EventBus/EventBus.csproj src/BuildingBlocks/EventBus/EventBus/ | |||||
COPY src/BuildingBlocks/EventBus/EventBus.Tests/EventBus.Tests.csproj src/BuildingBlocks/EventBus/EventBus.Tests/ | |||||
COPY src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.csproj src/BuildingBlocks/EventBus/EventBusRabbitMQ/ | |||||
COPY src/BuildingBlocks/EventBus/EventBusServiceBus/EventBusServiceBus.csproj src/BuildingBlocks/EventBus/EventBusServiceBus/ | |||||
COPY src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEF.csproj src/BuildingBlocks/EventBus/IntegrationEventLogEF/ | |||||
COPY src/BuildingBlocks/WebHostCustomization/WebHost.Customization/WebHost.Customization.csproj src/BuildingBlocks/WebHostCustomization/WebHost.Customization/ | |||||
COPY src/Services/Basket/Basket.API/Basket.API.csproj src/Services/Basket/Basket.API/ | |||||
COPY src/Services/Basket/Basket.FunctionalTests/Basket.FunctionalTests.csproj src/Services/Basket/Basket.FunctionalTests/ | |||||
COPY src/Services/Basket/Basket.UnitTests/Basket.UnitTests.csproj src/Services/Basket/Basket.UnitTests/ | |||||
COPY src/Services/Catalog/Catalog.API/Catalog.API.csproj src/Services/Catalog/Catalog.API/ | |||||
COPY src/Services/Catalog/Catalog.FunctionalTests/Catalog.FunctionalTests.csproj src/Services/Catalog/Catalog.FunctionalTests/ | |||||
COPY src/Services/Catalog/Catalog.UnitTests/Catalog.UnitTests.csproj src/Services/Catalog/Catalog.UnitTests/ | |||||
COPY src/Services/Identity/Identity.API/Identity.API.csproj src/Services/Identity/Identity.API/ | |||||
COPY src/Services/Location/Locations.API/Locations.API.csproj src/Services/Location/Locations.API/ | |||||
COPY src/Services/Location/Locations.FunctionalTests/Locations.FunctionalTests.csproj src/Services/Location/Locations.FunctionalTests/ | |||||
COPY src/Services/Marketing/Marketing.API/Marketing.API.csproj src/Services/Marketing/Marketing.API/ | |||||
COPY src/Services/Marketing/Marketing.FunctionalTests/Marketing.FunctionalTests.csproj src/Services/Marketing/Marketing.FunctionalTests/ | |||||
COPY src/Services/Ordering/Ordering.API/Ordering.API.csproj src/Services/Ordering/Ordering.API/ | |||||
COPY src/Services/Ordering/Ordering.BackgroundTasks/Ordering.BackgroundTasks.csproj src/Services/Ordering/Ordering.BackgroundTasks/ | |||||
COPY src/Services/Ordering/Ordering.Domain/Ordering.Domain.csproj src/Services/Ordering/Ordering.Domain/ | |||||
COPY src/Services/Ordering/Ordering.FunctionalTests/Ordering.FunctionalTests.csproj src/Services/Ordering/Ordering.FunctionalTests/ | |||||
COPY src/Services/Ordering/Ordering.Infrastructure/Ordering.Infrastructure.csproj src/Services/Ordering/Ordering.Infrastructure/ | |||||
COPY src/Services/Ordering/Ordering.SignalrHub/Ordering.SignalrHub.csproj src/Services/Ordering/Ordering.SignalrHub/ | |||||
COPY src/Services/Ordering/Ordering.UnitTests/Ordering.UnitTests.csproj src/Services/Ordering/Ordering.UnitTests/ | |||||
COPY src/Services/Payment/Payment.API/Payment.API.csproj src/Services/Payment/Payment.API/ | |||||
COPY src/Services/Webhooks/Webhooks.API/Webhooks.API.csproj src/Services/Webhooks/Webhooks.API/ | |||||
COPY src/Web/WebhookClient/WebhookClient.csproj src/Web/WebhookClient/ | |||||
COPY src/Web/WebMVC/WebMVC.csproj src/Web/WebMVC/ | |||||
COPY src/Web/WebSPA/WebSPA.csproj src/Web/WebSPA/ | |||||
COPY src/Web/WebStatus/WebStatus.csproj src/Web/WebStatus/ | |||||
COPY test/ServicesTests/Application.FunctionalTests/Application.FunctionalTests.csproj test/ServicesTests/Application.FunctionalTests/ | |||||
COPY test/ServicesTests/LoadTest/LoadTest.csproj test/ServicesTests/LoadTest/ | |||||
RUN dotnet restore eShopOnContainers-ServicesAndWebApps.sln | |||||
COPY . . | |||||
WORKDIR /src/src/Services/TenantCustomisations/TenantACustomisations | |||||
RUN dotnet publish --no-restore -c Release -o /app | |||||
FROM build AS publish | |||||
FROM base AS final | |||||
WORKDIR /app | |||||
COPY --from=publish /app . | |||||
ENTRYPOINT ["dotnet", "TenantACustomisations.dll"] |
@ -0,0 +1,12 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace TenantACustomisations.ExternalServices | |||||
{ | |||||
public interface IRFIDService | |||||
{ | |||||
bool IsOrderRFIDTagged(int orderNumber); | |||||
} | |||||
} |
@ -0,0 +1,13 @@ | |||||
using Ordering.API.Application.Models; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace TenantACustomisations.ExternalServices | |||||
{ | |||||
public interface IShippingService | |||||
{ | |||||
ShippingInformation CalculateShippingInformation(int orderId); | |||||
} | |||||
} |
@ -0,0 +1,23 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
using Ordering.API.Application.Models; | |||||
namespace TenantACustomisations.ExternalServices | |||||
{ | |||||
public class MockedShippingService : IShippingService | |||||
{ | |||||
public ShippingInformation CalculateShippingInformation(int orderId) | |||||
{ | |||||
ShippingInformation shippingInformation = new ShippingInformation(); | |||||
shippingInformation.ShippingTime = DateTime.Today; | |||||
shippingInformation.ArrivalTime = DateTime.Today.AddDays(2); | |||||
shippingInformation.FragilityLevel = Fragility.Medium; | |||||
shippingInformation.PriorityLevel = Priority.High; | |||||
shippingInformation.OrderNumber = orderId.ToString(); | |||||
return shippingInformation; | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,12 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace TenantACustomisations.ExternalServices | |||||
{ | |||||
public enum Fragility | |||||
{ | |||||
Low, Medium, High | |||||
} | |||||
} |
@ -0,0 +1,12 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace TenantACustomisations.ExternalServices | |||||
{ | |||||
public enum Priority | |||||
{ | |||||
Low, Medium, High | |||||
} | |||||
} |
@ -0,0 +1,17 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace TenantACustomisations.ExternalServices | |||||
{ | |||||
public class ShippingInformation | |||||
{ | |||||
public int ShippingInformationId { get; set; } | |||||
public DateTime ArrivalTime { get; set; } | |||||
public DateTime ShippingTime { get; set; } | |||||
public Priority PriorityLevel {get;set;} | |||||
public Fragility FragilityLevel { get; set; } | |||||
public String OrderNumber { get; set; } | |||||
} | |||||
} |
@ -0,0 +1,33 @@ | |||||
using Autofac; | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; | |||||
using System.Reflection; | |||||
using TenantACustomisations.ExternalServices; | |||||
using TenantACustomisations.IntegrationEvents.Events; | |||||
namespace Microsoft.eShopOnContainers.Services.TenantACustomisations.Infrastructure.AutofacModules | |||||
{ | |||||
public class ApplicationModule | |||||
:Autofac.Module | |||||
{ | |||||
public string QueriesConnectionString { get; } | |||||
public ApplicationModule(string qconstr) | |||||
{ | |||||
QueriesConnectionString = qconstr; | |||||
} | |||||
protected override void Load(ContainerBuilder builder) | |||||
{ | |||||
builder.RegisterAssemblyTypes(typeof(UserCheckoutAcceptedIntegrationEvent).GetTypeInfo().Assembly) | |||||
.AsClosedTypesOf(typeof(IIntegrationEventHandler<>)); | |||||
builder.RegisterType<MockedShippingService>() | |||||
.As<IShippingService>() | |||||
.InstancePerLifetimeScope(); | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,12 @@ | |||||
using Autofac; | |||||
namespace Microsoft.eShopOnContainers.Services.TenantACustomisations.Infrastructure.AutofacModules | |||||
{ | |||||
public class MediatorModule : Autofac.Module | |||||
{ | |||||
protected override void Load(ContainerBuilder builder) | |||||
{ | |||||
//TODO | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,33 @@ | |||||
using Microsoft.AspNetCore.Authorization; | |||||
using Swashbuckle.AspNetCore.Swagger; | |||||
using Swashbuckle.AspNetCore.SwaggerGen; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace TenantACustomisations.Infrastructure.Filters | |||||
{ | |||||
public class AuthorizeCheckOperationFilter : IOperationFilter | |||||
{ | |||||
public void Apply(Operation operation, OperationFilterContext context) | |||||
{ | |||||
// Check for authorize attribute | |||||
var hasAuthorize = context.MethodInfo.DeclaringType.GetCustomAttributes(true).OfType<AuthorizeAttribute>().Any() || | |||||
context.MethodInfo.GetCustomAttributes(true).OfType<AuthorizeAttribute>().Any(); | |||||
if (!hasAuthorize) return; | |||||
operation.Responses.TryAdd("401", new Response { Description = "Unauthorized" }); | |||||
operation.Responses.TryAdd("403", new Response { Description = "Forbidden" }); | |||||
operation.Security = new List<IDictionary<string, IEnumerable<string>>> | |||||
{ | |||||
new Dictionary<string, IEnumerable<string>> | |||||
{ | |||||
{ "oauth2", new [] { "orderingapi" } } | |||||
} | |||||
}; | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,69 @@ | |||||
namespace Microsoft.eShopOnContainers.Services.TenantACustomisations.Infrastructure.Filters | |||||
{ | |||||
using AspNetCore.Mvc; | |||||
using Microsoft.AspNetCore.Hosting; | |||||
using Microsoft.AspNetCore.Http; | |||||
using Microsoft.AspNetCore.Mvc.Filters; | |||||
using Microsoft.Extensions.Logging; | |||||
using System.Net; | |||||
public class HttpGlobalExceptionFilter : IExceptionFilter | |||||
{ | |||||
private readonly IHostingEnvironment env; | |||||
private readonly ILogger<HttpGlobalExceptionFilter> logger; | |||||
public HttpGlobalExceptionFilter(IHostingEnvironment env, ILogger<HttpGlobalExceptionFilter> logger) | |||||
{ | |||||
this.env = env; | |||||
this.logger = logger; | |||||
} | |||||
public void OnException(ExceptionContext context) | |||||
{ | |||||
logger.LogError(new EventId(context.Exception.HResult), | |||||
context.Exception, | |||||
context.Exception.Message); | |||||
if (1==2)//TODO | |||||
{ | |||||
var problemDetails = new ValidationProblemDetails() | |||||
{ | |||||
Instance = context.HttpContext.Request.Path, | |||||
Status = StatusCodes.Status400BadRequest, | |||||
Detail = "Please refer to the errors property for additional details." | |||||
}; | |||||
problemDetails.Errors.Add("DomainValidations", new string[] { context.Exception.Message.ToString() }); | |||||
context.Result = new BadRequestObjectResult(problemDetails); | |||||
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest; | |||||
} | |||||
else | |||||
{ | |||||
var json = new JsonErrorResponse | |||||
{ | |||||
Messages = new[] { "An error occur.Try it again." } | |||||
}; | |||||
if (env.IsDevelopment()) | |||||
{ | |||||
json.DeveloperMessage = context.Exception; | |||||
} | |||||
// Result asigned to a result object but in destiny the response is empty. This is a known bug of .net core 1.1 | |||||
// It will be fixed in .net core 1.1.2. See https://github.com/aspnet/Mvc/issues/5594 for more information | |||||
//TODO | |||||
//context.Result = new InternalServerErrorObjectResult(json); | |||||
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError; | |||||
} | |||||
context.ExceptionHandled = true; | |||||
} | |||||
private class JsonErrorResponse | |||||
{ | |||||
public string[] Messages { get; set; } | |||||
public object DeveloperMessage { get; set; } | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,49 @@ | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; | |||||
using Microsoft.Extensions.Logging; | |||||
using Newtonsoft.Json; | |||||
using Ordering.API.Application.IntegrationEvents.Events; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
using TenantACustomisations.Services; | |||||
namespace TenantACustomisations.IntegrationEvents.EventHandling | |||||
{ | |||||
public class CustomisationEventHandler : IIntegrationEventHandler<CustomisationEvent> | |||||
{ | |||||
private readonly ILogger<CustomisationEventHandler> _logger; | |||||
private readonly IEventBus _eventBus; | |||||
private readonly IValidationService validationService; | |||||
public CustomisationEventHandler(ILogger<CustomisationEventHandler> logger, IEventBus eventBus) | |||||
{ | |||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger)); | |||||
_eventBus = eventBus ?? throw new ArgumentNullException(nameof(eventBus)); | |||||
validationService = new ValidationService(); | |||||
} | |||||
public async Task Handle(CustomisationEvent @event) | |||||
{ | |||||
_logger.LogInformation("----- Handling integration event: {IntegrationEventId} - ({@IntegrationEvent})", @event.Id, @event); | |||||
IntegrationEvent integrationEvent = @event.@event; | |||||
switch (integrationEvent.GetType().Name) | |||||
{ | |||||
case "UserCheckoutAcceptedIntegrationEvent": | |||||
if (validationService.Validate((UserCheckoutAcceptedIntegrationEvent)integrationEvent)) | |||||
{ | |||||
integrationEvent.CheckForCustomisation = false; | |||||
_eventBus.Publish(integrationEvent); | |||||
} | |||||
break; | |||||
default: | |||||
integrationEvent.CheckForCustomisation = false; | |||||
_eventBus.Publish(integrationEvent); | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,45 @@ | |||||
using Microsoft.AspNetCore.SignalR; | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; | |||||
using Microsoft.Extensions.Logging; | |||||
using Serilog.Context; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Diagnostics; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
using TenantACustomisations.Database; | |||||
using TenantACustomisations.ExternalServices; | |||||
using TenantACustomisations.IntegrationEvents.Events; | |||||
namespace TenantACustomisations.IntegrationEvents.EventHandling | |||||
{ | |||||
public class OrderStatusChangedToSubmittedIntegrationEventHandler : | |||||
IIntegrationEventHandler<OrderStatusChangedToSubmittedIntegrationEvent> | |||||
{ | |||||
private readonly ILogger<OrderStatusChangedToSubmittedIntegrationEventHandler> _logger; | |||||
private readonly IShippingService _shippingService; | |||||
private readonly TenantAContext _context; | |||||
public OrderStatusChangedToSubmittedIntegrationEventHandler(ILogger<OrderStatusChangedToSubmittedIntegrationEventHandler> logger, IShippingService shippingService, TenantAContext context) | |||||
{ | |||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger)); | |||||
_shippingService = shippingService ?? throw new ArgumentNullException(nameof(shippingService)); | |||||
_context = context ?? throw new ArgumentNullException(nameof(shippingService)); | |||||
} | |||||
public async Task Handle(OrderStatusChangedToSubmittedIntegrationEvent @event) | |||||
{ | |||||
using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}- TenantA")) | |||||
{ | |||||
_logger.LogInformation("----- Handling integration event: {IntegrationEventId} at TenantA - ({@IntegrationEvent})", @event.Id, @event); | |||||
_logger.LogInformation("Hello"); | |||||
//TODO | |||||
Debug.WriteLine(@event); | |||||
ShippingInformation shippingInformation = _shippingService.CalculateShippingInformation(@event.OrderId); | |||||
_context.ShippingInformation.Add(shippingInformation); | |||||
_logger.LogInformation("----- Saving shipping information: {IntegrationEventId} at TenantA - ({@IntegrationEvent}) - {@ShippingInformation}", @event.Id, @event, shippingInformation); | |||||
_context.SaveChanges(); | |||||
} | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,60 @@ | |||||
using MediatR; | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions; | |||||
using Microsoft.Extensions.Logging; | |||||
using Ordering.API.Application.Behaviors; | |||||
using Serilog.Context; | |||||
using System.Threading.Tasks; | |||||
using System; | |||||
using System.Diagnostics; | |||||
using TenantACustomisations.IntegrationEvents.Events; | |||||
using TenantACustomisations.ExternalServices; | |||||
using TenantACustomisations.Database; | |||||
namespace TenantACustomisations.IntegrationEvents.EventHandling | |||||
{ | |||||
public class TenantAUserCheckoutAcceptedIntegrationEventHandler : | |||||
IIntegrationEventHandler<UserCheckoutAcceptedIntegrationEvent> | |||||
{ | |||||
private readonly IMediator _mediator; | |||||
private readonly IEventBus _eventBus; | |||||
private readonly ILogger<TenantAUserCheckoutAcceptedIntegrationEventHandler> _logger; | |||||
//private readonly TenantAContext _context; | |||||
//private readonly IShippingService _shippingService; | |||||
public TenantAUserCheckoutAcceptedIntegrationEventHandler( | |||||
IMediator mediator, | |||||
ILogger<TenantAUserCheckoutAcceptedIntegrationEventHandler> logger, | |||||
IEventBus eventBus | |||||
) | |||||
{ | |||||
_mediator = mediator ?? throw new ArgumentNullException(nameof(mediator)); | |||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger)); | |||||
_eventBus = eventBus ?? throw new ArgumentNullException(nameof(eventBus)); | |||||
} | |||||
/// <summary> | |||||
/// Integration event handler which starts the create order process | |||||
/// </summary> | |||||
/// <param name="@event"> | |||||
/// Integration event message which is sent by the | |||||
/// basket.api once it has successfully process the | |||||
/// order items. | |||||
/// </param> | |||||
/// <returns></returns> | |||||
public async Task Handle(UserCheckoutAcceptedIntegrationEvent @event) | |||||
{ | |||||
using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}- TenantA")) | |||||
{ | |||||
_logger.LogInformation("----- Handling integration event: {IntegrationEventId} at TenantA- ({@IntegrationEvent})", @event.Id, @event); | |||||
_logger.LogInformation("Hello"); | |||||
//TODO | |||||
Debug.WriteLine(@event); | |||||
//Save shipping info | |||||
//Hard code view comp | |||||
//Retrieve shipping info and show | |||||
} | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,30 @@ | |||||
using System.Collections.Generic; | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; | |||||
namespace TenantACustomisations.IntegrationEvents.Events | |||||
{ | |||||
public class OrderStatusChangedToAwaitingValidationIntegrationEvent : IntegrationEvent | |||||
{ | |||||
public int OrderId { get; } | |||||
public IEnumerable<OrderStockItem> OrderStockItems { get; } | |||||
public OrderStatusChangedToAwaitingValidationIntegrationEvent(int orderId, | |||||
IEnumerable<OrderStockItem> orderStockItems) | |||||
{ | |||||
OrderId = orderId; | |||||
OrderStockItems = orderStockItems; | |||||
} | |||||
} | |||||
public class OrderStockItem | |||||
{ | |||||
public int ProductId { get; } | |||||
public int Units { get; } | |||||
public OrderStockItem(int productId, int units) | |||||
{ | |||||
ProductId = productId; | |||||
Units = units; | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,22 @@ | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace TenantACustomisations.IntegrationEvents.Events | |||||
{ | |||||
public class OrderStatusChangedToSubmittedIntegrationEvent : IntegrationEvent | |||||
{ | |||||
public int OrderId { get; set; } | |||||
public string OrderStatus { get; set; } | |||||
public string BuyerName { get; set; } | |||||
public OrderStatusChangedToSubmittedIntegrationEvent(int orderId, string orderStatus, string buyerName) | |||||
{ | |||||
OrderId = orderId; | |||||
OrderStatus = orderStatus; | |||||
BuyerName = buyerName; | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,62 @@ | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; | |||||
using Ordering.API.Application.Models; | |||||
using System; | |||||
namespace TenantACustomisations.IntegrationEvents.Events | |||||
{ | |||||
public class UserCheckoutAcceptedIntegrationEvent : IntegrationEvent | |||||
{ | |||||
public string UserId { get; } | |||||
public string UserName { get; } | |||||
public string City { get; set; } | |||||
public string Street { get; set; } | |||||
public string State { get; set; } | |||||
public string Country { get; set; } | |||||
public string ZipCode { get; set; } | |||||
public string CardNumber { get; set; } | |||||
public string CardHolderName { get; set; } | |||||
public DateTime CardExpiration { get; set; } | |||||
public string CardSecurityNumber { get; set; } | |||||
public int CardTypeId { get; set; } | |||||
public string Buyer { get; set; } | |||||
public Guid RequestId { get; set; } | |||||
public CustomerBasket Basket { get; } | |||||
public UserCheckoutAcceptedIntegrationEvent(string userId, string userName, string city, string street, | |||||
string state, string country, string zipCode, string cardNumber, string cardHolderName, | |||||
DateTime cardExpiration, string cardSecurityNumber, int cardTypeId, string buyer, Guid requestId, | |||||
CustomerBasket basket) | |||||
{ | |||||
UserId = userId; | |||||
City = city; | |||||
Street = street; | |||||
State = state; | |||||
Country = country; | |||||
ZipCode = zipCode; | |||||
CardNumber = cardNumber; | |||||
CardHolderName = cardHolderName; | |||||
CardExpiration = cardExpiration; | |||||
CardSecurityNumber = cardSecurityNumber; | |||||
CardTypeId = cardTypeId; | |||||
Buyer = buyer; | |||||
Basket = basket; | |||||
RequestId = requestId; | |||||
UserName = userName; | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,89 @@ | |||||
using Microsoft.AspNetCore; | |||||
using Microsoft.AspNetCore.Hosting; | |||||
using Microsoft.eShopOnContainers.Services.TenantACustomisations; | |||||
using Microsoft.Extensions.Configuration; | |||||
using Serilog; | |||||
using System; | |||||
using System.IO; | |||||
using TenantACustomisations.Database; | |||||
namespace Microsoft.eShopOnContainers.Services.TenantACustomisations | |||||
{ | |||||
public class Program | |||||
{ | |||||
public static readonly string Namespace = typeof(Program).Namespace; | |||||
public static readonly string AppName = Namespace.Substring(Namespace.LastIndexOf('.', Namespace.LastIndexOf('.') - 1) + 1); | |||||
public static int Main(string[] args) | |||||
{ | |||||
var configuration = GetConfiguration(); | |||||
Log.Logger = CreateSerilogLogger(configuration); | |||||
try | |||||
{ | |||||
Log.Information("Configuring web host ({ApplicationContext})...", AppName); | |||||
var host = BuildWebHost(configuration, args); | |||||
Log.Information("Starting web host ({ApplicationContext})...", AppName); | |||||
host.Run(); | |||||
return 0; | |||||
} | |||||
catch (Exception ex) | |||||
{ | |||||
Log.Fatal(ex, "Program terminated unexpectedly ({ApplicationContext})!", AppName); | |||||
return 1; | |||||
} | |||||
finally | |||||
{ | |||||
Log.CloseAndFlush(); | |||||
} | |||||
} | |||||
private static IWebHost BuildWebHost(IConfiguration configuration, string[] args) => | |||||
WebHost.CreateDefaultBuilder(args) | |||||
.CaptureStartupErrors(false) | |||||
.UseStartup<Startup>() | |||||
.UseApplicationInsights() | |||||
.UseContentRoot(Directory.GetCurrentDirectory()) | |||||
.UseConfiguration(configuration) | |||||
.UseSerilog() | |||||
.Build(); | |||||
private static Serilog.ILogger CreateSerilogLogger(IConfiguration configuration) | |||||
{ | |||||
var seqServerUrl = configuration["Serilog:SeqServerUrl"]; | |||||
var logstashUrl = configuration["Serilog:LogstashgUrl"]; | |||||
return new LoggerConfiguration() | |||||
.MinimumLevel.Verbose() | |||||
.Enrich.WithProperty("ApplicationContext", AppName) | |||||
.Enrich.FromLogContext() | |||||
.WriteTo.Console() | |||||
.WriteTo.Seq(string.IsNullOrWhiteSpace(seqServerUrl) ? "http://seq" : seqServerUrl) | |||||
.WriteTo.Http(string.IsNullOrWhiteSpace(logstashUrl) ? "http://logstash:8080" : logstashUrl) | |||||
.ReadFrom.Configuration(configuration) | |||||
.CreateLogger(); | |||||
} | |||||
private static IConfiguration GetConfiguration() | |||||
{ | |||||
var builder = new ConfigurationBuilder() | |||||
.SetBasePath(Directory.GetCurrentDirectory()) | |||||
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) | |||||
.AddEnvironmentVariables(); | |||||
var config = builder.Build(); | |||||
if (config.GetValue<bool>("UseVault", false)) | |||||
{ | |||||
builder.AddAzureKeyVault( | |||||
$"https://{config["Vault:Name"]}.vault.azure.net/", | |||||
config["Vault:ClientId"], | |||||
config["Vault:ClientSecret"]); | |||||
} | |||||
return builder.Build(); | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,30 @@ | |||||
{ | |||||
"$schema": "http://json.schemastore.org/launchsettings.json", | |||||
"iisSettings": { | |||||
"windowsAuthentication": false, | |||||
"anonymousAuthentication": true, | |||||
"iisExpress": { | |||||
"applicationUrl": "http://localhost:57040", | |||||
"sslPort": 44328 | |||||
} | |||||
}, | |||||
"profiles": { | |||||
"IIS Express": { | |||||
"commandName": "IISExpress", | |||||
"launchBrowser": true, | |||||
"launchUrl": "api/values", | |||||
"environmentVariables": { | |||||
"ASPNETCORE_ENVIRONMENT": "Development" | |||||
} | |||||
}, | |||||
"TenantACustomisations": { | |||||
"commandName": "Project", | |||||
"launchBrowser": true, | |||||
"launchUrl": "api/values", | |||||
"applicationUrl": "https://localhost:5001;http://localhost:5000", | |||||
"environmentVariables": { | |||||
"ASPNETCORE_ENVIRONMENT": "Development" | |||||
} | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,14 @@ | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; | |||||
using Ordering.API.Application.IntegrationEvents.Events; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace TenantACustomisations.Services | |||||
{ | |||||
interface IValidationService | |||||
{ | |||||
Boolean Validate(UserCheckoutAcceptedIntegrationEvent @event); | |||||
} | |||||
} |
@ -0,0 +1,16 @@ | |||||
using Ordering.API.Application.IntegrationEvents.Events; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace TenantACustomisations.Services | |||||
{ | |||||
public class ValidationService : IValidationService | |||||
{ | |||||
public bool Validate(UserCheckoutAcceptedIntegrationEvent @event) | |||||
{ | |||||
return @event.State == "Oslo"; | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,416 @@ | |||||
namespace Microsoft.eShopOnContainers.Services.TenantACustomisations | |||||
{ | |||||
using AspNetCore.Http; | |||||
using Autofac; | |||||
using Autofac.Extensions.DependencyInjection; | |||||
using Microsoft.ApplicationInsights.Extensibility; | |||||
using Microsoft.ApplicationInsights.ServiceFabric; | |||||
using Microsoft.AspNetCore.Authentication.JwtBearer; | |||||
using Microsoft.AspNetCore.Builder; | |||||
using Microsoft.AspNetCore.Hosting; | |||||
using Microsoft.AspNetCore.Mvc; | |||||
using Microsoft.Azure.ServiceBus; | |||||
using Microsoft.EntityFrameworkCore; | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus; | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ; | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBusServiceBus; | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF; | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services; | |||||
using Microsoft.Extensions.Configuration; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using Microsoft.Extensions.Logging; | |||||
using RabbitMQ.Client; | |||||
using Swashbuckle.AspNetCore.Swagger; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Data.Common; | |||||
using System.IdentityModel.Tokens.Jwt; | |||||
using System.Reflection; | |||||
using HealthChecks.UI.Client; | |||||
using Microsoft.AspNetCore.Diagnostics.HealthChecks; | |||||
using Microsoft.Extensions.Diagnostics.HealthChecks; | |||||
using Infrastructure.AutofacModules; | |||||
using Microsoft.eShopOnContainers.Services.TenantACustomisations.Infrastructure.Filters; | |||||
using global::TenantACustomisations.Infrastructure.Filters; | |||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; | |||||
using global::TenantACustomisations.IntegrationEvents.EventHandling; | |||||
using global::TenantACustomisations.IntegrationEvents.Events; | |||||
using global::TenantACustomisations.ExternalServices; | |||||
using global::TenantACustomisations.Database; | |||||
public class Startup | |||||
{ | |||||
public Startup(IConfiguration configuration) | |||||
{ | |||||
Configuration = configuration; | |||||
} | |||||
public IConfiguration Configuration { get; } | |||||
public IServiceProvider ConfigureServices(IServiceCollection services) | |||||
{ | |||||
services.AddApplicationInsights(Configuration) | |||||
.AddCustomMvc() | |||||
.AddHealthChecks(Configuration) | |||||
.AddCustomDbContext(Configuration) | |||||
.AddCustomSwagger(Configuration) | |||||
.AddCustomIntegrations(Configuration) | |||||
.AddCustomConfiguration(Configuration) | |||||
.AddEventBus(Configuration) | |||||
.AddCustomAuthentication(Configuration); | |||||
//configure autofac | |||||
var container = new ContainerBuilder(); | |||||
container.Populate(services); | |||||
container.RegisterModule(new MediatorModule()); | |||||
container.RegisterModule(new ApplicationModule(Configuration["ConnectionString"])); | |||||
return new AutofacServiceProvider(container.Build()); | |||||
} | |||||
public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) | |||||
{ | |||||
//loggerFactory.AddAzureWebAppDiagnostics(); | |||||
//loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace); | |||||
var pathBase = Configuration["PATH_BASE"]; | |||||
if (!string.IsNullOrEmpty(pathBase)) | |||||
{ | |||||
loggerFactory.CreateLogger<Startup>().LogDebug("Using PATH BASE '{pathBase}'", pathBase); | |||||
app.UsePathBase(pathBase); | |||||
} | |||||
app.UseCors("CorsPolicy"); | |||||
app.UseHealthChecks("/liveness", new HealthCheckOptions | |||||
{ | |||||
Predicate = r => r.Name.Contains("self") | |||||
}); | |||||
app.UseHealthChecks("/hc", new HealthCheckOptions() | |||||
{ | |||||
Predicate = _ => true, | |||||
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse | |||||
}); | |||||
ConfigureAuth(app); | |||||
app.UseMvcWithDefaultRoute(); | |||||
app.UseSwagger() | |||||
.UseSwaggerUI(c => | |||||
{ | |||||
c.SwaggerEndpoint($"{ (!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty) }/swagger/v1/swagger.json", "Ordering.API V1"); | |||||
c.OAuthClientId("orderingswaggerui"); | |||||
c.OAuthAppName("Ordering Swagger UI"); | |||||
}); | |||||
ConfigureEventBus(app); | |||||
using (var serviceScope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope()) | |||||
{ | |||||
var context = serviceScope.ServiceProvider.GetRequiredService<TenantAContext>(); | |||||
context.Database.EnsureCreated(); | |||||
} | |||||
} | |||||
private void ConfigureEventBus(IApplicationBuilder app) | |||||
{ | |||||
var eventBus = app.ApplicationServices.GetRequiredService<BuildingBlocks.EventBus.Abstractions.IEventBus>(); | |||||
//eventBus.Subscribe<UserCheckoutAcceptedIntegrationEvent, IIntegrationEventHandler<UserCheckoutAcceptedIntegrationEvent>>(); | |||||
eventBus.Subscribe<OrderStatusChangedToSubmittedIntegrationEvent, OrderStatusChangedToSubmittedIntegrationEventHandler>(); | |||||
} | |||||
protected virtual void ConfigureAuth(IApplicationBuilder app) | |||||
{ | |||||
if (Configuration.GetValue<bool>("UseLoadTest")) | |||||
{ | |||||
//app.UseMiddleware<ByPassAuthMiddleware>(); | |||||
//TODO | |||||
} | |||||
app.UseAuthentication(); | |||||
} | |||||
} | |||||
static class CustomExtensionsMethods | |||||
{ | |||||
public static IServiceCollection AddApplicationInsights(this IServiceCollection services, IConfiguration configuration) | |||||
{ | |||||
services.AddApplicationInsightsTelemetry(configuration); | |||||
var orchestratorType = configuration.GetValue<string>("OrchestratorType"); | |||||
if (orchestratorType?.ToUpper() == "K8S") | |||||
{ | |||||
// Enable K8s telemetry initializer | |||||
services.AddApplicationInsightsKubernetesEnricher(); | |||||
} | |||||
if (orchestratorType?.ToUpper() == "SF") | |||||
{ | |||||
// Enable SF telemetry initializer | |||||
services.AddSingleton<ITelemetryInitializer>((serviceProvider) => | |||||
new FabricTelemetryInitializer()); | |||||
} | |||||
return services; | |||||
} | |||||
public static IServiceCollection AddCustomMvc(this IServiceCollection services) | |||||
{ | |||||
// Add framework services. | |||||
services.AddMvc(options => | |||||
{ | |||||
options.Filters.Add(typeof(HttpGlobalExceptionFilter)); | |||||
}) | |||||
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2) | |||||
.AddControllersAsServices(); //Injecting Controllers themselves thru DI | |||||
//For further info see: http://docs.autofac.org/en/latest/integration/aspnetcore.html#controllers-as-services | |||||
services.AddCors(options => | |||||
{ | |||||
options.AddPolicy("CorsPolicy", | |||||
builder => builder | |||||
.SetIsOriginAllowed((host) => true) | |||||
.AllowAnyMethod() | |||||
.AllowAnyHeader() | |||||
.AllowCredentials()); | |||||
}); | |||||
return services; | |||||
} | |||||
public static IServiceCollection AddHealthChecks(this IServiceCollection services, IConfiguration configuration) | |||||
{ | |||||
var hcBuilder = services.AddHealthChecks(); | |||||
hcBuilder.AddCheck("self", () => HealthCheckResult.Healthy()); | |||||
hcBuilder | |||||
.AddSqlServer( | |||||
configuration["ConnectionString"], | |||||
name: "OrderingDB-check", | |||||
tags: new string[] { "orderingdb" }); | |||||
if (configuration.GetValue<bool>("AzureServiceBusEnabled")) | |||||
{ | |||||
hcBuilder | |||||
.AddAzureServiceBusTopic( | |||||
configuration["EventBusConnection"], | |||||
topicName: "eshop_event_bus", | |||||
name: "ordering-servicebus-check", | |||||
tags: new string[] { "servicebus" }); | |||||
} | |||||
else | |||||
{ | |||||
hcBuilder | |||||
.AddRabbitMQ( | |||||
$"amqp://{configuration["EventBusConnection"]}", | |||||
name: "ordering-rabbitmqbus-check", | |||||
tags: new string[] { "rabbitmqbus" }); | |||||
} | |||||
return services; | |||||
} | |||||
public static IServiceCollection AddCustomDbContext(this IServiceCollection services, IConfiguration configuration) | |||||
{ | |||||
services.AddDbContext<TenantAContext>(options => | |||||
options.UseSqlServer(configuration["ConnectionString"])); | |||||
services.AddDbContext<IntegrationEventLogContext>(options => | |||||
{ | |||||
options.UseSqlServer(configuration["ConnectionString"], | |||||
sqlServerOptionsAction: sqlOptions => | |||||
{ | |||||
sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name); | |||||
//Configuring Connection Resiliency: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency | |||||
sqlOptions.EnableRetryOnFailure(maxRetryCount: 10, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null); | |||||
}); | |||||
}); | |||||
return services; | |||||
} | |||||
public static IServiceCollection AddCustomSwagger(this IServiceCollection services, IConfiguration configuration) | |||||
{ | |||||
services.AddSwaggerGen(options => | |||||
{ | |||||
options.DescribeAllEnumsAsStrings(); | |||||
options.SwaggerDoc("v1", new Swashbuckle.AspNetCore.Swagger.Info | |||||
{ | |||||
Title = "Ordering HTTP API", | |||||
Version = "v1", | |||||
Description = "The Ordering Service HTTP API", | |||||
TermsOfService = "Terms Of Service" | |||||
}); | |||||
options.AddSecurityDefinition("oauth2", new OAuth2Scheme | |||||
{ | |||||
Type = "oauth2", | |||||
Flow = "implicit", | |||||
AuthorizationUrl = $"{configuration.GetValue<string>("IdentityUrlExternal")}/connect/authorize", | |||||
TokenUrl = $"{configuration.GetValue<string>("IdentityUrlExternal")}/connect/token", | |||||
Scopes = new Dictionary<string, string>() | |||||
{ | |||||
{ "orders", "Ordering API" } | |||||
} | |||||
}); | |||||
options.OperationFilter<AuthorizeCheckOperationFilter>(); | |||||
}); | |||||
return services; | |||||
} | |||||
public static IServiceCollection AddCustomIntegrations(this IServiceCollection services, IConfiguration configuration) | |||||
{ | |||||
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); | |||||
//services.AddTransient<IIdentityService, IdentityService>(); | |||||
services.AddTransient<Func<DbConnection, IIntegrationEventLogService>>( | |||||
sp => (DbConnection c) => new IntegrationEventLogService(c)); | |||||
//services.AddTransient<IOrderingIntegrationEventService, OrderingIntegrationEventService>(); | |||||
if (configuration.GetValue<bool>("AzureServiceBusEnabled")) | |||||
{ | |||||
services.AddSingleton<IServiceBusPersisterConnection>(sp => | |||||
{ | |||||
var logger = sp.GetRequiredService<ILogger<DefaultServiceBusPersisterConnection>>(); | |||||
var serviceBusConnectionString = configuration["EventBusConnection"]; | |||||
var serviceBusConnection = new ServiceBusConnectionStringBuilder(serviceBusConnectionString); | |||||
return new DefaultServiceBusPersisterConnection(serviceBusConnection, logger); | |||||
}); | |||||
} | |||||
else | |||||
{ | |||||
services.AddSingleton<IRabbitMQPersistentConnection>(sp => | |||||
{ | |||||
var logger = sp.GetRequiredService<ILogger<DefaultRabbitMQPersistentConnection>>(); | |||||
var factory = new ConnectionFactory() | |||||
{ | |||||
HostName = configuration["EventBusConnection"], | |||||
DispatchConsumersAsync = true | |||||
}; | |||||
if (!string.IsNullOrEmpty(configuration["EventBusUserName"])) | |||||
{ | |||||
factory.UserName = configuration["EventBusUserName"]; | |||||
} | |||||
if (!string.IsNullOrEmpty(configuration["EventBusPassword"])) | |||||
{ | |||||
factory.Password = configuration["EventBusPassword"]; | |||||
} | |||||
var retryCount = 5; | |||||
if (!string.IsNullOrEmpty(configuration["EventBusRetryCount"])) | |||||
{ | |||||
retryCount = int.Parse(configuration["EventBusRetryCount"]); | |||||
} | |||||
return new DefaultRabbitMQPersistentConnection(factory, logger, retryCount); | |||||
}); | |||||
} | |||||
return services; | |||||
} | |||||
public static IServiceCollection AddCustomConfiguration(this IServiceCollection services, IConfiguration configuration) | |||||
{ | |||||
services.AddOptions(); | |||||
//services.Configure<OrderingSettings>(configuration); | |||||
services.Configure<ApiBehaviorOptions>(options => | |||||
{ | |||||
options.InvalidModelStateResponseFactory = context => | |||||
{ | |||||
var problemDetails = new ValidationProblemDetails(context.ModelState) | |||||
{ | |||||
Instance = context.HttpContext.Request.Path, | |||||
Status = StatusCodes.Status400BadRequest, | |||||
Detail = "Please refer to the errors property for additional details." | |||||
}; | |||||
return new BadRequestObjectResult(problemDetails) | |||||
{ | |||||
ContentTypes = { "application/problem+json", "application/problem+xml" } | |||||
}; | |||||
}; | |||||
}); | |||||
return services; | |||||
} | |||||
public static IServiceCollection AddEventBus(this IServiceCollection services, IConfiguration configuration) | |||||
{ | |||||
var subscriptionClientName = configuration["SubscriptionClientName"]; | |||||
if (configuration.GetValue<bool>("AzureServiceBusEnabled")) | |||||
{ | |||||
services.AddSingleton<IEventBus, EventBusServiceBus>(sp => | |||||
{ | |||||
var serviceBusPersisterConnection = sp.GetRequiredService<IServiceBusPersisterConnection>(); | |||||
var iLifetimeScope = sp.GetRequiredService<ILifetimeScope>(); | |||||
var logger = sp.GetRequiredService<ILogger<EventBusServiceBus>>(); | |||||
var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>(); | |||||
return new EventBusServiceBus(serviceBusPersisterConnection, logger, | |||||
eventBusSubcriptionsManager, subscriptionClientName, iLifetimeScope); | |||||
}); | |||||
} | |||||
else | |||||
{ | |||||
services.AddSingleton<IEventBus, EventBusRabbitMQ>(sp => | |||||
{ | |||||
var rabbitMQPersistentConnection = sp.GetRequiredService<IRabbitMQPersistentConnection>(); | |||||
var iLifetimeScope = sp.GetRequiredService<ILifetimeScope>(); | |||||
var logger = sp.GetRequiredService<ILogger<EventBusRabbitMQ>>(); | |||||
var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>(); | |||||
var retryCount = 5; | |||||
if (!string.IsNullOrEmpty(configuration["EventBusRetryCount"])) | |||||
{ | |||||
retryCount = int.Parse(configuration["EventBusRetryCount"]); | |||||
} | |||||
return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, subscriptionClientName, retryCount); | |||||
}); | |||||
} | |||||
services.AddSingleton<IEventBusSubscriptionsManager, InMemoryEventBusSubscriptionsManager>(); | |||||
//services.AddTransient<TenantAUserCheckoutAcceptedIntegrationEventHandler>(); | |||||
return services; | |||||
} | |||||
public static IServiceCollection AddCustomAuthentication(this IServiceCollection services, IConfiguration configuration) | |||||
{ | |||||
// prevent from mapping "sub" claim to nameidentifier. | |||||
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("sub"); | |||||
var identityUrl = configuration.GetValue<string>("IdentityUrl"); | |||||
services.AddAuthentication(options => | |||||
{ | |||||
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; | |||||
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; | |||||
}).AddJwtBearer(options => | |||||
{ | |||||
options.Authority = identityUrl; | |||||
options.RequireHttpsMetadata = false; | |||||
options.Audience = "orders"; | |||||
}); | |||||
return services; | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,58 @@ | |||||
<Project Sdk="Microsoft.NET.Sdk.Web"> | |||||
<PropertyGroup> | |||||
<TargetFramework>netcoreapp2.2</TargetFramework> | |||||
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel> | |||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS> | |||||
<DockerfileContext>..\eShopOnContainersCustomised</DockerfileContext> | |||||
</PropertyGroup> | |||||
<ItemGroup> | |||||
<PackageReference Include="AspNetCore.HealthChecks.AzureServiceBus" Version="2.2.0" /> | |||||
<PackageReference Include="AspNetCore.HealthChecks.Rabbitmq" Version="2.2.0" /> | |||||
<PackageReference Include="AspNetCore.HealthChecks.SqlServer" Version="2.2.0" /> | |||||
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="2.2.4" /> | |||||
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.2.1" /> | |||||
<PackageReference Include="Dapper" Version="1.50.7" /> | |||||
<PackageReference Include="FluentValidation.AspNetCore" Version="7.5.0" /> | |||||
<PackageReference Include="MediatR" Version="5.1.0" /> | |||||
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="5.1.0" /> | |||||
<PackageReference Include="Microsoft.ApplicationInsights" Version="2.11.0" /> | |||||
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.2.1" /> | |||||
<PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.6.1" /> | |||||
<PackageReference Include="Microsoft.ApplicationInsights.Kubernetes" Version="1.0.2" /> | |||||
<PackageReference Include="Microsoft.ApplicationInsights.ServiceFabric" Version="2.2.2" /> | |||||
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.2.0" /> | |||||
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.HealthChecks" Version="2.2.0" /> | |||||
<PackageReference Include="Microsoft.AspNetCore.HealthChecks" Version="1.0.0" /> | |||||
<PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.2.0" PrivateAssets="All" /> | |||||
<PackageReference Include="Microsoft.Extensions.Configuration.AzureKeyVault" Version="2.2.0" /> | |||||
<PackageReference Include="Microsoft.Extensions.Logging.AzureAppServices" Version="2.2.0" /> | |||||
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.2.4" /> | |||||
<PackageReference Include="Polly" Version="6.0.1" /> | |||||
<PackageReference Include="Serilog" Version="2.9.0" /> | |||||
<PackageReference Include="Serilog.AspNetCore" Version="2.1.1" /> | |||||
<PackageReference Include="Serilog.Enrichers.Environment" Version="2.1.3" /> | |||||
<PackageReference Include="Serilog.Settings.Configuration" Version="3.0.1" /> | |||||
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" /> | |||||
<PackageReference Include="Serilog.Sinks.Http" Version="4.2.1" /> | |||||
<PackageReference Include="Serilog.Sinks.Seq" Version="4.0.0" /> | |||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="3.0.0" /> | |||||
<PackageReference Include="Swashbuckle.AspNetCore.Swagger" Version="4.0.1" /> | |||||
<PackageReference Include="System.Reflection" Version="4.3.0" /> | |||||
<PackageReference Include="System.ValueTuple" Version="4.5.0" /> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusRabbitMQ\EventBusRabbitMQ.csproj" /> | |||||
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusServiceBus\EventBusServiceBus.csproj" /> | |||||
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBus\EventBus.csproj" /> | |||||
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\IntegrationEventLogEF\IntegrationEventLogEF.csproj" /> | |||||
<ProjectReference Include="..\..\Ordering\Ordering.API\Ordering.API.csproj" /> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<PackageReference Update="Microsoft.NETCore.App" Version="2.2.0" /> | |||||
</ItemGroup> | |||||
</Project> |
@ -0,0 +1,9 @@ | |||||
{ | |||||
"Logging": { | |||||
"LogLevel": { | |||||
"Default": "Debug", | |||||
"System": "Information", | |||||
"Microsoft": "Information" | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,29 @@ | |||||
{ | |||||
"Serilog": { | |||||
"SeqServerUrl": null, | |||||
"LogstashgUrl": null, | |||||
"MinimumLevel": { | |||||
"Default": "Information", | |||||
"Override": { | |||||
"Microsoft": "Warning", | |||||
"Microsoft.eShopOnContainers": "Information", | |||||
"System": "Warning" | |||||
} | |||||
} | |||||
}, | |||||
"IdentityUrl": "http://localhost:5105", | |||||
//"ConnectionString": "127.0.0.1", | |||||
"ConnectionString": "Server=tcp:127.0.0.1,5433;Database=Microsoft.eShopOnContainers.Services.TenantADb;User Id=sa;Password=Pass@word;", | |||||
"AzureServiceBusEnabled": false, | |||||
"SubscriptionClientName": "TenantACustomisation", | |||||
"ApplicationInsights": { | |||||
"InstrumentationKey": "" | |||||
}, | |||||
"EventBusRetryCount": 5, | |||||
"UseVault": false, | |||||
"Vault": { | |||||
"Name": "eshop", | |||||
"ClientId": "your-clien-id", | |||||
"ClientSecret": "your-client-secret" | |||||
} | |||||
} |
@ -0,0 +1,12 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace Microsoft.eShopOnContainers.WebMVC.ViewModels.Customisation | |||||
{ | |||||
public enum Fragility | |||||
{ | |||||
Low, Medium, High | |||||
} | |||||
} |
@ -0,0 +1,12 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace Microsoft.eShopOnContainers.WebMVC.ViewModels.Customisation | |||||
{ | |||||
public enum Priority | |||||
{ | |||||
Low, Medium, High | |||||
} | |||||
} |
@ -0,0 +1,17 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace Microsoft.eShopOnContainers.WebMVC.ViewModels.Customisation | |||||
{ | |||||
public class ShippingInformation | |||||
{ | |||||
public int ShippingInformationId { get; set; } | |||||
public DateTime ArrivalTime { get; set; } | |||||
public DateTime ShippingTime { get; set; } | |||||
public Priority PriorityLevel { get; set; } | |||||
public Fragility FragilityLevel { get; set; } | |||||
public String OrderNumber { get; set; } | |||||
} | |||||
} |