### Run the Streetlights API Example Source: https://github.com/asyncapi/saunter/blob/main/examples/StreetlightsAPI/README.md Starts the Streetlights API example project using the .NET CLI. The output will show available endpoints for AsyncAPI documentation and UI. ```sh cd ~/saunter/examples/StreetlightsAPI dotnet run ``` -------------------------------- ### Multi-Document Setup Source: https://github.com/asyncapi/saunter/blob/main/_autodocs/README.md Example of configuring multiple named AsyncAPI documents. ```APIDOC ## Multi-Document Setup ### Description Demonstrates how to configure and use multiple named AsyncAPI documents. ### Code Example ```csharp services.ConfigureNamedAsyncApi("Users", doc => { doc.Info = new AsyncApiInfo("Users API", "1.0.0"); }); [AsyncApi("Users")] public class UserService { } ``` ``` -------------------------------- ### Configure AsyncAPI Schema Generation Source: https://github.com/asyncapi/saunter/blob/main/_autodocs/api-reference/async-api-options.md This example shows a comprehensive setup for AsyncAPI schema generation. It configures the assemblies to scan, the base AsyncAPI document details (version, info, servers, default content type), middleware routes and UI, and adds custom filters for documents, channels, and operations. ```csharp services.AddAsyncApiSchemaGeneration(options => { // Specify assemblies to scan options.AssemblyMarkerTypes = new[] { typeof(UserService.Program), typeof(OrderService.Program) }; // Configure base document options.AsyncApi = new AsyncApiDocument { AsyncApi = "2.6.0", Info = new AsyncApiInfo() { Title = "Event Streaming API", Version = "3.0.0", Description = "Internal async messaging across services", Contact = new AsyncApiContact() { Name = "Platform Team", Email = "platform@example.com" } }, Servers = { ["prod"] = new AsyncApiServer() { Url = "kafka.prod.example.com:9092", Protocol = "kafka" }, ["staging"] = new AsyncApiServer() { Url = "kafka.staging.example.com:9092", Protocol = "kafka" } }, DefaultContentType = "application/json" }; // Configure middleware routes options.Middleware.Route = "/api/events/schema.json"; options.Middleware.UiBaseRoute = "/api/events/ui/"; options.Middleware.UiTitle = "Event Streaming"; // Add filters options.AddDocumentFilter(); options.AddAsyncApiChannelFilter(); options.AddOperationFilter(); }); // Configure multiple documents services.ConfigureNamedAsyncApi("Users", doc => { doc.Info = new AsyncApiInfo("User Events", "1.0.0"); doc.Servers["prod"] = new AsyncApiServer() { Url = "..." }; }); services.ConfigureNamedAsyncApi("Orders", doc => { doc.Info = new AsyncApiInfo("Order Events", "2.0.0"); doc.Servers["prod"] = new AsyncApiServer() { Url = "..." }; }); ``` -------------------------------- ### Example AsyncAPI JSON Output Source: https://github.com/asyncapi/saunter/blob/main/README.md This is an example of the AsyncAPI JSON document generated by Saunter, showing properties from `Startup.cs` and attributes. ```json // HTTP GET /asyncapi/asyncapi.json { // Properties from Startup.cs "asyncapi": "2.1.0", "info": { "title": "Streetlights API", "version": "1.0.0", "description": "The Smartylighting Streetlights API allows you to remotely manage the city lights.", // ... }, // Properties generated from Attributes "channels": { "light/measured": { "publish": { "operationId": "PublishLightMeasuredEvent", "summary": "Inform about environmental lighting conditions for a particular streetlight.", //... } ``` -------------------------------- ### Install UI Assets with npm Source: https://github.com/asyncapi/saunter/blob/main/examples/StreetlightsAPI/README.md Installs the necessary UI assets for the Saunter project. Requires Node.js and npm. ```sh cd ~/saunter/src/Saunter.UI npm install ``` -------------------------------- ### Install Saunter Package Source: https://github.com/asyncapi/saunter/blob/main/README.md Use the dotnet CLI to add the Saunter package to your project. ```bash dotnet add package Saunter ``` -------------------------------- ### ChannelAttribute Usage Examples Source: https://github.com/asyncapi/saunter/blob/main/_autodocs/api-reference/attributes.md Shows how to use the ChannelAttribute with direct channel names and with server/binding references. It also includes an example of dynamic channel naming using a resolver. ```csharp [AsyncApi] public class MessageBus { [Channel("user.registered")] [PublishOperation(typeof(UserRegisteredEvent), "Users")] public void OnUserRegistered(UserRegisteredEvent evt) { } [Channel("user.deactivated", BindingsRef = "amqpQueue", Servers = new[] { "messaging" })] [SubscribeOperation(typeof(UserDeactivatedEvent))] public void OnUserDeactivated(UserDeactivatedEvent evt) { } } public class DynamicChannelResolver : IChannelResolver { private readonly Type _messageType; public DynamicChannelResolver(Type messageType) { _messageType = messageType; } public string ResolveChannelName() => $"events.{_messageType.Name}"; } [Channel(typeof(DynamicChannelResolver), typeof(MyEvent))] [PublishOperation(typeof(MyEvent))] public void OnMyEvent(MyEvent evt) { } ``` -------------------------------- ### ChannelParameterAttribute Usage Example Source: https://github.com/asyncapi/saunter/blob/main/_autodocs/api-reference/attributes.md Example demonstrating how to apply ChannelParameterAttribute to a method to define a channel parameter. ```csharp [Channel("user/{userId}/events")] [ChannelParameter("userId", typeof(int), Description = "The user's ID")] [PublishOperation(typeof(UserEvent))] public void OnUserEvent(int userId, UserEvent evt) { } ``` -------------------------------- ### AsyncApiAttribute Usage Examples Source: https://github.com/asyncapi/saunter/blob/main/_autodocs/api-reference/attributes.md Demonstrates how to apply the AsyncApiAttribute to classes. The first example uses the default document name, while the second specifies 'Orders' for a particular document. ```csharp [AsyncApi] public class DefaultMessageBus { [Channel("events.processed")] [PublishOperation(typeof(ProcessedEvent))] public void OnProcessed(ProcessedEvent evt) { } } [AsyncApi("Orders")] public class OrderMessageBus { [Channel("order.created")] [PublishOperation(typeof(OrderCreatedEvent))] public void OnOrderCreated(OrderCreatedEvent evt) { } } ``` -------------------------------- ### Complete Filtering Example Source: https://github.com/asyncapi/saunter/blob/main/_autodocs/api-reference/filters.md Demonstrates document, channel, and operation filters for enriching an AsyncAPI document. Includes registration of these filters. ```csharp // Document-level filter public class EnrichmentFilter : IDocumentFilter { public void Apply(AsyncApiDocument document, DocumentFilterContext context) { document.ExternalDocs ??= new AsyncApiExternalDocumentation(); document.ExternalDocs.Url = new("https://example.com/docs"); document.ExternalDocs.Description = "Full API documentation"; } } // Channel-level filter public class ChannelFilter : IChannelFilter { public void Apply(AsyncApiChannel channel, ChannelFilterContext context) { // Ensure all channels have descriptions if (string.IsNullOrEmpty(channel.Description)) { channel.Description = $"Channel: {context.Channel.Name}"; } } } // Operation-level filter public class OperationFilter : IOperationFilter { public void Apply(AsyncApiOperation operation, OperationFilterContext context) { // Automatically add correlation ID to all messages operation.Traits ??= new List(); } } // Registration services.AddAsyncApiSchemaGeneration(options => { options.AssemblyMarkerTypes = new[] { typeof(Program) }; options.AddDocumentFilter(); options.AddAsyncApiChannelFilter(); options.AddOperationFilter(); }); ``` -------------------------------- ### Install and Register AsyncApiSchemaGeneration Source: https://github.com/asyncapi/saunter/blob/main/_autodocs/quick-reference.md Configure services to add AsyncApi schema generation, specifying assembly marker types and basic document information. ```csharp services.AddAsyncApiSchemaGeneration(options => { options.AssemblyMarkerTypes = new[] { typeof(MyMessageBus) }; options.AsyncApi = new AsyncApiDocument { Info = new AsyncApiInfo { Title = "My Service", Version = "1.0.0" } }; }); ``` -------------------------------- ### Install Saunter NuGet Package Source: https://github.com/asyncapi/saunter/blob/main/_autodocs/README.md Add the Saunter NuGet package to your .NET project using the `dotnet add package` command. ```bash dotnet add package Saunter ``` -------------------------------- ### Multi-Document Setup Source: https://github.com/asyncapi/saunter/blob/main/_autodocs/README.md Configures and registers a named AsyncAPI document and associates it with a service class. Use this for generating multiple, distinct AsyncAPI documents within a single application. ```csharp services.ConfigureNamedAsyncApi("Users", doc => { doc.Info = new AsyncApiInfo("Users API", "1.0.0"); }); [AsyncApi("Users")] public class UserService { } ``` -------------------------------- ### Example Usage of IOperationFilter Source: https://github.com/asyncapi/saunter/blob/main/_autodocs/types.md Demonstrates how to implement IOperationFilter to access method and operation details for modifying AsyncAPI operations. ```csharp public class MyOperationFilter : IOperationFilter { public void Apply(AsyncApiOperation operation, OperationFilterContext context) { var methodName = context.Method.Name; var operationType = context.Operation.OperationType; // Modify the operation based on its source } } ``` -------------------------------- ### Channel Union Example Source: https://github.com/asyncapi/saunter/blob/main/_autodocs/architecture.md Demonstrates how channels with the same name are merged, allowing both publish and subscribe operations on a single channel. This is useful for defining bidirectional event streams. ```csharp ```csharp [Channel("events")] [PublishOperation(typeof(CreatedEvent))] public void OnCreated(CreatedEvent evt) { } [Channel("events")] [SubscribeOperation(typeof(ProcessedEvent))] public void OnProcessed(ProcessedEvent evt) { } ``` ``` -------------------------------- ### Example: Convention-Based Channel Names Source: https://github.com/asyncapi/saunter/blob/main/_autodocs/api-reference/channel-resolver.md An example implementation of IChannelResolver that generates channel names based on the message type's name using a convention. ```APIDOC ## Example 1: Convention-Based Channel Names ```csharp public class ConventionChannelResolver : IChannelResolver { private readonly Type _messageType; public ConventionChannelResolver(Type messageType) { _messageType = messageType; } public string ResolveChannelName() { // Generate channel name from message type name // UserCreatedEvent -> user.created var name = _messageType.Name .Replace("Event", "") .Insert(0, _messageType.Name[0].ToString().ToLower()) .Substring(1); return name.ToLower() + ".event"; } } // Usage [Channel(typeof(ConventionChannelResolver), typeof(UserCreatedEvent))] [PublishOperation(typeof(UserCreatedEvent))] public void OnUserCreated(UserCreatedEvent evt) { } // Generates channel: usercreated.event ``` ``` -------------------------------- ### ChannelDeprecationFilter Example Source: https://github.com/asyncapi/saunter/blob/main/_autodocs/api-reference/filters.md An example implementation of IChannelFilter demonstrating how to mark channels as deprecated or restrict server access based on channel names. ```APIDOC ### Usage Example ```csharp public class ChannelDeprecationFilter : IChannelFilter { public void Apply(AsyncApiChannel channel, ChannelFilterContext context) { // Mark channels with specific name patterns as deprecated if (context.Channel.Name.StartsWith("v1.")) { channel.Description = "⚠️ **Deprecated.** Use v2.* channels instead."; } // Add server restrictions for sensitive channels if (context.Channel.Name.Contains("payment")) { channel.Servers = new List { "production-only" }; } } } options.AddAsyncApiChannelFilter(); ``` ``` -------------------------------- ### Simple Publish Operation Source: https://github.com/asyncapi/saunter/blob/main/_autodocs/README.md Example of defining a simple publish operation using attributes. ```APIDOC ## Simple Publish ### Description Defines a method that publishes messages to a specific channel. ### Code Example ```csharp [Channel("events.created")] [PublishOperation(typeof(CreatedEvent))] public void OnCreated(CreatedEvent evt) { } ``` ``` -------------------------------- ### Accessing Multiple AsyncAPI Documents Source: https://github.com/asyncapi/saunter/blob/main/_autodocs/quick-reference.md Demonstrates how to access different AsyncAPI documents, including named APIs and the default document, via HTTP GET requests. ```shell # Named API: "Users" GET /asyncapi/Users/asyncapi.json GET /asyncapi/Users/ui/ # Named API: "Orders" GET /asyncapi/Orders/asyncapi.json GET /asyncapi/Orders/ui/ # Default document GET /asyncapi/asyncapi.json GET /asyncapi/ui/ ``` -------------------------------- ### Publish and Subscribe on Same Channel Source: https://github.com/asyncapi/saunter/blob/main/_autodocs/README.md Example of defining a method that handles both publishing and subscribing on the same channel. ```APIDOC ## Both on Same Channel ### Description Defines a method that can both publish and subscribe to messages on the same channel. ### Code Example ```csharp [Channel("events")] [PublishOperation(typeof(CreatedEvent))] [SubscribeOperation(typeof(ProcessedEvent))] public void OnEvent(object evt) { } ``` ``` -------------------------------- ### Implementing a Document Filter Source: https://github.com/asyncapi/saunter/blob/main/_autodocs/types.md Example of how to implement a custom document filter using DocumentFilterContext to access and process scanned AsyncAPI types. ```csharp public class MyDocumentFilter : IDocumentFilter { public void Apply(AsyncApiDocument document, DocumentFilterContext context) { // context.AsyncApiTypes contains all [AsyncApi] decorated types foreach (var type in context.AsyncApiTypes) { // Process each type } } } ``` -------------------------------- ### Implementing a Channel Filter Source: https://github.com/asyncapi/saunter/blob/main/_autodocs/types.md Example of implementing a custom channel filter using ChannelFilterContext to access the defining member and channel attribute for modifications. ```csharp public class MyChannelFilter : IChannelFilter { public void Apply(AsyncApiChannel channel, ChannelFilterContext context) { var method = (MethodInfo)context.Member; var channelName = context.Channel.Name; // Modify the channel based on its source } } ``` -------------------------------- ### Simple Subscribe Operation Source: https://github.com/asyncapi/saunter/blob/main/_autodocs/README.md Example of defining a simple subscribe operation using attributes. ```APIDOC ## Simple Subscribe ### Description Defines a method that subscribes to messages from a specific channel. ### Code Example ```csharp [Channel("user.registered")] [SubscribeOperation(typeof(UserRegisteredEvent))] public void OnUserRegistered(UserRegisteredEvent evt) { } ``` ``` -------------------------------- ### Map AsyncAPI Documents with Default Route Source: https://github.com/asyncapi/saunter/blob/main/_autodocs/api-reference/endpoint-route-builder-extensions.md Maps the AsyncAPI JSON document endpoint using the default route (`/asyncapi/asyncapi.json`). This is a common setup for basic integration. ```csharp app.UseEndpoints(endpoints => { endpoints.MapAsyncApiDocuments(); // Maps GET /asyncapi/asyncapi.json endpoints.MapAsyncApiUi(); endpoints.MapControllers(); }); ``` -------------------------------- ### Apply Custom Document Filter Source: https://github.com/asyncapi/saunter/blob/main/_autodocs/quick-reference.md Implement the IDocumentFilter interface to modify the entire AsyncAPI document. This example adds external documentation links to the document. ```csharp public class MyDocumentFilter : IDocumentFilter { public void Apply(AsyncApiDocument doc, DocumentFilterContext ctx) { doc.ExternalDocs = new AsyncApiExternalDocumentation() { Url = new("https://docs.example.com") }; } } options.AddDocumentFilter(); ``` -------------------------------- ### Map AsyncAPI UI with Default Route Source: https://github.com/asyncapi/saunter/blob/main/_autodocs/api-reference/endpoint-route-builder-extensions.md Maps the AsyncAPI UI endpoint using the default base route (`/asyncapi/ui/`). This setup includes serving embedded static files and handling wildcard routes for client-side routing. ```csharp app.UseEndpoints(endpoints => { endpoints.MapAsyncApiDocuments(); endpoints.MapAsyncApiUi(); // Maps GET /asyncapi/ui/* and redirects base route endpoints.MapControllers(); }); ``` -------------------------------- ### ExternalDocsFilter Implementation and Registration Source: https://github.com/asyncapi/saunter/blob/main/_autodocs/api-reference/filters.md An example implementation of IDocumentFilter that adds external documentation and tags to the AsyncAPI document. Register custom filters using options.AddDocumentFilter(). ```csharp public class ExternalDocsFilter : IDocumentFilter { public void Apply(AsyncApiDocument document, DocumentFilterContext context) { document.ExternalDocs = new AsyncApiExternalDocumentation() { Url = new("https://docs.example.com/events"), Description = "Full event documentation" }; // Add tags at document level document.Tags.Add(new AsyncApiTag() { Name = "Events", Description = "All event types" }); } } // Register options.AddDocumentFilter(); ``` -------------------------------- ### Configure Bindings in Startup.cs Source: https://github.com/asyncapi/saunter/blob/main/README.md Configure AMQP and HTTP bindings within the AsyncAPI document in your application's startup configuration. These bindings can then be referenced by operations and channels. ```csharp // Startup.cs services.AddAsyncApiSchemaGeneration(options => { options.AsyncApi = new AsyncApiDocument { Components = { ChannelBindings = { ["amqpDev"] = new() { new AMQPChannelBinding { Is = ChannelType.Queue, Exchange = new() { Name = "example-exchange", Vhost = "/development" } } } }, OperationBindings = { { "postBind", new() { new HttpOperationBinding { Method = "POST", Type = HttpOperationType.Response, } } } } } } }); ``` -------------------------------- ### Implement Custom Operation Filter Source: https://github.com/asyncapi/saunter/blob/main/_autodocs/api-reference/filters.md Example of a custom operation filter that adds OAuth2 security requirements and example values to an operation. Use this to enforce security or provide message examples. ```csharp public class OperationSecurityFilter : IOperationFilter { public void Apply(AsyncApiOperation operation, OperationFilterContext context) { // Add security requirements to all operations var securityRequirements = new Dictionary> { { "oauth2", new List { "read:events" } } }; operation.Security.Add(securityRequirements); // Add example values operation.Examples = new Dictionary { { "example1", new AsyncApiExample() { Summary = "A typical message", Value = "{ \"id\": 123 }" }} }; } } ``` ```csharp options.AddOperationFilter(); ``` -------------------------------- ### Map AsyncAPI Endpoints Source: https://github.com/asyncapi/saunter/blob/main/README.md In the `Configure` method of `Startup.cs`, map the endpoints for the AsyncAPI JSON document and the UI using `MapAsyncApiDocuments` and `MapAsyncApiUi`. ```csharp app.UseEndpoints(endpoints => { endpoints.MapAsyncApiDocuments(); endpoints.MapAsyncApiUi(); endpoints.MapControllers(); }); ``` -------------------------------- ### SubscribeOperationAttribute Usage Example Source: https://github.com/asyncapi/saunter/blob/main/_autodocs/api-reference/attributes.md Example of using SubscribeOperationAttribute to mark a method for subscribing to messages from a specific channel. ```csharp [Channel("user.registered")] [SubscribeOperation(typeof(UserRegisteredEvent), "Users")] public void OnUserRegistered(UserRegisteredEvent evt) { Console.WriteLine($"User {evt.UserId} registered"); } ``` -------------------------------- ### Map AsyncAPI Endpoints in Startup.cs Source: https://github.com/asyncapi/saunter/blob/main/_autodocs/README.md Map the endpoints for serving the AsyncAPI JSON document and the UI in your `Startup.Configure()` method. This makes the generated specification accessible via HTTP. ```csharp app.UseEndpoints(endpoints => { endpoints.MapAsyncApiDocuments(); endpoints.MapAsyncApiUi(); }); ``` -------------------------------- ### PublishOperationAttribute Usage Example Source: https://github.com/asyncapi/saunter/blob/main/_autodocs/api-reference/attributes.md Example of using PublishOperationAttribute to mark a method for publishing messages to a specific channel with associated tags. ```csharp [Channel("light.measured")] [PublishOperation(typeof(LightMeasuredEvent), "Sensors", "Lighting")] public void PublishLightMeasured(int streetlightId, int lumens) { // Implementation } ``` -------------------------------- ### Configure Saunter Basic Options Source: https://github.com/asyncapi/saunter/blob/main/_autodocs/quick-reference.md Set assembly marker types, and configure the OpenAPI schema and UI routes and title. ```csharp options.AssemblyMarkerTypes = new[] { typeof(MyType) }; options.Middleware.Route = "/api/schema.json"; options.Middleware.UiBaseRoute = "/api/docs/"; options.Middleware.UiTitle = "My API"; ``` -------------------------------- ### ChannelDeprecationFilter Example Source: https://github.com/asyncapi/saunter/blob/main/_autodocs/api-reference/filters.md An example implementation of `IChannelFilter` that marks channels with specific name patterns as deprecated and adds server restrictions for sensitive channels. This filter is registered using `options.AddAsyncApiChannelFilter();`. ```csharp public class ChannelDeprecationFilter : IChannelFilter { public void Apply(AsyncApiChannel channel, ChannelFilterContext context) { // Mark channels with specific name patterns as deprecated if (context.Channel.Name.StartsWith("v1.")) { channel.Description = "⚠️ **Deprecated.** Use v2.* channels instead."; } // Add server restrictions for sensitive channels if (context.Channel.Name.Contains("payment")) { channel.Servers = new List { "production-only" }; } } } options.AddAsyncApiChannelFilter(); ``` -------------------------------- ### Options and Configuration Source: https://github.com/asyncapi/saunter/blob/main/_autodocs/INDEX.md Details on configuration options for AsyncAPI, including general options and middleware options. ```APIDOC ## Options and Configuration ### Description Provides information about the configuration classes used to customize the behavior of AsyncAPI schema generation and middleware. ### Classes - `AsyncApiOptions`: Contains general configuration options for AsyncAPI (6 properties, 3 methods). - `AsyncApiMiddlewareOptions`: Contains configuration options specifically for the AsyncAPI middleware (3 properties). ``` -------------------------------- ### Operation with Tags Source: https://github.com/asyncapi/saunter/blob/main/_autodocs/README.md Example of defining an operation with additional tags for categorization. ```APIDOC ## With Tags ### Description Defines a publish operation with associated tags for better organization. ### Code Example ```csharp [Channel("orders.created")] [PublishOperation(typeof(OrderCreatedEvent), "Orders", "Critical")] public void OnOrderCreated(OrderCreatedEvent evt) { } ``` ``` -------------------------------- ### Local Package Creation Command Source: https://github.com/asyncapi/saunter/blob/main/CONTRIBUTING.md Use this command to create NuGet packages locally. This is useful for testing package generation before a formal release. ```bash dotnet pack ./src/Saunter/Saunter.csproj ``` -------------------------------- ### Configure Saunter Middleware and AsyncAPI Document Source: https://github.com/asyncapi/saunter/blob/main/_autodocs/README.md Set up middleware routes, UI paths, and define the AsyncAPI document details including info, servers, and protocols. This is a common configuration. ```csharp options.Middleware.Route = "/api/schema.json"; options.Middleware.UiBaseRoute = "/api/docs/"; options.Middleware.UiTitle = "My API"; options.AsyncApi = new AsyncApiDocument { Info = new AsyncApiInfo { Title = "My Service", Version = "1.0.0", Description = "Description" }, Servers = new Dictionary { ["kafka"] = new AsyncApiServer { Url = "kafka:9092", Protocol = "kafka" } } }; ``` -------------------------------- ### Operation with Custom Channel Name Resolution Source: https://github.com/asyncapi/saunter/blob/main/_autodocs/README.md Example of defining an operation that uses a custom resolver for channel names. ```APIDOC ## With Custom Channel Name Resolution ### Description Defines a publish operation that uses a custom channel name resolver. ### Code Example ```csharp [Channel(typeof(MyResolver), typeof(MyEvent))] [PublishOperation(typeof(MyEvent))] public void OnMyEvent(MyEvent evt) { } ``` ``` -------------------------------- ### Configure Saunter Services Source: https://github.com/asyncapi/saunter/blob/main/README.md Configure Saunter in the `ConfigureServices` method of `Startup.cs`. Specify assemblies to scan and optionally provide initial AsyncAPI document information. ```csharp // Add Saunter to the application services. services.AddAsyncApiSchemaGeneration(options => { // Specify example type(s) from assemblies to scan. options.AssemblyMarkerTypes = new[] { typeof(StreetlightMessageBus) }; // Build as much (or as little) of the AsyncApi document as you like. // Saunter will generate Channels, Operations, Messages, etc, but you // may want to specify Info here. options.Middleware.UiTitle = "Streetlights API"; options.AsyncApi = new AsyncApiDocument { Info = new AsyncApiInfo() { Title = "Streetlights API", Version = "1.0.0", Description = "The Smartylighting Streetlights API allows you to remotely manage the city lights.", License = new AsyncApiLicense() { Name = "Apache 2.0", Url = new("https://www.apache.org/licenses/LICENSE-2.0"), } }, Servers = { ["mosquitto"] = new AsyncApiServer(){ Url = "test.mosquitto.org", Protocol = "mqtt"}, ["webapi"] = new AsyncApiServer(){ Url = "localhost:5000", Protocol = "http"}, }, }; }); ``` -------------------------------- ### Get Streetlights Data Source: https://github.com/asyncapi/saunter/blob/main/examples/StreetlightsAPI/README.md Retrieves a list of streetlights using a curl-like command. This demonstrates how to fetch data from the API. ```powershell Invoke-WebRequest 'http://localhost:5000/api/streetlights' | ConvertFrom-Json ``` -------------------------------- ### Local Build and Test Command Source: https://github.com/asyncapi/saunter/blob/main/CONTRIBUTING.md Run this command locally to build the project and execute tests. Ensure all tests pass before submitting changes. ```bash dotnet build && dotnet test ``` -------------------------------- ### AsyncApiMiddlewareOptions Source: https://github.com/asyncapi/saunter/blob/main/_autodocs/public-api-surface.md Configuration for AsyncAPI HTTP endpoint and UI. ```APIDOC ## Class: AsyncApiMiddlewareOptions ### Description HTTP endpoint and UI configuration for AsyncAPI. ### Properties - `Route` (string) - Default: `/asyncapi/asyncapi.json` - `UiBaseRoute` (string) - Default: `/asyncapi/ui/` - `UiTitle` (string) - Default: `AsyncAPI` ``` -------------------------------- ### Configure AsyncAPI Servers Source: https://github.com/asyncapi/saunter/blob/main/_autodocs/quick-reference.md Define the servers for the AsyncAPI document, specifying their URLs, protocols, and descriptions. ```csharp options.AsyncApi.Servers = new Dictionary { ["kafka"] = new AsyncApiServer { Url = "kafka.example.com:9092", Protocol = "kafka", Description = "Kafka cluster" }, ["mqtt"] = new AsyncApiServer { Url = "mqtt.example.com:1883", Protocol = "mqtt", Description = "MQTT broker" } }; ``` -------------------------------- ### Get AsyncAPI Schema Source: https://github.com/asyncapi/saunter/blob/main/examples/StreetlightsAPI/README.md Retrieves the AsyncAPI schema document for the Streetlights API. Note that schemas are automatically moved to the `components` section. ```APIDOC ## GET /asyncapi/asyncapi.json ### Description Retrieves the AsyncAPI schema document for the Streetlights API. Schemas are automatically moved to the `components` section. ### Method GET ### Endpoint /asyncapi/asyncapi.json ### Response #### Success Response (200) - **asyncapi** (string) - The AsyncAPI version. - **info** (object) - General information about the API. - **title** (string) - The title of the API. - **version** (string) - The version of the API. - **description** (string) - A description of the API. - **license** (object) - Information about the API's license. - **name** (string) - The name of the license. - **url** (string) - The URL to the license. - **servers** (object) - Information about the available servers. - **[server_name]** (object) - Details of a specific server. - **url** (string) - The URL of the server. - **protocol** (string) - The protocol used by the server. - **defaultContentType** (string) - The default content type for messages. - **channels** (object) - Definitions of the available channels. - **[channel_name]** (object) - Details of a specific channel. - **servers** (array) - List of servers available for this channel. - **publish** (object) - Operation for publishing messages. - **operationId** (string) - Unique identifier for the publish operation. - **summary** (string) - A brief summary of the publish operation. - **tags** (array) - Tags associated with the operation. - **bindings** (object) - Bindings for the publish operation. - **message** (object) - The message definition for the publish operation. - **subscribe** (object) - Operation for subscribing to messages. - **operationId** (string) - Unique identifier for the subscribe operation. - **summary** (string) - A brief summary of the subscribe operation. - **tags** (array) - Tags associated with the operation. - **message** (object) - The message definition for the subscribe operation. - **bindings** (object) - Bindings for the subscribe operation. - **components** (object) - Reusable components like schemas, messages, and bindings. - **schemas** (object) - Definitions of data schemas. - **[schema_name]** (object) - A specific schema definition. - **title** (string) - The title of the schema. - **type** (string) - The data type of the schema. - **properties** (object) - Properties of the schema. - **nullable** (boolean) - Indicates if the schema can be null. - **messages** (object) - Definitions of messages. - **[message_name]** (object) - A specific message definition. - **payload** (object) - The payload of the message. - **name** (string) - The name of the message. - **title** (string) - The title of the message. - **channelBindings** (object) - Definitions of channel bindings. - **[binding_name]** (object) - A specific channel binding definition. - **operationBindings** (object) - Definitions of operation bindings. - **[binding_name]** (object) - A specific operation binding definition. ### Request Example ```powershell Invoke-WebRequest -Method GET -Uri 'http://localhost:5000/asyncapi/asyncapi.json' ``` ``` -------------------------------- ### Configure AsyncAPI Document Info Source: https://github.com/asyncapi/saunter/blob/main/_autodocs/quick-reference.md Define the AsyncAPI document information, including title, version, description, contact details, and license. ```csharp options.AsyncApi = new AsyncApiDocument { Info = new AsyncApiInfo { Title = "Service", Version = "1.0.0", Description = "Service description", Contact = new AsyncApiContact { Name = "Support", Email = "support@example.com", Url = new("https://example.com/support") }, License = new AsyncApiLicense { Name = "MIT", Url = new("https://opensource.org/licenses/MIT") } } }; ``` -------------------------------- ### Apply Custom Operation Filter Source: https://github.com/asyncapi/saunter/blob/main/_autodocs/quick-reference.md Implement the IOperationFilter interface to modify operations. This example adds OAuth2 security scheme information to operations. ```csharp public class MyOperationFilter : IOperationFilter { public void Apply(AsyncApiOperation operation, OperationFilterContext ctx) { operation.Security.Add(new Dictionary> { { "oauth2", new List { "read:events" } } }); } } options.AddOperationFilter(); ``` -------------------------------- ### Implement Custom AsyncApiDocumentProvider Source: https://github.com/asyncapi/saunter/blob/main/_autodocs/api-reference/document-provider.md Provide your own document provider by implementing the IAsyncApiDocumentProvider interface. This allows for custom logic, such as loading documents from files instead of attributes. Register your custom provider using AddScoped. ```csharp public class CustomAsyncApiDocumentProvider : IAsyncApiDocumentProvider { public AsyncApiDocument GetDocument(string? documentName, AsyncApiOptions options) { // Custom logic to generate the document // For example, load from a YAML/JSON file instead of attributes var doc = new AsyncApiDocument(); // Populate doc... return doc; } } // Replace the default implementation services.AddAsyncApiSchemaGeneration(options => { options.AssemblyMarkerTypes = new[] { typeof(Startup) }; }); // Override with custom provider services.AddScoped(); ``` -------------------------------- ### Configure AsyncAPI Schema Generation Source: https://github.com/asyncapi/saunter/blob/main/_autodocs/quick-reference.md Configure schema generation services and define multiple AsyncAPI documents with custom info and servers. Use AssemblyMarkerTypes to specify assemblies to scan for AsyncAPI attributes. ```csharp services.AddAsyncApiSchemaGeneration(options => { options.AssemblyMarkerTypes = new[] { typeof(Startup) }; }); services.ConfigureNamedAsyncApi("Users", doc => { doc.Info = new AsyncApiInfo("Users API", "1.0.0"); doc.Servers["kafka"] = new AsyncApiServer() { Url = "kafka:9092", Protocol = "kafka" }; }); services.ConfigureNamedAsyncApi("Orders", doc => { doc.Info = new AsyncApiInfo("Orders API", "2.0.0"); doc.Servers["kafka"] = new AsyncApiServer() { Url = "kafka:9092", Protocol = "kafka" }; }); ``` -------------------------------- ### Configure Custom HTTP Routes Source: https://github.com/asyncapi/saunter/blob/main/_autodocs/README.md Customize the default routes for AsyncAPI JSON documents and the UI by configuring AsyncApiMiddlewareOptions. This allows for flexible endpoint naming and organization. ```csharp options.Middleware.Route = "/custom/path.json"; options.Middleware.UiBaseRoute = "/custom/ui/"; ``` -------------------------------- ### AsyncAPI UI Assets Source: https://github.com/asyncapi/saunter/blob/main/_autodocs/quick-reference.md Serves static assets for the AsyncAPI UI. ```APIDOC ## GET /asyncapi/ui/assets/* ### Description Serves various static assets (CSS, JS, fonts) required by the AsyncAPI UI. ### Method GET ### Endpoint /asyncapi/ui/assets/* ``` -------------------------------- ### Generate Schema for a Simple Type Source: https://github.com/asyncapi/saunter/blob/main/_autodocs/api-reference/schema-generator.md Demonstrates generating a schema for a simple C# class and shows the expected JSON output for the root schema and all referenced schemas. ```csharp public class UserCreatedEvent { public int UserId { get; set; } public string Email { get; set; } public DateTime CreatedAt { get; set; } } var result = schemaGenerator.Generate(typeof(UserCreatedEvent)); // result.Value.Root generates: // { // "title": "UserCreatedEvent", // "type": "object", // "properties": { // "userId": { "type": "integer", "format": "int32" }, // "email": { "type": "string" }, // "createdAt": { "type": "string", "format": "date-time" } // } // } // result.Value.All contains: // - UserCreatedEvent schema // - int32 schema // - string schema // - date-time schema ``` -------------------------------- ### Configure Multiple AsyncAPI Documents Source: https://github.com/asyncapi/saunter/blob/main/README.md Configure multiple, named AsyncAPI documents using `ConfigureNamedAsyncApi` in your application's startup. This allows for distinct API specifications within the same application. ```csharp // Startup.cs // Add Saunter to the application services. services.AddAsyncApiSchemaGeneration(options => { // Specify example type(s) from assemblies to scan. options.AssemblyMarkerTypes = new[] {typeof(FooMessageBus)}; } // Configure one or more named AsyncAPI documents services.ConfigureNamedAsyncApi("Foo", asyncApi => { asyncApi.Info = new Info("Foo API", "1.0.0"); // ... }); services.ConfigureNamedAsyncApi("Bar", asyncApi => { asyncApi.Info = new Info("Bar API", "1.0.0"); // ... }); ``` -------------------------------- ### Configure Custom AsyncAPI Routes Source: https://github.com/asyncapi/saunter/blob/main/_autodocs/quick-reference.md Customize the routes for AsyncAPI documents and UI by setting `Middleware.Route` and `Middleware.UiBaseRoute` in the options before mapping endpoints. ```csharp options.Middleware.Route = "/custom/path.json"; options.Middleware.UiBaseRoute = "/custom/ui/"; // Later in Configure: app.UseEndpoints(endpoints => { endpoints.MapAsyncApiDocuments(); endpoints.MapAsyncApiUi(); }); ``` -------------------------------- ### Apply Custom Channel Filter Source: https://github.com/asyncapi/saunter/blob/main/_autodocs/quick-reference.md Implement the IChannelFilter interface to modify individual channels. This filter adds a description to channels whose names start with 'deprecated.'. ```csharp public class MyChannelFilter : IChannelFilter { public void Apply(AsyncApiChannel channel, ChannelFilterContext ctx) { if (ctx.Channel.Name.StartsWith("deprecated.")) { channel.Description = "⚠️ Deprecated"; } } } options.AddAsyncApiChannelFilter(); ``` -------------------------------- ### Accessing Multiple Documents Source: https://github.com/asyncapi/saunter/blob/main/_autodocs/quick-reference.md Demonstrates how to access multiple AsyncAPI documents, including named APIs. ```APIDOC ## Named API: "Users" ### Description Accesses the AsyncAPI document and UI for the 'Users' named API. ### Method GET ### Endpoint /asyncapi/Users/asyncapi.json ### Method GET ### Endpoint /asyncapi/Users/ui/ ``` ```APIDOC ## Named API: "Orders" ### Description Accesses the AsyncAPI document and UI for the 'Orders' named API. ### Method GET ### Endpoint /asyncapi/Orders/asyncapi.json ### Method GET ### Endpoint /asyncapi/Orders/ui/ ``` -------------------------------- ### Register Saunter Services Source: https://github.com/asyncapi/saunter/blob/main/_autodocs/architecture.md Register Saunter's services with dependency injection during application startup. Configure options like the assembly marker types to scan. ```csharp services.AddAsyncApiSchemaGeneration(options => { options.AssemblyMarkerTypes = new[] { typeof(MyClass) }; // ... }); ``` -------------------------------- ### MessageAttribute Usage Example Source: https://github.com/asyncapi/saunter/blob/main/_autodocs/api-reference/attributes.md Demonstrates how to apply the MessageAttribute to a method to define a 'PublishOperation' for an 'OrderCreated' event, specifying payload, headers, title, summary, description, message ID, and tags. ```csharp [Channel("orders.created")] [PublishOperation(typeof(OrderCreated))] [Message(typeof(OrderCreated), "Orders", Title = "Order Created Event", Summary = "Sent when a new order is created", Description = "Contains complete order details including items and shipping address", MessageId = "order.created.v1", HeadersType = typeof(MessageHeaders))] public void OnOrderCreated(OrderCreated order) { } public class MessageHeaders { public string CorrelationId { get; set; } public string TraceId { get; set; } } ``` -------------------------------- ### Run Reverse Proxy Integration Tests Source: https://github.com/asyncapi/saunter/blob/main/test/Saunter.IntegrationTests.ReverseProxy/README.md Execute the integration tests for Saunter behind a reverse proxy using Docker Compose. Ensure you are in the root project directory before running. ```bash docker-compose --file ./test/Saunter.IntegrationTests.ReverseProxy/docker-compose.yml up --build ``` -------------------------------- ### MapAsyncApiDocuments Source: https://github.com/asyncapi/saunter/blob/main/_autodocs/api-reference/endpoint-route-builder-extensions.md Maps the HTTP endpoint that serves the generated AsyncAPI JSON document. This method registers a GET endpoint for the AsyncAPI document, with configurable routes for default and named APIs. ```APIDOC ## MapAsyncApiDocuments ### Description Maps the HTTP endpoint that serves the generated AsyncAPI JSON document. ### Method GET ### Endpoint `/asyncapi/asyncapi.json` (default) or `/asyncapi/{document}/asyncapi.json` for named APIs. ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Response #### Success Response (200) - **content** (string) - The AsyncAPI document serialized as JSON (AsyncAPI 2.0 format). - **contentType** (string) - `application/json` ### Request Example ```http GET /asyncapi/asyncapi.json ``` ### Response Example ```json { "asyncapi": "2.0.0", "info": { "title": "My API", "version": "1.0.0" }, "channels": {} } ``` ``` -------------------------------- ### MapAsyncApiUi Source: https://github.com/asyncapi/saunter/blob/main/_autodocs/api-reference/endpoint-route-builder-extensions.md Maps the HTTP endpoints that serve the AsyncAPI UI (ReDoc/SwaggerUI style interface). This method registers GET endpoints for serving the UI assets and handles routing for a user-friendly interface. ```APIDOC ## MapAsyncApiUi ### Description Maps the HTTP endpoints that serve the AsyncAPI UI (ReDoc/SwaggerUI style interface). ### Method GET ### Endpoint `/asyncapi/ui/` (default) or `/asyncapi/{document}/ui/` for named APIs. This route also handles wildcard paths for serving assets and the index.html. ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Response #### Success Response (200) - **content** (string) - HTML, CSS, JavaScript files for the AsyncAPI UI. ### Request Example ```http GET /asyncapi/ui/ GET /asyncapi/ui/index.html GET /asyncapi/ui/assets/main.js ``` ### Response Example (Serves UI assets, typically HTML, CSS, and JavaScript files) ``` -------------------------------- ### Saunter Project File Structure Source: https://github.com/asyncapi/saunter/blob/main/_autodocs/INDEX.md Overview of the directory structure for the Saunter project documentation. Includes line counts for each file. ```text output/ ├── README.md (550 lines) - Main overview & getting started ├── INDEX.md (this file) - Documentation index ├── quick-reference.md (534 lines) - Common patterns & examples ├── public-api-surface.md (473 lines) - Complete API inventory ├── architecture.md (528 lines) - System design & architecture ├── configuration.md (400 lines) - Configuration options ├── types.md (321 lines) - Types & enumerations └── api-reference/ (2,024 lines total) ├── service-collection-extensions.md (149 lines) - DI registration methods ├── endpoint-route-builder-extensions.md (172 lines) - Endpoint mapping methods ├── attributes.md (330 lines) - All AsyncAPI attributes ├── async-api-options.md (372 lines) - Configuration class reference ├── filters.md (287 lines) - Filter interfaces & usage ├── channel-resolver.md (260 lines) - Dynamic channel naming ├── document-provider.md (202 lines) - Document generation └── schema-generator.md (252 lines) - JSON schema generation ``` -------------------------------- ### Define Message Schema in C# Source: https://github.com/asyncapi/saunter/blob/main/_autodocs/README.md Define a C# class to represent the schema of messages exchanged. The library automatically generates the message schema from these types. This example defines a `UserCreatedEvent` with `UserId` and `Email` properties. ```csharp public class UserCreatedEvent { public int UserId { get; set; } public string Email { get; set; } } ``` -------------------------------- ### Environment-Specific Configuration Source: https://github.com/asyncapi/saunter/blob/main/_autodocs/configuration.md Set up AsyncAPI generation services, customizing middleware routes and UI titles based on the ASP.NET Core environment. Ensure 'ASPNETCORE_ENVIRONMENT' variable is set. ```csharp public void ConfigureServices(IServiceCollection services) { var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"); services.AddAsyncApiSchemaGeneration(options => { options.AssemblyMarkerTypes = new[] { typeof(Startup) }; if (environment == "Production") { options.Middleware.Route = "/api/schema/asyncapi.json"; options.Middleware.UiTitle = "Production Events"; } else { options.Middleware.Route = "/asyncapi/dev.json"; options.Middleware.UiTitle = $"[{environment}] Events"; } }); } ``` -------------------------------- ### Convention-Based Channel Name Resolver Implementation Source: https://github.com/asyncapi/saunter/blob/main/_autodocs/api-reference/channel-resolver.md An example implementation of IChannelResolver that generates channel names based on the message type's name using a convention. It removes 'Event' suffixes and formats the name. ```csharp public class ConventionChannelResolver : IChannelResolver { private readonly Type _messageType; public ConventionChannelResolver(Type messageType) { _messageType = messageType; } public string ResolveChannelName() { // Generate channel name from message type name // UserCreatedEvent -> user.created var name = _messageType.Name .Replace("Event", "") .Insert(0, _messageType.Name[0].ToString().ToLower()) .Substring(1); return name.ToLower() + ".event"; } } // Usage [Channel(typeof(ConventionChannelResolver), typeof(UserCreatedEvent))] [PublishOperation(typeof(UserCreatedEvent))] public void OnUserCreated(UserCreatedEvent evt) { } // Generates channel: usercreated.event ``` -------------------------------- ### Configuration Classes Source: https://github.com/asyncapi/saunter/blob/main/_autodocs/MANIFEST.txt Configuration classes allow customization of AsyncAPI generation behavior and middleware options. ```APIDOC ## Configuration Classes ### Description Classes used for configuring the behavior of AsyncAPI generation and middleware. ### Classes - **AsyncApiOptions** - Description: Options for configuring the AsyncAPI document generation process. - **AsyncApiMiddlewareOptions** - Description: Options for configuring the AsyncAPI middleware. ``` -------------------------------- ### Decorate Message Bus Class with AsyncApi Attribute Source: https://github.com/asyncapi/saunter/blob/main/_autodocs/README.md Apply the `[AsyncApi]` attribute to your message bus class to indicate that it should be scanned for AsyncAPI definitions. This example decorates `MyMessageBus` and defines a publish operation for `ProcessedEvent` on the 'events.processed' channel. ```csharp [AsyncApi] public class MyMessageBus { [Channel("events.processed")] [PublishOperation(typeof(ProcessedEvent))] public void OnProcessed(ProcessedEvent evt) { } } ``` -------------------------------- ### Configuration Classes Source: https://github.com/asyncapi/saunter/blob/main/_autodocs/README.md Classes used for configuring the AsyncAPI generation process. ```APIDOC ## Configuration Classes ### AsyncApiOptions - **Description**: Main configuration class for AsyncAPI generation. ### AsyncApiMiddlewareOptions - **Description**: Configuration class for HTTP-related options for the AsyncAPI middleware. ``` -------------------------------- ### AsyncApiMiddlewareOptions Source: https://github.com/asyncapi/saunter/blob/main/_autodocs/types.md Configuration class for HTTP endpoints related to AsyncAPI documentation. It allows customization of the route for the AsyncAPI JSON document, the base route for the UI, and the UI's title. ```APIDOC ## Class: AsyncApiMiddlewareOptions ### Description Configuration for HTTP endpoints. Allows customization of the route where the AsyncAPI JSON document is served, the base route for the AsyncAPI UI, and the title displayed in the browser tab and UI page. ### Properties #### Route - **Type**: string - **Default**: `/asyncapi/asyncapi.json` - **Description**: HTTP path where the AsyncAPI JSON document is served. #### UiBaseRoute - **Type**: string - **Default**: `/asyncapi/ui/` - **Description**: HTTP base path where the AsyncAPI UI is served. #### UiTitle - **Type**: string - **Default**: `AsyncAPI` - **Description**: Browser tab title and UI page title. ``` -------------------------------- ### Add AsyncAPI Document and Channel Filters in C# Source: https://github.com/asyncapi/saunter/blob/main/_autodocs/README.md Customize the generated AsyncAPI document by adding filters. Use `AddDocumentFilter` for general document modifications and `AddAsyncApiChannelFilter` for channel-specific adjustments. This example demonstrates adding custom filters to the options. ```csharp options.AddDocumentFilter(); options.AddAsyncApiChannelFilter(); options.AddOperationFilter(); ```