- Implemented comprehensive unit tests for RabbitMqTransportServer, covering constructor, disposal, connection management, event handlers, and exception handling. - Added configuration tests for RabbitMqTransportServer to validate SSL, durable queues, auto-recovery, and custom virtual host options. - Created unit tests for UdpFrameProtocol, including frame parsing and serialization, header size validation, and round-trip data preservation. - Developed tests for UdpTransportClient, focusing on connection handling, event subscriptions, and exception scenarios. - Established tests for UdpTransportServer, ensuring proper start/stop behavior, connection state management, and event handling. - Included tests for UdpTransportOptions to verify default values and modification capabilities. - Enhanced service registration tests for Udp transport services in the dependency injection container.
12 KiB
Gateway OpenAPI Implementation
This document describes the implementation architecture of OpenAPI document aggregation in the StellaOps Router Gateway.
Architecture
The Gateway generates OpenAPI 3.1.0 documentation by aggregating schemas and endpoint metadata from connected microservices.
Component Overview
┌─────────────────────────────────────────────────────────────────────┐
│ Gateway │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────┐ ┌────────────────────┐ │
│ │ ConnectionManager │───►│ InMemoryRoutingState│ │
│ │ │ │ │ │
│ │ - OnHelloReceived │ │ - Connections[] │ │
│ │ - OnConnClosed │ │ - Endpoints │ │
│ └──────────────────┘ │ - Schemas │ │
│ │ └─────────┬──────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌──────────────────┐ ┌────────────────────┐ │
│ │ OpenApiDocument │◄───│ GatewayOpenApi │ │
│ │ Cache │ │ DocumentCache │ │
│ │ │ │ │ │
│ │ - Invalidate() │ │ - TTL expiration │ │
│ └──────────────────┘ │ - ETag generation │ │
│ └─────────┬──────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────┐ │
│ │ OpenApiDocument │ │
│ │ Generator │ │
│ │ │ │
│ │ - GenerateInfo() │ │
│ │ - GeneratePaths() │ │
│ │ - GenerateTags() │ │
│ │ - GenerateSchemas()│ │
│ └─────────┬──────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────┐ │
│ │ ClaimSecurity │ │
│ │ Mapper │ │
│ │ │ │
│ │ - SecuritySchemes │ │
│ │ - SecurityRequire │ │
│ └────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
Components
| Component | File | Responsibility |
|---|---|---|
IOpenApiDocumentGenerator |
OpenApi/IOpenApiDocumentGenerator.cs |
Interface for document generation |
OpenApiDocumentGenerator |
OpenApi/OpenApiDocumentGenerator.cs |
Builds OpenAPI 3.1.0 JSON |
IGatewayOpenApiDocumentCache |
OpenApi/IGatewayOpenApiDocumentCache.cs |
Interface for document caching |
GatewayOpenApiDocumentCache |
OpenApi/GatewayOpenApiDocumentCache.cs |
TTL + invalidation caching |
ClaimSecurityMapper |
OpenApi/ClaimSecurityMapper.cs |
Maps claims to OAuth2 scopes |
OpenApiEndpoints |
OpenApi/OpenApiEndpoints.cs |
HTTP endpoint handlers |
OpenApiAggregationOptions |
OpenApi/OpenApiAggregationOptions.cs |
Configuration options |
OpenApiDocumentGenerator
Generates the complete OpenAPI 3.1.0 document from routing state.
Process Flow
- Collect connections from
IGlobalRoutingState - Generate info section from
OpenApiAggregationOptions - Generate paths by iterating all endpoints across connections
- Generate components including schemas and security schemes
- Generate tags from unique service names
Schema Handling
Schemas are prefixed with service name to avoid naming conflicts:
var prefixedId = $"{conn.Instance.ServiceName}_{schemaId}";
// billing_CreateInvoiceRequest
Operation ID Generation
Operation IDs follow a consistent pattern:
var operationId = $"{serviceName}_{path}_{method}";
// billing_invoices_POST
GatewayOpenApiDocumentCache
Implements caching with TTL expiration and content-based ETags.
Cache Behavior
| Trigger | Action |
|---|---|
| First request | Generate and cache document |
| Subsequent requests (within TTL) | Return cached document |
| TTL expired | Regenerate document |
| Connection added/removed | Invalidate cache |
ETag Generation
ETags are computed from SHA256 hash of document content:
var hash = SHA256.HashData(Encoding.UTF8.GetBytes(documentJson));
var etag = $"\"{Convert.ToHexString(hash)[..16]}\"";
Thread Safety
The cache uses locking to ensure thread-safe regeneration:
lock (_lock)
{
if (_cachedDocument is null || IsExpired())
{
RegenerateDocument();
}
}
ClaimSecurityMapper
Maps endpoint claim requirements to OpenAPI security schemes.
Security Scheme Generation
Always generates BearerAuth scheme. Generates OAuth2 scheme only when endpoints have claim requirements:
public static JsonObject GenerateSecuritySchemes(
IEnumerable<EndpointDescriptor> endpoints,
string tokenUrl)
{
var schemes = new JsonObject();
// Always add BearerAuth
schemes["BearerAuth"] = new JsonObject { ... };
// Collect scopes from all endpoints
var scopes = CollectScopes(endpoints);
// Add OAuth2 only if scopes exist
if (scopes.Count > 0)
{
schemes["OAuth2"] = GenerateOAuth2Scheme(tokenUrl, scopes);
}
return schemes;
}
Per-Operation Security
Each endpoint with claims gets a security requirement:
public static JsonArray GenerateSecurityRequirement(EndpointDescriptor endpoint)
{
if (endpoint.RequiringClaims.Count == 0)
return new JsonArray(); // No security required
return new JsonArray
{
new JsonObject
{
["BearerAuth"] = new JsonArray(),
["OAuth2"] = new JsonArray(claims.Select(c => c.Type))
}
};
}
Configuration Reference
OpenApiAggregationOptions
| Property | Type | Default | Description |
|---|---|---|---|
Title |
string |
"StellaOps Gateway API" |
API title |
Description |
string |
"Unified API..." |
API description |
Version |
string |
"1.0.0" |
API version |
ServerUrl |
string |
"/" |
Base server URL |
CacheTtlSeconds |
int |
60 |
Cache TTL |
Enabled |
bool |
true |
Enable/disable |
LicenseName |
string |
"AGPL-3.0-or-later" |
License name |
ContactName |
string? |
null |
Contact name |
ContactEmail |
string? |
null |
Contact email |
TokenUrl |
string |
"/auth/token" |
OAuth2 token URL |
YAML Configuration
OpenApi:
Title: "My Gateway API"
Description: "Unified API for all microservices"
Version: "2.0.0"
ServerUrl: "https://api.example.com"
CacheTtlSeconds: 60
Enabled: true
LicenseName: "AGPL-3.0-or-later"
ContactName: "API Team"
ContactEmail: "api@example.com"
TokenUrl: "/auth/token"
Service Registration
Services are registered via dependency injection in ServiceCollectionExtensions:
services.Configure<OpenApiAggregationOptions>(
configuration.GetSection("OpenApi"));
services.AddSingleton<IOpenApiDocumentGenerator, OpenApiDocumentGenerator>();
services.AddSingleton<IGatewayOpenApiDocumentCache, GatewayOpenApiDocumentCache>();
Endpoints are mapped in ApplicationBuilderExtensions:
app.MapGatewayOpenApiEndpoints();
Cache Invalidation
The ConnectionManager invalidates the cache on connection changes:
private Task HandleHelloReceivedAsync(ConnectionState state, HelloPayload payload)
{
_routingState.AddConnection(state);
_openApiCache?.Invalidate(); // Invalidate on new connection
return Task.CompletedTask;
}
private Task HandleConnectionClosedAsync(string connectionId)
{
_routingState.RemoveConnection(connectionId);
_openApiCache?.Invalidate(); // Invalidate on disconnect
return Task.CompletedTask;
}
Extension Points
Custom Routing Plugins
The Gateway supports custom routing plugins via IRoutingPlugin. While not directly related to OpenAPI, routing decisions can affect which endpoints are exposed.
Future Enhancements
Potential extension points for future development:
- Schema Transformers: Modify schemas before aggregation
- Tag Customization: Custom tag generation logic
- Response Examples: Include example responses from connected services
- Webhooks: Notify external systems on document changes
Testing
Unit tests are located in src/Gateway/__Tests/StellaOps.Gateway.WebService.Tests/OpenApi/:
| Test File | Coverage |
|---|---|
OpenApiDocumentGeneratorTests.cs |
Document structure, schema merging, tag generation |
GatewayOpenApiDocumentCacheTests.cs |
TTL expiry, invalidation, ETag consistency |
ClaimSecurityMapperTests.cs |
Security scheme generation from claims |
Test Patterns
[Fact]
public void GenerateDocument_WithConnections_GeneratesPaths()
{
// Arrange
var endpoint = new EndpointDescriptor { ... };
var connection = CreateConnection("inventory", "1.0.0", endpoint);
_routingState.Setup(x => x.GetAllConnections()).Returns([connection]);
// Act
var document = _sut.GenerateDocument();
// Assert
var doc = JsonDocument.Parse(document);
doc.RootElement.GetProperty("paths")
.TryGetProperty("/api/items", out _)
.Should().BeTrue();
}
See Also
- Schema Validation - JSON Schema validation in microservices
- OpenAPI Aggregation - Configuration and usage guide
- API Overview - General API conventions