Browse Source

Add persistence of published integration events for Catalog (the only microservice publishing integration events by the moment).

pull/126/head
dsanz 8 years ago
parent
commit
f502c2388f
13 changed files with 300 additions and 29 deletions
  1. +1
    -0
      src/Services/Catalog/Catalog.API/Catalog.API.csproj
  2. +22
    -8
      src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs
  3. +31
    -1
      src/Services/Catalog/Catalog.API/Infrastructure/CatalogContext.cs
  4. +123
    -0
      src/Services/Catalog/Catalog.API/Infrastructure/Migrations/20170314083211_AddEventTable.Designer.cs
  5. +34
    -0
      src/Services/Catalog/Catalog.API/Infrastructure/Migrations/20170314083211_AddEventTable.cs
  6. +34
    -10
      src/Services/Catalog/Catalog.API/Infrastructure/Migrations/CatalogContextModelSnapshot.cs
  7. +1
    -1
      src/Services/Common/Infrastructure/Catalog/CatalogPriceChanged.cs
  8. +5
    -2
      src/Services/Common/Infrastructure/Data/EventStateEnum.cs
  9. +26
    -0
      src/Services/Common/Infrastructure/Data/IntegrationEvent.cs
  10. +3
    -3
      src/Services/Common/Infrastructure/EventBus.cs
  11. +3
    -3
      src/Services/Common/Infrastructure/IEventBus.cs
  12. +1
    -1
      src/Services/Common/Infrastructure/IIntegrationEventHandler.cs
  13. +16
    -0
      src/Services/Common/Infrastructure/IntegrationEventBase.cs

+ 1
- 0
src/Services/Catalog/Catalog.API/Catalog.API.csproj View File

@ -45,6 +45,7 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="1.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="1.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="1.1.0" />
<PackageReference Include="Newtonsoft.Json" Version="9.0.1" />
<PackageReference Include="Swashbuckle" Version="6.0.0-beta902" />
</ItemGroup>


+ 22
- 8
src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs View File

@ -5,7 +5,9 @@ using Microsoft.eShopOnContainers.Services.Catalog.API.Model;
using Microsoft.eShopOnContainers.Services.Catalog.API.ViewModel;
using Microsoft.eShopOnContainers.Services.Common.Infrastructure;
using Microsoft.eShopOnContainers.Services.Common.Infrastructure.Catalog;
using Microsoft.eShopOnContainers.Services.Common.Infrastructure.Data;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
@ -104,12 +106,6 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers
var model = new PaginatedItemsViewModel<CatalogItem>(
pageIndex, pageSize, totalItems, itemsOnPage);
//hook to run integration tests until POST methods are created
if (catalogTypeId.HasValue && catalogTypeId == 1)
{
_eventBus.Publish(new CatalogPriceChanged(2, 10.4M, 8.4M));
}
return Ok(model);
}
@ -153,11 +149,12 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers
_context.CatalogItems.Update(item);
await _context.SaveChangesAsync();
_eventBus.Publish(new CatalogPriceChanged(item.Id, item.Price, oldPrice));
var @event = new CatalogPriceChanged(item.Id, item.Price, oldPrice);
await ProcessEventAsync(@event);
}
return Ok();
}
}
private List<CatalogItem> ComposePicUri(List<CatalogItem> items) {
var baseUri = _settings.Value.ExternalCatalogBaseUrl;
@ -168,5 +165,22 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers
return items;
}
private async Task ProcessEventAsync(IntegrationEventBase @event)
{
_eventBus.Publish(@event);
var eventData = new IntegrationEvent(@event);
eventData.TimesSent++;
eventData.State = EventStateEnum.Sent;
try
{
_context.IntegrationEvents.Add(eventData);
await _context.SaveChangesAsync();
}
catch (Exception ex)
{
var t = ex.Message;
}
}
}
}

+ 31
- 1
src/Services/Catalog/Catalog.API/Infrastructure/CatalogContext.cs View File

