Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NATS Aspire container #1175

Merged
merged 57 commits into from
Mar 7, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
a4dcc11
NATS Aspire container
mtmk Dec 2, 2023
135a213
Use central package management
ReubenBond Dec 2, 2023
2088f5b
Merge branch 'main' into nats
sebastienros Dec 13, 2023
7b9690f
Add telemetry to defaults
mtmk Jan 14, 2024
732c8f1
Add JetStream option
mtmk Jan 14, 2024
6c2d73c
Merge branch 'main' into nats
mtmk Jan 14, 2024
56126f5
Fixed build
mtmk Jan 14, 2024
35a8972
Merge branch 'main' into nats
mtmk Feb 12, 2024
35f22fe
Fixed sln merge issue
mtmk Feb 12, 2024
7d94f67
Moved sample to playgrounds
mtmk Feb 12, 2024
27ec961
sln fixes
mtmk Feb 12, 2024
70987ad
Updated playground nats
mtmk Feb 12, 2024
b5c2c37
Moved hosting bits
mtmk Feb 12, 2024
28b2112
Merge fix
mtmk Feb 12, 2024
47d5268
Fixed spelling
mtmk Feb 12, 2024
4d2ca61
Slightly more involved NATS sample
mtmk Feb 12, 2024
7916e6b
NATS sample tweak
mtmk Feb 12, 2024
27a8609
Rename to Aspire.Nats.Client and added config
mtmk Feb 13, 2024
3e8fc4f
Merge remote-tracking branch 'upstream/main' into nats
mtmk Feb 13, 2024
4e27824
Fixed build
mtmk Feb 13, 2024
ec581ba
Merge remote-tracking branch 'upstream/main' into nats
mtmk Feb 14, 2024
f2884de
Added NATS publish as container
mtmk Feb 14, 2024
f52811d
NATS conformance tests
mtmk Feb 14, 2024
2eb8acd
NATS health check
mtmk Feb 14, 2024
607f025
NATS health checks
mtmk Feb 14, 2024
f772de8
NATS client extension docs
mtmk Feb 14, 2024
cec35c9
Added a simple ping example
mtmk Feb 14, 2024
54d5946
Bit more elaborate NATS example
mtmk Feb 14, 2024
6e255ae
Removed `nats` scheme
Feb 22, 2024
4d37047
Update src/Aspire.Hosting/Nats/NatsBuilderExtensions.cs
mtmk Feb 23, 2024
ee10f49
Merge remote-tracking branch 'upstream/main' into nats
mtmk Feb 24, 2024
b583065
Catch up to main
mtmk Feb 24, 2024
d138aa9
NATS ConnectionStringExpression
mtmk Feb 24, 2024
9b950eb
ConnectionStringExpression fix
mtmk Feb 24, 2024
2f77b55
Merge remote-tracking branch 'upstream/main' into nats
mtmk Feb 24, 2024
08e26aa
Fixed test
mtmk Feb 24, 2024
48d6bd3
Nats VerifyManifest test
mtmk Feb 25, 2024
f6fd1b0
Merge remote-tracking branch 'upstream/main' into nats
mtmk Mar 2, 2024
ff2cb10
nats catches up to main
mtmk Mar 2, 2024
bf74eda
nats endpoint tweak
mtmk Mar 3, 2024
a40a832
Merge remote-tracking branch 'upstream/main' into nats
mtmk Mar 3, 2024
1405623
nats with image
mtmk Mar 3, 2024
8eb4d94
nats image tweaks
mtmk Mar 3, 2024
4d488e2
revert
mtmk Mar 3, 2024
4afdad8
Merge remote-tracking branch 'upstream/main' into nats
mtmk Mar 4, 2024
1a62a75
nats manifest test
mtmk Mar 4, 2024
3606984
Update tests/Aspire.Nats.Client.Tests/ConfigurationTests.cs
mtmk Mar 5, 2024
6eefa74
rename nats AppHost
mtmk Mar 5, 2024
388bd85
rename Aspire.NATS.Net
mtmk Mar 5, 2024
f31d49f
nats health checks tweak
mtmk Mar 5, 2024
2c10732
nats component test tweaks
mtmk Mar 5, 2024
f98cfa6
Merge remote-tracking branch 'upstream/main' into nats
mtmk Mar 5, 2024
0eab699
nats component docs
mtmk Mar 5, 2024
1f773f9
nats resource string constants
mtmk Mar 5, 2024
15ffa21
Merge branch 'main' into nats
sebastienros Mar 7, 2024
7836160
Add manifest
sebastienros Mar 7, 2024
d005020
Rename AddNats to AddNatsClient
sebastienros Mar 7, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions playground/nats/Nats.ApiService/Nats.ApiService.csproj
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
Expand All @@ -7,6 +7,10 @@
<InvariantGlobalization>true</InvariantGlobalization>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Swashbuckle.AspNetCore" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\src\Components\Aspire.Nats\Aspire.Nats.csproj" />
<ProjectReference Include="..\..\Playground.ServiceDefaults\Playground.ServiceDefaults.csproj" />
Expand Down
47 changes: 47 additions & 0 deletions playground/nats/Nats.ApiService/Nats.ApiService.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
@HostAddress = http://localhost:5156

POST {{HostAddress}}/stream/
Content-Type: application/json

{
"name": "EVENTS",
"description": "Events Description",
"subjects": ["events.>"],
"max_msgs": 10
}

###

GET {{HostAddress}}/stream/EVENTS
Accept: application/json

###

POST {{HostAddress}}/publish/
Content-Type: application/json

