New doc supporting CORS, gRCP, and general client integration
This commit is contained in:
@@ -0,0 +1,344 @@
|
||||
# Notifier Service Client Integration Prompt
|
||||
|
||||
**Use this prompt with Claude or another AI assistant when building a client application that integrates with the Notifier service.**
|
||||
|
||||
---
|
||||
|
||||
## Prompt for AI Assistant
|
||||
|
||||
I need to build a client application that integrates with a notification service using gRPC. Please help me implement a production-ready client that follows best practices for performance, security, and reliability.
|
||||
|
||||
### Service Details
|
||||
|
||||
**Protocol:** gRPC (HTTP/2)
|
||||
**Service Address:** `notifier-grpc:50051` (Kubernetes internal DNS)
|
||||
**Authentication:** API Key via gRPC metadata
|
||||
**API Key Format:** `nk_` prefix followed by 64 hex characters (e.g., `nk_abc123...`)
|
||||
|
||||
### Authentication Requirements
|
||||
|
||||
1. **API Key Delivery:**
|
||||
- Send API key in gRPC metadata with EVERY request
|
||||
- Two accepted header formats:
|
||||
- `authorization: Bearer nk_your_api_key_here` (preferred)
|
||||
- `x-api-key: nk_your_api_key_here`
|
||||
|
||||
2. **Per-Request Authentication:**
|
||||
- The service validates the API key on every RPC call
|
||||
- Authentication is NOT done at connection time
|
||||
- This means you can (and should) use long-lived connections
|
||||
|
||||
3. **Rate Limiting:**
|
||||
- Rate limits are enforced per API key
|
||||
- Default: 100 requests per minute (configurable per key)
|
||||
- Rate limit exceeded returns: `codes.ResourceExhausted`
|
||||
- Implement exponential backoff retry for rate limit errors
|
||||
|
||||
4. **Credential Management:**
|
||||
- Load API key from environment variable: `NOTIFIER_API_KEY`
|
||||
- NEVER hardcode API keys in source code
|
||||
- NEVER log the full API key (mask it: `nk_abc...xyz`)
|
||||
- Store in Kubernetes Secret if deploying to k8s
|
||||
|
||||
### Connection Management Requirements
|
||||
|
||||
**CRITICAL:** Use long-lived gRPC connections for optimal performance.
|
||||
|
||||
1. **Connection Lifecycle:**
|
||||
- Create ONE connection at application startup
|
||||
- Reuse the connection for ALL requests
|
||||
- Close the connection only when application exits
|
||||
- Do NOT create a new connection for each request
|
||||
|
||||
2. **Keepalive Configuration:**
|
||||
- Configure client keepalive to prevent connection timeouts
|
||||
- Recommended settings:
|
||||
- Time: 10 seconds (ping every 10s of inactivity)
|
||||
- Timeout: 3 seconds (wait for ping response)
|
||||
- PermitWithoutStream: true (allow pings when idle)
|
||||
|
||||
3. **Why This Matters:**
|
||||
- Connection reuse: 10x faster (1.5ms vs 15ms per request)
|
||||
- Eliminates TCP + TLS handshake overhead (10-15ms per request)
|
||||
- HTTP/2 multiplexing: handle multiple concurrent requests on one connection
|
||||
- Lower resource usage: one connection vs thousands
|
||||
|
||||
### Error Handling Requirements
|
||||
|
||||
1. **Handle These Error Codes:**
|
||||
- `codes.Unauthenticated` - Invalid or missing API key → Log error, don't retry
|
||||
- `codes.ResourceExhausted` - Rate limit exceeded → Retry with exponential backoff
|
||||
- `codes.InvalidArgument` - Bad request payload → Log error, don't retry
|
||||
- `codes.Unavailable` - Service temporarily unavailable → Retry with backoff
|
||||
- `codes.DeadlineExceeded` - Request timeout → Retry (may be transient)
|
||||
|
||||
2. **Retry Strategy:**
|
||||
- Implement exponential backoff: 100ms, 200ms, 400ms, 800ms
|
||||
- Maximum 3-5 retry attempts for retryable errors
|
||||
- Only retry: rate limits, unavailable, deadline exceeded
|
||||
- Do NOT retry: authentication errors, invalid arguments
|
||||
|
||||
3. **Context Timeouts:**
|
||||
- Set reasonable timeout for each request (5-30 seconds)
|
||||
- Use `context.WithTimeout()` for every RPC call
|
||||
- Allow timeout to be configurable
|
||||
|
||||
### Code Structure Requirements
|
||||
|
||||
1. **Client Structure:**
|
||||
```
|
||||
NotifierClient struct:
|
||||
- conn: *grpc.ClientConn (long-lived)
|
||||
- client: pb.NotifierServiceClient
|
||||
- apiKey: string (loaded from env)
|
||||
- logger: logging interface
|
||||
```
|
||||
|
||||
2. **Required Methods:**
|
||||
- `NewNotifierClient(address, apiKey string) (*NotifierClient, error)` - Initialize
|
||||
- `SendNotification(ctx, request) (response, error)` - Send single notification
|
||||
- `SendBatchNotifications(ctx, requests) (responses, error)` - Send batch
|
||||
- `HealthCheck(ctx) error` - Verify connection health
|
||||
- `Close() error` - Clean up connection
|
||||
|
||||
3. **Initialization Pattern:**
|
||||
- Create client as singleton at application startup
|
||||
- Use `sync.Once` to ensure only one instance
|
||||
- Defer `Close()` in main() to ensure cleanup
|
||||
|
||||
### Observability Requirements
|
||||
|
||||
1. **Logging:**
|
||||
- Log connection establishment (info level)
|
||||
- Log authentication failures (warn level)
|
||||
- Log rate limit exceeded (warn level)
|
||||
- Log successful sends (debug level)
|
||||
- NEVER log the full API key (mask it)
|
||||
|
||||
2. **Metrics (if using Prometheus):**
|
||||
- Counter: `notifier_requests_total` (labels: status, method)
|
||||
- Counter: `notifier_requests_failed_total` (labels: error_code)
|
||||
- Histogram: `notifier_request_duration_seconds`
|
||||
- Counter: `notifier_rate_limit_errors_total`
|
||||
|
||||
3. **Health Checks:**
|
||||
- Implement background health check goroutine
|
||||
- Check every 30-60 seconds
|
||||
- Call the service's HealthCheck RPC
|
||||
- Log warnings if health checks fail
|
||||
|
||||
### Security Requirements
|
||||
|
||||
1. **Credential Security:**
|
||||
- Load API key ONLY from environment variables
|
||||
- Never commit API keys to version control
|
||||
- Use `.gitignore` to exclude any files with credentials
|
||||
- Implement a `String()` method that masks the API key for logging
|
||||
|
||||
2. **TLS Configuration:**
|
||||
- For local development: `grpc.WithInsecure()` is acceptable
|
||||
- For production: Use TLS with proper certificate validation
|
||||
- For Kubernetes: Internal communication may use insecure (cluster network is trusted)
|
||||
|
||||
3. **Graceful Degradation:**
|
||||
- If notifier service is unavailable, application should continue
|
||||
- Log notification failures but don't crash the application
|
||||
- Consider implementing a circuit breaker pattern for resilience
|
||||
|
||||
### Testing Requirements
|
||||
|
||||
1. **Unit Tests:**
|
||||
- Mock the gRPC client interface
|
||||
- Test retry logic with rate limit errors
|
||||
- Test context timeout handling
|
||||
- Test credential masking in logs
|
||||
|
||||
2. **Integration Tests:**
|
||||
- Test actual connection to notifier service (if available)
|
||||
- Test authentication with valid and invalid keys
|
||||
- Test rate limiting behavior
|
||||
|
||||
### Kubernetes Deployment Requirements (if applicable)
|
||||
|
||||
1. **Environment Variables:**
|
||||
```yaml
|
||||
env:
|
||||
- name: NOTIFIER_API_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: notifier-credentials
|
||||
key: api-key
|
||||
- name: NOTIFIER_ADDRESS
|
||||
value: "notifier-grpc:50051"
|
||||
```
|
||||
|
||||
2. **Secrets Configuration:**
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: notifier-credentials
|
||||
type: Opaque
|
||||
stringData:
|
||||
api-key: nk_your_actual_api_key_here
|
||||
```
|
||||
|
||||
### Available gRPC Methods
|
||||
|
||||
The NotifierService provides these RPC methods:
|
||||
|
||||
1. **SendNotification** - Send a single notification
|
||||
- Request: `SendNotificationRequest`
|
||||
- Response: `SendNotificationResponse` (contains notification_id)
|
||||
|
||||
2. **SendBatchNotifications** - Send multiple notifications at once
|
||||
- Request: `SendBatchNotificationsRequest`
|
||||
- Response: `SendBatchNotificationsResponse` (contains notification_ids)
|
||||
|
||||
3. **GetNotification** - Retrieve notification status by ID
|
||||
- Request: `GetNotificationRequest` (notification_id)
|
||||
- Response: `GetNotificationResponse`
|
||||
|
||||
4. **ListNotifications** - List recent notifications
|
||||
- Request: `ListNotificationsRequest` (pagination)
|
||||
- Response: `ListNotificationsResponse`
|
||||
|
||||
5. **CancelNotification** - Cancel a pending notification
|
||||
- Request: `CancelNotificationRequest` (notification_id)
|
||||
- Response: `CancelNotificationResponse`
|
||||
|
||||
6. **RetryNotification** - Retry a failed notification
|
||||
- Request: `RetryNotificationRequest` (notification_id)
|
||||
- Response: `RetryNotificationResponse`
|
||||
|
||||
7. **GetStats** - Get service statistics
|
||||
- Request: `GetStatsRequest`
|
||||
- Response: `GetStatsResponse`
|
||||
|
||||
8. **GetNotifiers** - List available notifier types and accounts
|
||||
- Request: `GetNotifiersRequest`
|
||||
- Response: `GetNotifiersResponse`
|
||||
|
||||
9. **HealthCheck** - Verify service health
|
||||
- Request: `HealthCheckRequest`
|
||||
- Response: `HealthCheckResponse`
|
||||
|
||||
### Notification Types Supported
|
||||
|
||||
- `NOTIFICATION_TYPE_EMAIL` - Email notifications
|
||||
- `NOTIFICATION_TYPE_SLACK` - Slack messages
|
||||
- `NOTIFICATION_TYPE_NTFY` - Ntfy.sh push notifications
|
||||
- `NOTIFICATION_TYPE_STDOUT` - Console output (development only)
|
||||
|
||||
### Request Example Structure
|
||||
|
||||
```protobuf
|
||||
message SendNotificationRequest {
|
||||
NotificationType type = 1; // Required: email, slack, ntfy, stdout
|
||||
string subject = 2; // Required: notification subject/title
|
||||
string body = 3; // Required: notification body/content
|
||||
repeated string recipients = 4; // Required: email addresses, slack channels, etc.
|
||||
Priority priority = 5; // Optional: normal, high, critical
|
||||
string account = 6; // Optional: specific notifier account to use
|
||||
map<string, string> metadata = 7; // Optional: additional metadata
|
||||
}
|
||||
```
|
||||
|
||||
### Implementation Goals
|
||||
|
||||
Please implement a client that:
|
||||
|
||||
1. ✅ Uses a single long-lived gRPC connection
|
||||
2. ✅ Sends API key in metadata with every request
|
||||
3. ✅ Implements exponential backoff retry for rate limits
|
||||
4. ✅ Loads credentials from environment variables
|
||||
5. ✅ Uses context with timeout for all requests
|
||||
6. ✅ Implements keepalive to prevent connection timeouts
|
||||
7. ✅ Includes proper error handling for all error codes
|
||||
8. ✅ Logs important events without exposing credentials
|
||||
9. ✅ Provides a health check mechanism
|
||||
10. ✅ Includes graceful shutdown (connection cleanup)
|
||||
11. ✅ Is production-ready with proper error handling and logging
|
||||
12. ✅ Includes basic unit tests
|
||||
|
||||
### Example Usage Pattern
|
||||
|
||||
The client should be usable like this:
|
||||
|
||||
```go
|
||||
// Initialize once at startup
|
||||
client, err := notifier.NewClient(
|
||||
os.Getenv("NOTIFIER_ADDRESS"),
|
||||
os.Getenv("NOTIFIER_API_KEY"),
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
// Use throughout application lifetime
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
resp, err := client.SendNotification(ctx, ¬ifier.SendRequest{
|
||||
Type: notifier.TypeEmail,
|
||||
Subject: "Important Alert",
|
||||
Body: "This is a critical notification",
|
||||
Recipients: []string{"admin@example.com"},
|
||||
Priority: notifier.PriorityHigh,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
log.Printf("Failed to send notification: %v", err)
|
||||
// Application continues - notification failure is not fatal
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("Notification sent successfully: %s", resp.NotificationID)
|
||||
```
|
||||
|
||||
### Additional Context
|
||||
|
||||
- This is a microservices architecture running in Kubernetes
|
||||
- The notifier service handles routing to multiple notification backends (SMTP, Slack, Ntfy)
|
||||
- The client may need to send notifications from multiple goroutines concurrently
|
||||
- High reliability is important, but notification failures should not crash the application
|
||||
- The API key has a rate limit of 100 requests per minute (may vary per key)
|
||||
|
||||
### Code Generation Note
|
||||
|
||||
If you need the protobuf definitions, they can be generated from the service's proto files located at `api/grpc/proto/notifier.proto` in the notifier service repository.
|
||||
|
||||
---
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
**Copy the prompt above and provide it to your AI assistant when building the client. The prompt includes:**
|
||||
|
||||
- All authentication requirements and formats
|
||||
- Connection management best practices
|
||||
- Error handling strategies
|
||||
- Security requirements
|
||||
- Observability guidelines
|
||||
- Complete API method listing
|
||||
- Production-ready patterns
|
||||
- Kubernetes deployment configuration
|
||||
|
||||
**The AI assistant should generate:**
|
||||
- Complete, production-ready client code
|
||||
- Proper connection pooling and keepalive
|
||||
- Comprehensive error handling
|
||||
- Secure credential management
|
||||
- Health check implementation
|
||||
- Unit tests
|
||||
- Usage documentation
|
||||
|
||||
**Review the generated code for:**
|
||||
- ✅ Single long-lived connection (not creating connections per request)
|
||||
- ✅ API key sent in metadata with every RPC
|
||||
- ✅ Keepalive configuration present
|
||||
- ✅ Exponential backoff retry logic
|
||||
- ✅ No hardcoded credentials
|
||||
- ✅ Proper context timeouts
|
||||
- ✅ Error handling for all gRPC error codes
|
||||
- ✅ Graceful shutdown (Close() method)
|
||||
Reference in New Issue
Block a user