@ -3,6 +3,7 @@
using EntityFrameworkCore.Metadata.Builders;
using Microsoft.EntityFrameworkCore;
using Model;
using Microsoft.eShopOnContainers.Services.Common.Infrastructure.Data;
public class CatalogContext : DbContext
{
@ -12,12 +13,15 @@
public DbSet<CatalogItem> CatalogItems { get; set; }
public DbSet<CatalogBrand> CatalogBrands { get; set; }
public DbSet<CatalogType> CatalogTypes { get; set; }
public DbSet<IntegrationEvent> IntegrationEvents { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<CatalogBrand>(ConfigureCatalogBrand);
builder.Entity<CatalogType>(ConfigureCatalogType);
builder.Entity<CatalogItem>(ConfigureCatalogItem);
}
builder.Entity<IntegrationEvent>(ConfigureIntegrationEvent);
}
void ConfigureCatalogItem(EntityTypeBuilder<CatalogItem> builder)
{
@ -75,5 +79,31 @@
.IsRequired()
.HasMaxLength(100);
}
void ConfigureIntegrationEvent(EntityTypeBuilder<IntegrationEvent> builder)
{
builder.ToTable("IntegrationEvent");
builder.HasKey(e => e.EventId);
builder.Property(e => e.EventId)
.IsRequired();
builder.Property(e => e.Content)
.IsRequired();
builder.Property(e => e.CreationTime)
.IsRequired();
builder.Property(e => e.State)
.IsRequired();
builder.Property(e => e.TimesSent)
.IsRequired();
builder.Property(e => e.EventTypeName)
.IsRequired();
}
}
}

+ 123
- 0
src/Services/Catalog/Catalog.API/Infrastructure/Migrations/20170314083211_AddEventTable.Designer.cs View File

@ -0,0 +1,123 @@
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure;
using Microsoft.eShopOnContainers.Services.Common.Infrastructure;
namespace Catalog.API.Infrastructure.Migrations
{
[DbContext(typeof(CatalogContext))]
[Migration("20170314083211_AddEventTable")]
partial class AddEventTable
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
modelBuilder
.HasAnnotation("ProductVersion", "1.1.0-rtm-22752")
.HasAnnotation("SqlServer:Sequence:.catalog_brand_hilo", "'catalog_brand_hilo', '', '1', '10', '', '', 'Int64', 'False'")
.HasAnnotation("SqlServer:Sequence:.catalog_hilo", "'catalog_hilo', '', '1', '10', '', '', 'Int64', 'False'")
.HasAnnotation("SqlServer:Sequence:.catalog_type_hilo", "'catalog_type_hilo', '', '1', '10', '', '', 'Int64', 'False'")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogBrand", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:HiLoSequenceName", "catalog_brand_hilo")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.SequenceHiLo);
b.Property<string>("Brand")
.IsRequired()
.HasMaxLength(100);
b.HasKey("Id");
b.ToTable("CatalogBrand");
});
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogItem", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:HiLoSequenceName", "catalog_hilo")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.SequenceHiLo);
b.Property<int>("CatalogBrandId");
b.Property<int>("CatalogTypeId");
b.Property<string>("Description");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(50);
b.Property<string>("PictureUri");
b.Property<decimal>("Price");
b.HasKey("Id");
b.HasIndex("CatalogBrandId");
b.HasIndex("CatalogTypeId");
b.ToTable("Catalog");
});
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogType", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:HiLoSequenceName", "catalog_type_hilo")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.SequenceHiLo);
b.Property<string>("Type")
.IsRequired()
.HasMaxLength(100);
b.HasKey("Id");
b.ToTable("CatalogType");
});
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Common.Infrastructure.Data.IntegrationEvent", b =>
{
b.Property<Guid>("EventId")
.ValueGeneratedOnAdd();
b.Property<string>("Content")
.IsRequired();
b.Property<DateTime>("CreationTime");
b.Property<string>("EventTypeName")
.IsRequired()
.HasMaxLength(200);
b.Property<int>("State");
b.Property<int>("TimesSent");
b.HasKey("EventId");
b.ToTable("IntegrationEvent");
});
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogItem", b =>
{
b.HasOne("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogBrand", "CatalogBrand")
.WithMany()
.HasForeignKey("CatalogBrandId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogType", "CatalogType")
.WithMany()
.HasForeignKey("CatalogTypeId")
.OnDelete(DeleteBehavior.Cascade);
});
}
}
}

+ 34
- 0
src/Services/Catalog/Catalog.API/Infrastructure/Migrations/20170314083211_AddEventTable.cs View File