{
"subject": "events.mouse.click",
"name": "Mouse Event",
"description": "Mouse Event Description",
"priority": 0.1
}

###

POST {{HostAddress}}/publish/
Content-Type: application/json

{
"subject": "events.key.a",
"name": "Key Event",
"description": "Key Event Description",
"priority": 0.5
}

###

GET {{HostAddress}}/consume/EVENTS
Accept: application/json

###
77 changes: 71 additions & 6 deletions playground/nats/Nats.ApiService/Program.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,82 @@
using System.Text.Json.Serialization;
using NATS.Client.Core;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make this app a little more real? Can it use pubsub as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure, let me have a go!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I created a simple JetStream example since it's easier demonstrate persisted messages using Web API. As for pubsub I suppose we'd need a frontend using SignalR or Blazor. Maybe a simple chat or something?

using NATS.Client.JetStream;
using NATS.Client.JetStream.Models;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

builder.AddServiceDefaults();

builder.AddNats("nats");
builder.AddNats("nats", opts =>
{
var jsonRegistry = new NatsJsonContextSerializerRegistry(AppJsonContext.Default);
return opts with { SerializerRegistry = jsonRegistry };
Comment on lines +17 to +18
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this required or optional?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's optional. required for json serialization. Would you suggests to make it easier for ad hoc JSON serialization?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think so, but I don't know enough about your API design to suggest something.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Challenge is our core NuGet packages are AOT ready and for ad-hoc JSON we have an extra package. I can bring NATS ad-hoc JSON package into Aspite.Nats.Client if AOT is not a concern (yet?).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Aspire component packages are intended to be AOT compatible if the associated client library is AOT compatible. For example, we are using the Configuration Binder source generator, so config binding works in native AOT.

We have 2 service apps in https://github.com/dotnet/eShop that support being published to native AOT:

So, yes, AOT is a concern for Aspire.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the comments. I'm going to leave this as it is for now since it looks like we're going to need some changes on NATS Client side as well (nats-io/nats.net#417)

});

builder.Services.AddSingleton<INatsJSContext>(static provider =>
davidfowl marked this conversation as resolved.
Show resolved Hide resolved
{
return new NatsJSContextFactory().CreateContext(provider.GetService<INatsConnection>()!);
});

var app = builder.Build();

app.MapGet("/", async (INatsConnection nats) => $"""
Hello NATS!
rtt: {await nats.PingAsync()}
{nats.ServerInfo}
""");
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}

app.MapPost("/stream", async (StreamConfig config, INatsJSContext jetStream) =>
{
var stream = await jetStream.CreateStreamAsync(config);
var name = stream.Info.Config.Name;
return Results.Created($"/stream/{name}", name);
});

app.MapGet("/stream/{name}", async (string name, INatsJSContext jetStream) =>
{
var stream = await jetStream.GetStreamAsync(name);
return Results.Ok(stream.Info);
});

app.MapPost("/publish/", async (AppEvent @event, INatsJSContext jetStream) =>
{
var ack = await jetStream.PublishAsync(@event.Subject, @event);
ack.EnsureSuccess();
return Results.Created();
});

app.MapGet("/consume/{name}", async (string name, INatsJSContext jetStream) =>
{
var stream = await jetStream.GetStreamAsync(name);
var consumer = await stream.CreateOrderedConsumerAsync();

var events = new List<AppEvent>();
await foreach(var msg in consumer.ConsumeAsync<AppEvent>())
{
events.Add(msg.Data!);

if (msg.Metadata?.NumPending == 0)
{
break;
}
}

return Results.Ok(events);
});

app.Run();

public record AppEvent(string Subject, string Name, string Description, decimal Priority);

[JsonSerializable(typeof(AppEvent))]
[JsonSourceGenerationOptions(PropertyNameCaseInsensitive = true)]
public partial class AppJsonContext : JsonSerializerContext
{
}
2 changes: 1 addition & 1 deletion playground/nats/Nats.AppHosting/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

var builder = DistributedApplication.CreateBuilder(args);

var nats = builder.AddNats("nats");
var nats = builder.AddNats("nats", enableJetStream: true);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You may wish to add a PublishAsContainer(...) extension method. Take a look what we have done for resources like Redis where we don't have AddRedisContainer(...) anymore but we've added a PublishAsContainer() method which can be used to tell the resource to write to the manifest as a container.v0 resource.


builder.AddProject<Projects.Nats_ApiService>("apiservice")
.WithReference(nats);
Expand Down
15 changes: 12 additions & 3 deletions src/Components/Aspire.Nats/NatsBuilderExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,20 +1,29 @@
// Licensed to the .NET Foundation under one or more agreements.
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.Extensions.Configuration;
using NATS.Client.Core;
using NATS.Client.Hosting;

namespace Microsoft.Extensions.Hosting;

public static class NatsBuilderExtensions
{
public static void AddNats(this IHostApplicationBuilder builder, string connectionName)
public static void AddNats(this IHostApplicationBuilder builder, string connectionName, Func<NatsOpts, NatsOpts>? configureOptions = null)
{
ArgumentNullException.ThrowIfNull(builder);

if (builder.Configuration.GetConnectionString(connectionName) is string connectionString)
{
builder.Services.AddNats(configureOpts: opts => opts with { Url = connectionString });
builder.Services.AddNats(configureOpts: opts =>
{
if (configureOptions != null)
{
opts = configureOptions(opts);
}

return opts with { Url = connectionString };
});
}
else
{
Expand Down
Loading