Add unit tests for RabbitMq and Udp transport servers and clients
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
- 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.
This commit is contained in:
325
docs/modules/gateway/openapi.md
Normal file
325
docs/modules/gateway/openapi.md
Normal file
@@ -0,0 +1,325 @@
|
||||
# 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
|
||||
|
||||
1. **Collect connections** from `IGlobalRoutingState`
|
||||
2. **Generate info** section from `OpenApiAggregationOptions`
|
||||
3. **Generate paths** by iterating all endpoints across connections
|
||||
4. **Generate components** including schemas and security schemes
|
||||
5. **Generate tags** from unique service names
|
||||
|
||||
### Schema Handling
|
||||
|
||||
Schemas are prefixed with service name to avoid naming conflicts:
|
||||
|
||||
```csharp
|
||||
var prefixedId = $"{conn.Instance.ServiceName}_{schemaId}";
|
||||
// billing_CreateInvoiceRequest
|
||||
```
|
||||
|
||||
### Operation ID Generation
|
||||
|
||||
Operation IDs follow a consistent pattern:
|
||||
|
||||
```csharp
|
||||
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:
|
||||
|
||||
```csharp
|
||||
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:
|
||||
|
||||
```csharp
|
||||
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:
|
||||
|
||||
```csharp
|
||||
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:
|
||||
|
||||
```csharp
|
||||
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
|
||||
|
||||
```yaml
|
||||
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`:
|
||||
|
||||
```csharp
|
||||
services.Configure<OpenApiAggregationOptions>(
|
||||
configuration.GetSection("OpenApi"));
|
||||
services.AddSingleton<IOpenApiDocumentGenerator, OpenApiDocumentGenerator>();
|
||||
services.AddSingleton<IGatewayOpenApiDocumentCache, GatewayOpenApiDocumentCache>();
|
||||
```
|
||||
|
||||
Endpoints are mapped in `ApplicationBuilderExtensions`:
|
||||
|
||||
```csharp
|
||||
app.MapGatewayOpenApiEndpoints();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Cache Invalidation
|
||||
|
||||
The `ConnectionManager` invalidates the cache on connection changes:
|
||||
|
||||
```csharp
|
||||
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
|
||||
|
||||
```csharp
|
||||
[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](../router/schema-validation.md) - JSON Schema validation in microservices
|
||||
- [OpenAPI Aggregation](../router/openapi-aggregation.md) - Configuration and usage guide
|
||||
- [API Overview](../../api/overview.md) - General API conventions
|
||||
Reference in New Issue
Block a user