@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Migrations;
namespace Catalog.API.Infrastructure.Migrations
{
public partial class AddEventTable : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "IntegrationEvent",
columns: table => new
{
EventId = table.Column<Guid>(nullable: false),
Content = table.Column<string>(nullable: false),
CreationTime = table.Column<DateTime>(nullable: false),
EventTypeName = table.Column<string>(maxLength: 200, nullable: false),
State = table.Column<int>(nullable: false),
TimesSent = table.Column<int>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_IntegrationEvent", x => x.EventId);
});
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "IntegrationEvent");
}
}
}

+ 34
- 10
src/Services/Catalog/Catalog.API/Infrastructure/Migrations/CatalogContextModelSnapshot.cs View File

@ -4,6 +4,7 @@ using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure;
using Microsoft.eShopOnContainers.Services.Common.Infrastructure;
namespace Catalog.API.Infrastructure.Migrations
{
@ -13,13 +14,13 @@ namespace Catalog.API.Infrastructure.Migrations
protected override void BuildModel(ModelBuilder modelBuilder)
{
modelBuilder
.HasAnnotation("ProductVersion", "1.0.1")
.HasAnnotation("ProductVersion", "1.1.0-rtm-22752")
.HasAnnotation("SqlServer:Sequence:.catalog_brand_hilo", "'catalog_brand_hilo', '', '1', '10', '', '', 'Int64', 'False'")
.HasAnnotation("SqlServer:Sequence:.catalog_hilo", "'catalog_hilo', '', '1', '10', '', '', 'Int64', 'False'")
.HasAnnotation("SqlServer:Sequence:.catalog_type_hilo", "'catalog_type_hilo', '', '1', '10', '', '', 'Int64', 'False'")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure.CatalogBrand", b =>
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogBrand", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
@ -28,14 +29,14 @@ namespace Catalog.API.Infrastructure.Migrations
b.Property<string>("Brand")
.IsRequired()
.HasAnnotation("MaxLength", 100);
.HasMaxLength(100);
b.HasKey("Id");
b.ToTable("CatalogBrand");
});
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure.CatalogItem", b =>
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogItem", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
@ -50,7 +51,7 @@ namespace Catalog.API.Infrastructure.Migrations
b.Property<string>("Name")
.IsRequired()
.HasAnnotation("MaxLength", 50);
.HasMaxLength(50);
b.Property<string>("PictureUri");
@ -65,7 +66,7 @@ namespace Catalog.API.Infrastructure.Migrations
b.ToTable("Catalog");
});
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure.CatalogType", b =>
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogType", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
@ -74,21 +75,44 @@ namespace Catalog.API.Infrastructure.Migrations
b.Property<string>("Type")
.IsRequired()
.HasAnnotation("MaxLength", 100);
.HasMaxLength(100);
b.HasKey("Id");
b.ToTable("CatalogType");
});
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure.CatalogItem", b =>
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Common.Infrastructure.Data.IntegrationEvent", b =>
{
b.HasOne("Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure.CatalogBrand", "CatalogBrand")
b.Property<Guid>("EventId")
.ValueGeneratedOnAdd();
b.Property<string>("Content")
.IsRequired();
b.Property<DateTime>("CreationTime");
b.Property<string>("EventTypeName")
.IsRequired()
.HasMaxLength(200);
b.Property<int>("State");
b.Property<int>("TimesSent");
b.HasKey("EventId");
b.ToTable("IntegrationEvent");
});
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogItem", b =>
{
b.HasOne("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogBrand", "CatalogBrand")
.WithMany()
.HasForeignKey("CatalogBrandId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure.CatalogType", "CatalogType")
b.HasOne("Microsoft.eShopOnContainers.Services.Catalog.API.Model.CatalogType", "CatalogType")
.WithMany()
.HasForeignKey("CatalogTypeId")
.OnDelete(DeleteBehavior.Cascade);


+ 1
- 1
src/Services/Common/Infrastructure/Catalog/CatalogPriceChanged.cs View File

@ -4,7 +4,7 @@ using System.Text;
namespace Microsoft.eShopOnContainers.Services.Common.Infrastructure.Catalog
{
public class CatalogPriceChanged : IIntegrationEvent
public class CatalogPriceChanged : IntegrationEventBase
{
public int ItemId { get; private set; }


src/Services/Common/Infrastructure/IIntegrationEvent.cs → src/Services/Common/Infrastructure/Data/EventStateEnum.cs View File

@ -4,7 +4,10 @@ using System.Text;
namespace Microsoft.eShopOnContainers.Services.Common.Infrastructure
{
public interface IIntegrationEvent
{
public enum EventStateEnum
{
NotSend = 0,
Sent = 1,
SendingFailed = 2
}
}

+ 26
- 0
src/Services/Common/Infrastructure/Data/IntegrationEvent.cs View File

@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Text;
using Newtonsoft.Json;
namespace Microsoft.eShopOnContainers.Services.Common.Infrastructure.Data
{
public class IntegrationEvent
{
public IntegrationEvent(IntegrationEventBase @event)
{
EventId = @event.Id;
CreationTime = DateTime.UtcNow;
EventTypeName = @event.GetType().FullName;
Content = JsonConvert.SerializeObject(@event);
State = EventStateEnum.NotSend;
TimesSent = 0;
}
public Guid EventId { get; private set; }
public string EventTypeName { get; private set; }
public EventStateEnum State { get; set; }
public int TimesSent { get; set; }
public DateTime CreationTime { get; private set; }
public string Content { get; private set; }
}
}

+ 3
- 3
src/Services/Common/Infrastructure/EventBus.cs View File

@ -30,7 +30,7 @@ namespace Microsoft.eShopOnContainers.Services.Common.Infrastructure
_handlers = new Dictionary<string, List<IIntegrationEventHandler>>();
_eventTypes = new List<Type>();
}
public void Publish(IIntegrationEvent @event)
public void Publish(IntegrationEventBase @event)
{
var eventName = @event.GetType().Name;
var factory = new ConnectionFactory() { HostName = _connectionString };
@ -51,7 +51,7 @@ namespace Microsoft.eShopOnContainers.Services.Common.Infrastructure
}
public void Subscribe<T>(IIntegrationEventHandler<T> handler) where T : IIntegrationEvent
public void Subscribe<T>(IIntegrationEventHandler<T> handler) where T : IntegrationEventBase
{
var eventName = typeof(T).Name;
if (_handlers.ContainsKey(eventName))
@ -72,7 +72,7 @@ namespace Microsoft.eShopOnContainers.Services.Common.Infrastructure
}
public void Unsubscribe<T>(IIntegrationEventHandler<T> handler) where T : IIntegrationEvent
public void Unsubscribe<T>(IIntegrationEventHandler<T> handler) where T : IntegrationEventBase
{
var eventName = typeof(T).Name;
if (_handlers.ContainsKey(eventName) && _handlers[eventName].Contains(handler))


+ 3
- 3
src/Services/Common/Infrastructure/IEventBus.cs View File

@ -6,8 +6,8 @@ namespace Microsoft.eShopOnContainers.Services.Common.Infrastructure
{
public interface IEventBus
{
void Subscribe<T>(IIntegrationEventHandler<T> handler) where T: IIntegrationEvent;
void Unsubscribe<T>(IIntegrationEventHandler<T> handler) where T : IIntegrationEvent;
void Publish(IIntegrationEvent @event);
void Subscribe<T>(IIntegrationEventHandler<T> handler) where T: IntegrationEventBase;
void Unsubscribe<T>(IIntegrationEventHandler<T> handler) where T : IntegrationEventBase;
void Publish(IntegrationEventBase @event);
}
}

+ 1
- 1
src/Services/Common/Infrastructure/IIntegrationEventHandler.cs View File

@ -6,7 +6,7 @@ using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Services.Common.Infrastructure
{
public interface IIntegrationEventHandler<in TIntegrationEvent> : IIntegrationEventHandler
where TIntegrationEvent: IIntegrationEvent
where TIntegrationEvent: IntegrationEventBase
{
Task Handle(TIntegrationEvent @event);
}


+ 16
- 0
src/Services/Common/Infrastructure/IntegrationEventBase.cs View File

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Microsoft.eShopOnContainers.Services.Common.Infrastructure
{
public class IntegrationEventBase
{
public IntegrationEventBase()
{
Id = Guid.NewGuid();
}
public Guid Id { get; private set; }
}
}

Loading…
Cancel
Save