Add unit tests for Router configuration and transport layers
- Implemented tests for RouterConfig, RoutingOptions, StaticInstanceConfig, and RouterConfigOptions to ensure default values are set correctly. - Added tests for RouterConfigProvider to validate configurations and ensure defaults are returned when no file is specified. - Created tests for ConfigValidationResult to check success and error scenarios. - Developed tests for ServiceCollectionExtensions to verify service registration for RouterConfig. - Introduced UdpTransportTests to validate serialization, connection, request-response, and error handling in UDP transport. - Added scripts for signing authority gaps and hashing DevPortal SDK snippets.
This commit is contained in:
297
examples/router/README.md
Normal file
297
examples/router/README.md
Normal file
@@ -0,0 +1,297 @@
|
||||
# StellaOps Router Example
|
||||
|
||||
This example demonstrates the StellaOps Router, Gateway, and Microservice SDK working together.
|
||||
|
||||
## Overview
|
||||
|
||||
The example includes:
|
||||
|
||||
- **Examples.Gateway** - HTTP gateway that routes requests to microservices
|
||||
- **Examples.Billing.Microservice** - Sample billing service with typed and streaming endpoints
|
||||
- **Examples.Inventory.Microservice** - Sample inventory service demonstrating multi-service routing
|
||||
- **Examples.Integration.Tests** - End-to-end integration tests
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- .NET 10 SDK
|
||||
- Docker and Docker Compose (for containerized deployment)
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
examples/router/
|
||||
├── Examples.Router.sln
|
||||
├── docker-compose.yaml
|
||||
├── README.md
|
||||
├── src/
|
||||
│ ├── Examples.Gateway/
|
||||
│ │ ├── Program.cs
|
||||
│ │ ├── router.yaml
|
||||
│ │ └── appsettings.json
|
||||
│ ├── Examples.Billing.Microservice/
|
||||
│ │ ├── Program.cs
|
||||
│ │ ├── microservice.yaml
|
||||
│ │ └── Endpoints/
|
||||
│ │ ├── CreateInvoiceEndpoint.cs
|
||||
│ │ ├── GetInvoiceEndpoint.cs
|
||||
│ │ └── UploadAttachmentEndpoint.cs
|
||||
│ └── Examples.Inventory.Microservice/
|
||||
│ ├── Program.cs
|
||||
│ └── Endpoints/
|
||||
│ ├── ListItemsEndpoint.cs
|
||||
│ └── GetItemEndpoint.cs
|
||||
└── tests/
|
||||
└── Examples.Integration.Tests/
|
||||
```
|
||||
|
||||
## Running Locally
|
||||
|
||||
### Build the Solution
|
||||
|
||||
```bash
|
||||
cd examples/router
|
||||
dotnet build Examples.Router.sln
|
||||
```
|
||||
|
||||
### Run with Docker Compose
|
||||
|
||||
```bash
|
||||
docker-compose up --build
|
||||
```
|
||||
|
||||
This starts:
|
||||
- Gateway on port 8080 (HTTP) and 5100 (TCP transport)
|
||||
- Billing microservice
|
||||
- Inventory microservice
|
||||
- RabbitMQ (optional, for message-based transport)
|
||||
|
||||
### Run Without Docker
|
||||
|
||||
Start each service in separate terminals:
|
||||
|
||||
```bash
|
||||
# Terminal 1: Gateway
|
||||
cd src/Examples.Gateway
|
||||
dotnet run
|
||||
|
||||
# Terminal 2: Billing Microservice
|
||||
cd src/Examples.Billing.Microservice
|
||||
dotnet run
|
||||
|
||||
# Terminal 3: Inventory Microservice
|
||||
cd src/Examples.Inventory.Microservice
|
||||
dotnet run
|
||||
```
|
||||
|
||||
## Example API Calls
|
||||
|
||||
### Billing Service
|
||||
|
||||
Create an invoice:
|
||||
```bash
|
||||
curl -X POST http://localhost:8080/invoices \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"customerId": "CUST-001", "amount": 99.99, "description": "Service fee"}'
|
||||
```
|
||||
|
||||
Get an invoice:
|
||||
```bash
|
||||
curl http://localhost:8080/invoices/INV-12345
|
||||
```
|
||||
|
||||
Upload an attachment (streaming):
|
||||
```bash
|
||||
curl -X POST http://localhost:8080/invoices/INV-12345/attachments \
|
||||
-H "Content-Type: application/octet-stream" \
|
||||
--data-binary @document.pdf
|
||||
```
|
||||
|
||||
### Inventory Service
|
||||
|
||||
List items:
|
||||
```bash
|
||||
curl "http://localhost:8080/items?page=1&pageSize=20"
|
||||
```
|
||||
|
||||
List items by category:
|
||||
```bash
|
||||
curl "http://localhost:8080/items?category=widgets"
|
||||
```
|
||||
|
||||
Get a specific item:
|
||||
```bash
|
||||
curl http://localhost:8080/items/SKU-001
|
||||
```
|
||||
|
||||
## Adding New Endpoints
|
||||
|
||||
### 1. Create the Endpoint Class
|
||||
|
||||
```csharp
|
||||
using StellaOps.Microservice;
|
||||
|
||||
[StellaEndpoint("POST", "/orders", TimeoutSeconds = 30)]
|
||||
public sealed class CreateOrderEndpoint : IStellaEndpoint<CreateOrderRequest, CreateOrderResponse>
|
||||
{
|
||||
public Task<CreateOrderResponse> HandleAsync(
|
||||
CreateOrderRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
// Implementation
|
||||
return Task.FromResult(new CreateOrderResponse { OrderId = "ORD-123" });
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Register in Program.cs
|
||||
|
||||
```csharp
|
||||
builder.Services.AddScoped<CreateOrderEndpoint>();
|
||||
```
|
||||
|
||||
### 3. Update router.yaml (if needed)
|
||||
|
||||
Add routing rules for the new endpoint path.
|
||||
|
||||
## Streaming Endpoints
|
||||
|
||||
For endpoints that handle large payloads (file uploads, etc.), implement `IRawStellaEndpoint`:
|
||||
|
||||
```csharp
|
||||
[StellaEndpoint("POST", "/files/{id}", SupportsStreaming = true)]
|
||||
public sealed class UploadFileEndpoint : IRawStellaEndpoint
|
||||
{
|
||||
public async Task<RawResponse> HandleAsync(
|
||||
RawRequestContext context,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var id = context.PathParameters["id"];
|
||||
|
||||
// Stream body directly without buffering
|
||||
await using var stream = context.Body;
|
||||
// Process stream...
|
||||
|
||||
return RawResponse.Ok("{}");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Cancellation Behavior
|
||||
|
||||
All endpoints receive a `CancellationToken` that is triggered when:
|
||||
|
||||
1. The client disconnects
|
||||
2. The request timeout is exceeded
|
||||
3. The gateway shuts down
|
||||
|
||||
Always respect the cancellation token in long-running operations:
|
||||
|
||||
```csharp
|
||||
public async Task<Response> HandleAsync(Request request, CancellationToken ct)
|
||||
{
|
||||
// Check cancellation periodically
|
||||
ct.ThrowIfCancellationRequested();
|
||||
|
||||
// Or pass to async operations
|
||||
await SomeLongOperation(ct);
|
||||
}
|
||||
```
|
||||
|
||||
## Payload Limits
|
||||
|
||||
Default limits are configured in `router.yaml`:
|
||||
|
||||
```yaml
|
||||
payloadLimits:
|
||||
maxRequestBodySizeBytes: 10485760 # 10 MB
|
||||
maxChunkSizeBytes: 65536 # 64 KB
|
||||
```
|
||||
|
||||
For streaming endpoints, the body is not buffered so these limits apply per-chunk.
|
||||
|
||||
## Running Tests
|
||||
|
||||
```bash
|
||||
cd tests/Examples.Integration.Tests
|
||||
dotnet test
|
||||
```
|
||||
|
||||
The integration tests verify:
|
||||
- End-to-end request routing
|
||||
- Multi-service registration
|
||||
- Streaming uploads
|
||||
- Request cancellation
|
||||
- Payload limit enforcement
|
||||
|
||||
## Configuration
|
||||
|
||||
### Gateway (router.yaml)
|
||||
|
||||
```yaml
|
||||
# Microservice routing rules
|
||||
services:
|
||||
billing:
|
||||
routes:
|
||||
- path: /invoices
|
||||
methods: [GET, POST]
|
||||
- path: /invoices/{id}
|
||||
methods: [GET, PUT, DELETE]
|
||||
- path: /invoices/{id}/attachments
|
||||
methods: [POST]
|
||||
inventory:
|
||||
routes:
|
||||
- path: /items
|
||||
methods: [GET]
|
||||
- path: /items/{sku}
|
||||
methods: [GET]
|
||||
```
|
||||
|
||||
### Microservice (microservice.yaml)
|
||||
|
||||
```yaml
|
||||
service:
|
||||
name: billing
|
||||
version: 1.0.0
|
||||
region: demo
|
||||
|
||||
endpoints:
|
||||
- path: /invoices
|
||||
method: POST
|
||||
timeoutSeconds: 30
|
||||
- path: /invoices/{id}
|
||||
method: GET
|
||||
timeoutSeconds: 10
|
||||
|
||||
routers:
|
||||
- host: localhost
|
||||
port: 5100
|
||||
transportType: InMemory
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Microservice not registering
|
||||
|
||||
Check that:
|
||||
1. Gateway is running and healthy
|
||||
2. Router host/port in microservice.yaml matches gateway
|
||||
3. Network connectivity between services
|
||||
|
||||
### Request timeouts
|
||||
|
||||
Increase the timeout in the endpoint attribute:
|
||||
|
||||
```csharp
|
||||
[StellaEndpoint("POST", "/long-operation", TimeoutSeconds = 120)]
|
||||
```
|
||||
|
||||
### Streaming not working
|
||||
|
||||
Ensure the endpoint:
|
||||
1. Is marked with `SupportsStreaming = true`
|
||||
2. Implements `IRawStellaEndpoint`
|
||||
3. Does not buffer the entire body before processing
|
||||
|
||||
## License
|
||||
|
||||
AGPL-3.0-or-later
|
||||
Reference in New Issue
Block a user