This sample demonstrates how to generate an AsyncAPI schema document from NServiceBus endpoints using Neuroglia.AsyncApi.
AsyncAPI integration is not an officially supported feature of NServiceBus, and this example is for demonstration purposes only.
Code walk-through
This sample contains four projects:
- AsyncAPI.Feature - a class library containing shared code required to enable the custom
AsyncApiFeature
- Shared - a class library defining the events that are being published and subscribed to
- AsyncAPI.GenericHost - a console application that publishes two events, and generates an AsyncAPI document schema for its structure and saves it to disk
- Subscriber - a console application that subscribes to the events published by AsyncAPI.GenericHost
Feature project
The project contains all the necessary code for registering a custom AsyncAPI document schema generator.
The EndpointConfigurationExtensions
enables the AsyncApiFeature
.
public static void EnableAsyncApiSupport(
this EndpointConfiguration endpointConfiguration)
{
endpointConfiguration.EnableFeature<AsyncApiFeature>();
}
AsyncAPI feature
The feature creates a list of events and registers them in the container so that they are available to be used by the AsyncAPI document generator.
foreach (var messageMetadata in messageMetadataRegistry.GetAllMessages())
{
if (conventions.IsEventType(messageMetadata.MessageType))
{
events.Add(messageMetadata.MessageType);
}
}
context.Services.AddSingleton(new TypeCache
{
EndpointName = context.Settings.EndpointName(),
Events = events
});
Finally, the code registers a custom implementation of the Neuroglia IAsyncApiDocumentGenerator
which will be used instead of the default implementation to generate the NServiceBus-specific schema document.
context.Services.AddTransient<IAsyncApiDocumentGenerator>(
provider => new ApiDocumentGenerator(provider));
AsyncAPI document generator
This custom implementation of the Neuroglia IAsyncApiDocumentGenerator
creates one AsyncAPI schema document for the NServiceBus endpoint hosted in the application. It demonstrates how channel information with the endpoint's address (queue name) can be generated, containing the publish operation and the payload of the event being published.
foreach (var publishedEvent in typeCache.Events)
{
var channelName = $"{publishedEvent.FullName!}";
document.WithChannel(channelName, channel =>
{
channelBuilder = channel;
channel
.WithAddress(typeCache.EndpointName)
.WithDescription(publishedEvent.FullName);
});
await GenerateV3OperationForAsync(
document,
channelName,
channelBuilder,
publishedEvent,
options,
cancellationToken);
}
This will get all events in the project - some may not be published by this endpoint, others may only be subscribed to. If that's the case, an extra filter would need to be added to differentiate the events, possibly based on namespace or custom attributes. Look at the AsyncAPI with custom message conventions sample to see how this can be addressed.
This code can be extended to include subscriptions, as well as sent/received messages.
Shared project
The project does not have any references to AsyncAPI. It contains two events that are published by the AsyncAPI.
application and subscribed to by the Subscriber
endpoint.
Generic host project
Setup AsyncAPI
The project enables the AsyncAPI schema generation using three setup calls.
First, by adding the Neuroglia AsyncAPI.
builder.Services.AddAsyncApi();
Second, by adding the AsyncAPI feature to the NServiceBus endpoint.
endpointConfiguration.EnableAsyncApiSupport();
Lastly, by adding a background service to generate and write the AsyncAPI document schema to disk.
builder.Services.AddHostedService<AsyncAPISchemaWriter>();
Access AsyncAPI schema document
The AsyncAPISchemaWriter
uses the custom document generator injected as part of the AsyncApiFeature
to generate the document schema and write it to disk.
var documents = await apiDocumentGenerator.GenerateAsync(
markupTypes: null!, options, cancellationToken);
var asyncApiDocuments = documents as IAsyncApiDocument[] ?? documents.ToArray();
if (!asyncApiDocuments.Any())
{
logger.LogInformation("No documents generated.");
return;
}
logger.LogInformation("Found #{Count} generated document(s).", asyncApiDocuments.Length);
foreach (var document in asyncApiDocuments)
{
using MemoryStream stream = new();
await asyncApiDocumentWriter.WriteAsync(
document, stream, AsyncApiDocumentFormat.Json, cancellationToken);
var schemaFile = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
"downloads",
$"{document.Title}.json");
await File.WriteAllBytesAsync(schemaFile, stream.ToArray(), cancellationToken);
logger.LogInformation($"Document {document.Title} written to {schemaFile}");
}
The file is saved into the default Downloads
folder - it can be viewed using the AsyncAPI Studio (by pasting in the contents) and incorporated into internal system documentation.
Subscriber project
The project does not have any references to AsyncAPI. It contains handlers for events that it is subscribing to.
Running the sample
When running the solution with Visual Studio, two applications will start automatically.
AsyncAPI.GenericHost (console)
- Press
1
to publishSampleEventOne
- received bySubscriber
. - Press
2
to publishSampleEventTwo
- received bySubscriber
.
Subscriber (console)
Displays all events published from AsyncAPI.
.