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.
326 lines
12 KiB
Markdown
326 lines
12 KiB
Markdown
# 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
|