Skip to content

Commit b6f132a

Browse files
authored
Merge pull request #78 from Particular/ui-features
2 parents ee643cb + be753f5 commit b6f132a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

83 files changed

+7753
-537
lines changed

.github/workflows/ci.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,13 @@ jobs:
2727
dotnet-version: 8.0.x
2828
- name: Build
2929
run: dotnet build src --configuration Release -graph
30+
- name: Set up Node.js
31+
uses: actions/[email protected]
32+
with:
33+
node-version: 21.6.x
34+
- name: install frontend packages
35+
run: npm install
36+
working-directory: src/frontend
37+
- name: build frontend
38+
run: npm run build
39+
working-directory: src/frontend

README.md

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,23 @@
1-
# MassTransitShowcaseDemo
1+
# The Particular Platform for MassTransit
22

3+
## Launching the Showcase
34

4-
## Launching the Particular Platform for MassTransit
5-
To run the code sample you have 3 options in terms of transports:
5+
To run the code sample you have 2 options in terms of transports:
66

77
## **RabbitMQ**
88

9-
>[!NOTE]
10-
The RabbitMQ Broker is started as part of the docker compose process
11-
>
9+
> [!NOTE]
10+
> The RabbitMQ Broker is started as part of the docker compose process
1211
1312
Run docker command below from the `src` folder in a CLI
13+
1414
```cmd
1515
docker compose -f docker-compose-base.yml -f compose-rabbitmq.yml --env-file rabbit.env up -d
1616
```
17+
1718
## **Azure ServiceBus**
1819

19-
Configure the access to your Azure Service Bus namespace by editing the variables in `src/asb.env`
20+
Configure the access to your Azure Service Bus namespace by editing the variables in `src/asb.env`
2021

