feat: wire Temporal gRPC into REST handler
CI / Vet, test, build (push) Failing after 2m31s
CI / Build and push image (push) Skipped

- Handler now maintains gRPC connection to Temporal (port 7233)
- startWorkflow & describeWorkflow translated to actual gRPC calls
- Other 20+ operations phased in via TEMPORAL_GRPC_MIGRATION roadmap
- Updated docs: TEMPORAL_USAGE now describes gRPC architecture
- Added TEMPORAL_GRPC_MIGRATION.md for implementation reference
- Deleted WORKFLOWS.md (outdated duplicate)

Fixes: gRPC was imported but unused - now operational for START/DESCRIBE.
Verification: go build ./cmd/gateway  (no errors)
This commit is contained in:
Admin Bot
2026-08-29 21:54:16 -07:00
parent 4633989a46
commit 4935ea9f95
4 changed files with 258 additions and 702 deletions
+64 -7
View File
@@ -5,8 +5,13 @@ import (
"context"
"encoding/json"
"fmt"
"log"
"net/http"
"time"
"go.temporal.io/api/common/v1"
"go.temporal.io/api/taskqueue/v1"
"go.temporal.io/api/workflowservice/v1"
)
// RequestPayload represents the unified request format for all operations
@@ -29,7 +34,8 @@ type ResponsePayload struct {
// Handler handles HTTP requests for Temporal operations
type Handler struct {
hostPort string // e.g., "localhost:7233"
hostPort string // e.g., "localhost:7233"
grpcClient *GRPCClient // gRPC connection to Temporal
}
// NewHandler creates a new Temporal HTTP handler
@@ -37,8 +43,16 @@ func NewHandler(hostPort string) *Handler {
if hostPort == "" {
hostPort = "localhost:7233"
}
grpcClient, err := NewGRPCClient(hostPort)
if err != nil {
log.Printf("WARNING: Failed to connect to Temporal at %s: %v", hostPort, err)
// Don't fail startup; operations will return errors
}
return &Handler{
hostPort: hostPort,
hostPort: hostPort,
grpcClient: grpcClient,
}
}
@@ -266,6 +280,10 @@ func getMap(payload map[string]interface{}, key string) map[string]interface{} {
// Workflow Operations
func (h *Handler) startWorkflow(ctx context.Context, namespace string, payload map[string]interface{}) (interface{}, string, string) {
if h.grpcClient == nil {
return nil, "TEMPORAL_UNAVAILABLE", "Temporal server connection not available"
}
workflowID := getString(payload, "workflow_id")
if workflowID == "" {
return nil, "INVALID_REQUEST", "workflow_id is required"
@@ -281,25 +299,64 @@ func (h *Handler) startWorkflow(ctx context.Context, namespace string, payload m
return nil, "INVALID_REQUEST", "task_queue is required"
}
// Would call Temporal WorkflowService.StartWorkflowExecution
input := getMap(payload, "input")
req := &workflowservice.StartWorkflowExecutionRequest{
Namespace: namespace,
WorkflowId: workflowID,
WorkflowType: &common.WorkflowType{Name: workflowType},
TaskQueue: &taskqueue.TaskQueue{Name: taskQueue},
}
if len(input) > 0 {
inputBytes, _ := json.Marshal(input)
req.Input = &common.Payloads{
Payloads: []*common.Payload{{Data: inputBytes}},
}
}
resp, err := h.grpcClient.GetWorkflowServiceStub().StartWorkflowExecution(ctx, req)
if err != nil {
return nil, "TEMPORAL_UNAVAILABLE", fmt.Sprintf("failed to start workflow: %v", err)
}
return map[string]interface{}{
"workflow_id": workflowID,
"run_id": fmt.Sprintf("run_%d", time.Now().UnixNano()),
"run_id": resp.RunId,
"start_time": time.Now(),
}, "", ""
}
func (h *Handler) describeWorkflow(ctx context.Context, namespace string, payload map[string]interface{}) (interface{}, string, string) {
if h.grpcClient == nil {
return nil, "TEMPORAL_UNAVAILABLE", "Temporal server connection not available"
}
workflowID := getString(payload, "workflow_id")
if workflowID == "" {
return nil, "INVALID_REQUEST", "workflow_id is required"
}
// Would call Temporal WorkflowService.DescribeWorkflowExecution
runID := getString(payload, "run_id")
resp, err := h.grpcClient.GetWorkflowServiceStub().DescribeWorkflowExecution(ctx, &workflowservice.DescribeWorkflowExecutionRequest{
Namespace: namespace,
Execution: &common.WorkflowExecution{WorkflowId: workflowID, RunId: runID},
})
if err != nil {
return nil, "WORKFLOW_NOT_FOUND", fmt.Sprintf("failed to describe workflow: %v", err)
}
status := "UNKNOWN"
if resp.WorkflowExecutionInfo != nil {
status = resp.WorkflowExecutionInfo.Status.String()
}
return map[string]interface{}{
"workflow_id": workflowID,
"status": "RUNNING",
"start_time": time.Now(),
"run_id": runID,
"status": status,
"start_time": resp.WorkflowExecutionInfo.StartTime,
}, "", ""
}