177 lines
4.9 KiB
Markdown
177 lines
4.9 KiB
Markdown
# Temporal gRPC Integration - Migration Status
|
|||
|
|
|
||
|
|
## Overview
|
||
|
|
|
||
|
|
Temporal REST ↔ gRPC bridge is being implemented. Client sends HTTP JSON → gateway translates to gRPC → Temporal server responds.
|
||
|
|
|
||
|
|
## Implementation Status
|
||
|
|
|
||
|
|
### Phase 1: Core Workflow Operations ✅ WIRED
|
||
|
|
|
||
|
|
- **START_WORKFLOW** ✅ gRPC: `StartWorkflowExecution`
|
||
|
|
- **DESCRIBE_WORKFLOW** ✅ gRPC: `DescribeWorkflowExecution`
|
||
|
|
- **LIST_WORKFLOWS** ⏳ TODO (requires pagination logic)
|
||
|
|
- **GET_WORKFLOW_HISTORY** ⏳ TODO
|
||
|
|
- **SIGNAL_WORKFLOW** ⏳ TODO
|
||
|
|
- **QUERY_WORKFLOW** ⏳ TODO
|
||
|
|
- **CANCEL_WORKFLOW** ⏳ TODO
|
||
|
|
- **TERMINATE_WORKFLOW** ⏳ TODO
|
||
|
|
- **RESET_WORKFLOW** ⏳ TODO
|
||
|
|
- **UPDATE_WORKFLOW** ⏳ TODO
|
||
|
|
|
||
|
|
### Phase 2: Activity Operations ⏳ NOT IMPLEMENTED
|
||
|
|
|
||
|
|
- HEARTBEAT_ACTIVITY
|
||
|
|
- COMPLETE_ACTIVITY
|
||
|
|
- FAIL_ACTIVITY
|
||
|
|
|
||
|
|
**Note:** Activity operations require different error handling (task tokens, etc.). See operations_grpc.go for reference.
|
||
|
|
|
||
|
|
### Phase 3: OperatorService Operations ⏳ NOT IMPLEMENTED
|
||
|
|
|
||
|
|
Requires separate gRPC stub. Currently:
|
||
|
|
- LIST_NAMESPACES → 501 NOT_IMPLEMENTED
|
||
|
|
- DESCRIBE_NAMESPACE → 501 NOT_IMPLEMENTED
|
||
|
|
- CREATE_NAMESPACE → 501 NOT_IMPLEMENTED
|
||
|
|
- UPDATE_NAMESPACE → 501 NOT_IMPLEMENTED
|
||
|
|
- DELETE_NAMESPACE → 501 NOT_IMPLEMENTED
|
||
|
|
- LIST_SEARCH_ATTRIBUTES → 501 NOT_IMPLEMENTED
|
||
|
|
- ADD_SEARCH_ATTRIBUTES → 501 NOT_IMPLEMENTED
|
||
|
|
- LIST_TASK_QUEUES → 501 NOT_IMPLEMENTED
|
||
|
|
- GET_CLUSTER_INFO → 501 NOT_IMPLEMENTED
|
||
|
|
- LIST_CLUSTER_MEMBERS → 501 NOT_IMPLEMENTED
|
||
|
|
- GET_SYSTEM_INFO → 501 NOT_IMPLEMENTED
|
||
|
|
|
||
|
|
## Architecture
|
||
|
|
|
||
|
|
```
|
||
|
|
HTTP Request (JSON)
|
||
|
|
↓
|
||
|
|
Handler.startWorkflow()
|
||
|
|
↓
|
||
|
|
Converts to protobuf (workflowservice.StartWorkflowExecutionRequest)
|
||
|
|
↓
|
||
|
|
gRPCClient.GetWorkflowServiceStub().StartWorkflowExecution(ctx, req)
|
||
|
|
↓
|
||
|
|
Temporal Server (port 7233)
|
||
|
|
↓
|
||
|
|
gRPC Response
|
||
|
|
↓
|
||
|
|
Convert to JSON response map
|
||
|
|
↓
|
||
|
|
HTTP 200 JSON
|
||
|
|
```
|
||
|
|
|
||
|
|
## Code References
|
||
|
|
|
||
|
|
- **handler.go**: HTTP ↔ gRPC translation layer
|
||
|
|
- `NewHandler()`: Creates gRPC connection via `NewGRPCClient()`
|
||
|
|
- `startWorkflow()`, `describeWorkflow()`: gRPC-wired operations
|
||
|
|
- Others: stubs or NOT_IMPLEMENTED
|
||
|
|
|
||
|
|
- **grpc_client.go**: Low-level gRPC connection management
|
||
|
|
- `NewGRPCClient()`: Dials Temporal at port 7233
|
||
|
|
- `GetWorkflowServiceStub()`: Returns `workflowservice.WorkflowServiceClient`
|
||
|
|
- `GetOperatorServiceStub()`: Returns `operatorservice.OperatorServiceClient`
|
||
|
|
|
||
|
|
- **operations_grpc.go**: Example gRPC implementations (reference for wiring)
|
||
|
|
- Shows payload marshaling patterns
|
||
|
|
- Shows error handling (gRPC status codes → HTTP 4xx/5xx)
|
||
|
|
|
||
|
|
## Next Steps (Phase 2)
|
||
|
|
|
||
|
|
1. Wire remaining WorkflowService operations (LIST, GET_HISTORY, SIGNAL, QUERY, etc.)
|
||
|
|
- All use same pattern: build protobuf request → call stub → map response to JSON
|
||
|
|
- Reference operations_grpc.go for exact patterns
|
||
|
|
|
||
|
|
2. Add OperatorService support (namespaces, cluster, search attrs)
|
||
|
|
- Create separate stub: `operatorServiceClient := NewGRPCClient().GetOperatorServiceStub()`
|
||
|
|
- Add methods to handler for each operation
|
||
|
|
|
||
|
|
3. Add Activity operations (heartbeat, complete, fail)
|
||
|
|
- Requires task token handling
|
||
|
|
- See operations_grpc_test.go for test patterns
|
||
|
|
|
||
|
|
## Build Status
|
||
|
|
|
||
|
|
```
|
||
|
|
go build ./cmd/gateway ✅ SUCCESS
|
||
|
|
```
|
||
|
|
|
||
|
|
## Testing
|
||
|
|
|
||
|
|
To test gRPC wiring locally:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Start Temporal locally (if not running)
|
||
|
|
docker run -d -p 7233:7233 temporalio/auto-setup:latest
|
||
|
|
|
||
|
|
# Start gateway
|
||
|
|
go run ./cmd/gateway
|
||
|
|
|
||
|
|
# Test (in another terminal)
|
||
|
|
curl -X POST http://localhost:8080/workflow \
|
||
|
|
-H 'Content-Type: application/json' \
|
||
|
|
-d '{
|
||
|
|
"action": "START_WORKFLOW",
|
||
|
|
"namespace": "default",
|
||
|
|
"payload": {
|
||
|
|
"workflow_id": "test-1",
|
||
|
|
"workflow_type": "MyWorkflow",
|
||
|
|
"task_queue": "my-queue"
|
||
|
|
}
|
||
|
|
}'
|
||
|
|
|
||
|
|
# Should return
|
||
|
|
{
|
||
|
|
"success": true,
|
||
|
|
"action": "START_WORKFLOW",
|
||
|
|
"data": {
|
||
|
|
"workflow_id": "test-1",
|
||
|
|
"run_id": "abc123...",
|
||
|
|
"start_time": "2026-08-27T..."
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## Key Implementation Details
|
||
|
|
|
||
|
|
### Protobuf Field Names
|
||
|
|
Temporal protobuf uses snake_case field names:
|
||
|
|
- `WorkflowId` not `WorkflowID`
|
||
|
|
- `RunId` not `RunID`
|
||
|
|
- `WorkflowType` (message) not `WorkflowTypeString`
|
||
|
|
- `TaskQueue` (message) not `TaskQueueName`
|
||
|
|
|
||
|
|
### Type Imports (from go.temporal.io/api)
|
||
|
|
```go
|
||
|
|
import (
|
||
|
|
"go.temporal.io/api/common/v1" // WorkflowExecution, WorkflowType, Payloads
|
||
|
|
"go.temporal.io/api/taskqueue/v1" // TaskQueue
|
||
|
|
"go.temporal.io/api/workflowservice/v1" // All Workflow* stubs
|
||
|
|
"go.temporal.io/api/operatorservice/v1" // Namespace/cluster stubs (not yet used)
|
||
|
|
)
|
||
|
|
```
|
||
|
|
|
||
|
|
### Payload Marshaling Pattern
|
||
|
|
```go
|
||
|
|
input := getMap(payload, "input")
|
||
|
|
if len(input) > 0 {
|
||
|
|
inputBytes, _ := json.Marshal(input)
|
||
|
|
req.Input = &common.Payloads{
|
||
|
|
Payloads: []*common.Payload{{Data: inputBytes}},
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Error Handling
|
||
|
|
- gRPC errors → map to HTTP status:
|
||
|
|
- `codes.NotFound` → 404
|
||
|
|
- `codes.InvalidArgument` → 400
|
||
|
|
- `codes.Unavailable` → 503
|
||
|
|
- others → 500
|
||
|
|
|
||
|
|
## Questions / Blockers
|
||
|
|
|
||
|
|
None currently. gRPC wiring is straightforward pattern-matching.
|