2122
```env
2223
CONNECTIONSTRING="Endpoint=sb://[NAMESPACE].servicebus.windows.net/;SharedAccessKeyName=[KEYNAME];SharedAccessKey=[KEY]"
@@ -30,16 +31,14 @@ docker compose -f docker-compose-base.yml -f compose-azure.yml --env-file asb.en
3031

3132
## Running the code
3233

33-
34-
>[!WARNING]
34+
> [!WARNING]
3535
> When using Visual Studio, make sure you have "Enable Multi-Project Launch profiles" setting on. Allow Visual Studio 2024 "multi-launch" so you can easily select the profile you want to run.
3636
>
3737
> It can be activated by accessing the Tools menu -> Manage preview features- Enable Multi-Project Launch profiles.
38-
>
3938
40-
After opening the solutions (from Visual Studio or Rider), choose one of the run profiles that matches the transport configured previously:
39+
After opening the solutions (from Visual Studio or Rider), choose one of the run profiles that matches the transport configured previously:
40+
4141
- `RabbitMQ`
42-
- `AmazonSQS`
4342
- `Azure Service Bus`
4443

4544
Run the solution to start the demo.
@@ -49,5 +48,4 @@ Navigate to http://localhost:9090/ to see the UI
4948

5049
The sample consists of 4 console applications hosting MassTransit message producers and consumers that implement a simplified order processing logic from an e-commerce system.
5150

52-
5351
![System Overview](diagram.svg "width=680")

src/Billing/Billing.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@
1313
<ProjectReference Include="..\Messages\Messages.csproj" />
1414
</ItemGroup>
1515

16+
<ItemGroup>
17+
<FrameworkReference Include="Microsoft.AspNetCore.App" />
18+
</ItemGroup>
19+
1620
<ItemGroup>
1721
<PackageReference Include="MassTransit.Azure.ServiceBus.Core" Version="8.3.0" />
1822
<PackageReference Include="MassTransit.RabbitMQ" Version="8.3.0" />

src/Billing/BillingHub.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
namespace Billing
2+
{
3+
using Microsoft.AspNetCore.SignalR;
4+
5+
public class BillingHub(SimulationEffects simulationEffects) : Hub
6+
{
7+
public override async Task OnConnectedAsync()
8+
{
9+
await Clients.Caller.SendAsync("SyncValues", simulationEffects.MessagesProcessed, simulationEffects.MessagesErrored, simulationEffects.ShouldFailRetries);
10+
await base.OnConnectedAsync();
11+
}
12+
13+
public async Task SetFailRetries(bool shouldFailRetries)
14+
{
15+
simulationEffects.ShouldFailRetries = shouldFailRetries;
16+
await Clients.Caller.SendAsync("SyncValues", simulationEffects.MessagesProcessed, simulationEffects.MessagesErrored, simulationEffects.ShouldFailRetries);
17+
}
18+
}
19+
}

src/Billing/ConsoleBackgroundService.cs

Lines changed: 0 additions & 47 deletions
This file was deleted.

src/Billing/Consumers/BillOrderConsumer.cs

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,33 @@
44
using Helper;
55
using MassTransit;
66
using Messages;
7+
using Microsoft.AspNetCore.SignalR;
78

8-
public class BillOrderConsumer(SimulationEffects simulationEffects) : IConsumer<OrderPlaced>
9+
public class BillOrderConsumer(SimulationEffects simulationEffects, IHubContext<BillingHub> billingHub) : IConsumer<OrderPlaced>
910
{
1011
public async Task Consume(ConsumeContext<OrderPlaced> context)
1112
{
12-
await simulationEffects.SimulatedMessageProcessing(context.CancellationToken);
13+
await billingHub.Clients.All.SendAsync("ProcessingMessage", context.Message, context.CancellationToken);
14+
try
15+
{
16+
await simulationEffects.SimulateBillingProcessing(context);
17+
}
18+
catch (OperationCanceledException) when (context.CancellationToken.IsCancellationRequested) { throw; }
19+
catch
20+
{
21+
var messageViewId = DeterministicGuid.GetTheViewId(context.MessageId.ToString(), context.ReceiveContext.InputAddress.ToString());
22+
23+
await billingHub.Clients.All.SendAsync("MessageError", context.Message, context.MessageId, messageViewId, context.CancellationToken);
24+
throw;
25+
}
1326

1427
var orderBilled = new OrderBilled
1528
{
16-
OrderId = context.Message.OrderId
29+
OrderId = context.Message.OrderId,
30+
Contents = context.Message.Contents
1731
};
1832

1933
await context.Publish(orderBilled);
20-
21-
await ConsoleHelper.WriteMessageProcessed(context.SentTime ?? DateTime.UtcNow);
34+
await billingHub.Clients.All.SendAsync("OrderBilled", orderBilled, context.CancellationToken);
2235
}
2336
}

src/Billing/Program.cs

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
using System.Text;
77
using Microsoft.Extensions.Logging;
88
using System;
9+
using Microsoft.AspNetCore.Hosting;
10+
using Microsoft.AspNetCore.Builder;
911

1012
class Program
1113
{
@@ -14,16 +16,31 @@ public static IHostBuilder CreateHostBuilder(string[] args)
1416
Console.OutputEncoding = Encoding.UTF8;
1517
var host = Host.CreateDefaultBuilder(args)
1618
.ConfigureLogging(cfg => cfg.SetMinimumLevel(LogLevel.Warning))
17-
.ConfigureServices((hostContext, services) =>
19+
.ConfigureWebHostDefaults(webBuilder =>
1820
{
19-
services.AddMassTransit(x =>
21+
webBuilder.ConfigureServices(services =>
2022
{
21-
x.AddConsumers(Assembly.GetExecutingAssembly());
22-
x.SetupTransport(args);
23-
});
23+
services.AddMassTransit(x =>
24+
{
25+
x.SetKebabCaseEndpointNameFormatter();
26+
x.AddConsumers(Assembly.GetExecutingAssembly());
27+
x.SetupTransport(args);
28+
});
2429

25-
services.AddSingleton<SimulationEffects>();
26-
services.AddHostedService<ConsoleBackgroundService>();
30+
services.AddCors();
31+
services.AddSignalR(options => { options.EnableDetailedErrors = true; });
32+
services.AddSingleton<SimulationEffects>();
33+
});
34+
webBuilder.UseUrls("http://*:5002");
35+
webBuilder.Configure(app =>
36+
{
37+
app.UseCors(options => options.AllowAnyHeader().AllowAnyMethod().WithOrigins("http://localhost:61335").AllowCredentials());
38+
app.UseRouting();
39+
app.UseEndpoints(endpoints =>
40+
{
41+
endpoints.MapHub<BillingHub>("/billingHub");
42+
});
43+
});
2744
});
2845

2946
return host;
@@ -36,4 +53,4 @@ static async Task Main(string[] args)
3653
var host = CreateHostBuilder(args).Build();
3754
await host.RunAsync();
3855
}
39-
}
56+
}

src/Billing/SimulationEffects.cs

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,42 @@
11
namespace Billing;
22

3-
public class SimulationEffects
3+
using MassTransit;
4+
using Messages;
5+
using Microsoft.AspNetCore.SignalR;
6+
7+
public class SimulationEffects(IHubContext<BillingHub> billingHub)
48
{
5-
public void IncreaseFailureRate() => failureRate = Math.Min(1, failureRate + FailureRateIncrement);
9+
int messagesErrored = 0;
10+
int messagesProcessed = 0;
611

7-
public void DecreaseFailureRate() => failureRate = Math.Max(0, failureRate - FailureRateIncrement);
12+
public int MessagesProcessed { get => messagesProcessed; private set => messagesProcessed = value; }
13+
public int MessagesErrored { get => messagesErrored; private set => messagesErrored = value; }
14+
public bool ShouldFailRetries { get; set; } = false;
815

9-
public void WriteState(TextWriter output) => output.WriteLine("Failure rate: {0:P0}", failureRate);
1016

11-
public async Task SimulatedMessageProcessing(CancellationToken cancellationToken = default)
17+
public async Task SimulateBillingProcessing(ConsumeContext<OrderPlaced> context)
1218
{
13-
await Task.Delay(200, cancellationToken);
19+
try
20+
{
21+
context.TryGetHeader("FailOn", out string failOn);
22+
//Retries leave ServiceControl headers on the ReceiveContext. Choosing one at random here...
23+
var isRetry = context.ReceiveContext.TransportHeaders.TryGetHeader("ServiceControl.RetryTo", out var _);
24+
if (isRetry)
25+
{
26+
await billingHub.Clients.All.SendAsync("RetryAttempted");
27+
}
28+
if (Enum.TryParse(failOn, out Consumers endpointName) && endpointName == Consumers.Billing
29+
&& (!isRetry || ShouldFailRetries))
30+
{
31+
Interlocked.Increment(ref messagesErrored);
32+
throw new Exception($"A simulated failure occurred in Billing, OrderId: {context.Message.OrderId}, Contents: {string.Join(", ", context.Message.Contents)}");
33+
}
1434

15-
if (Random.Shared.NextDouble() < failureRate)
35+
Interlocked.Increment(ref messagesProcessed);
36+
}
37+
finally
1638
{
17-
throw new Exception("BOOM! A failure occurred");
39+
await billingHub.Clients.All.SendAsync("SyncValues", MessagesProcessed, MessagesErrored, ShouldFailRetries, context.CancellationToken);
1840
}
1941
}
20-
21-
double failureRate;
22-
const double FailureRateIncrement = 0.1;
23-
24-
public void Reset()
25-
{
26-
failureRate = 0;
27-
}
2842
}

src/ClientUI/ClientHub.cs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
namespace ClientUI
2+
{
3+
using Messages;
4+
using Microsoft.AspNetCore.SignalR;
5+
6+
public class ClientHub(SimulatedCustomers simulatedCustomers) : Hub
7+
{
8+
public override async Task OnConnectedAsync()
9+
{
10+
await Clients.Caller.SendAsync("Initialise", simulatedCustomers.OrdersPlaced, Enum.GetValues<Consumers>().Select(x => x.ToString()));
11+
await base.OnConnectedAsync();
12+
}
13+
14+
public async Task CreateOrder(float requestCount, string failOn)
15+
{
16+
for (int i = 0; i < (int)requestCount; i++)
17+
{
18+
var order = await simulatedCustomers.PlaceSingleOrder(failOn, Context.ConnectionAborted);
19+
await Clients.All.SendAsync("OrderRequested", order, simulatedCustomers.OrdersPlaced, Context.ConnectionAborted);
20+
}
21+
}
22+
23+
public async Task RunScenario()
24+
{
25+
foreach (var failOn in Enum.GetValues<Consumers>())
26+
{
27+
for (int i = 0; i < 3; i++)
28+
{
29+
var order = await simulatedCustomers.PlaceSingleOrder(failOn.ToString(), Context.ConnectionAborted);
30+
await Clients.All.SendAsync("OrderRequested", order, simulatedCustomers.OrdersPlaced, Context.ConnectionAborted);
31+
}
32+
}
33+
}
34+
}
35+
36+
}

src/ClientUI/ClientUI.csproj

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,11 @@
1414
<PackageReference Include="MassTransit.AmazonSQS" Version="8.3.0" />
1515
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.1" />
1616
</ItemGroup>
17-
17+
18+
<ItemGroup>
19+
<FrameworkReference Include="Microsoft.AspNetCore.App" />
20+
</ItemGroup>
21+
1822
<ItemGroup>
1923
<ProjectReference Include="..\Messages\Messages.csproj" />
2024
<ProjectReference Include="..\Helper\Helper.csproj" />

0 commit comments

Comments
 (0)