mirror of
https://github.com/hanzoai/tasks.git
synced 2026-10-04 02:04:03 +00:00
## What changed?
Extracting workflow id during redirection based on interface
## Why?
Configurable checks if needed based on workflow id
## How did you test it?
- [ ] built
- [ ] run locally and tested manually
- [ ] covered by existing tests
- [ ] added new unit test(s)
- [ ] added new functional test(s)
```
michaely520 ~/projects/temporal % TEMPORAL_TEST_LOG_LEVEL=debug go test -tags test_dep -v ./tests/xdc -run TestStreamBasedReplicationTestSuite/EnableTransitionHistory/TestCloseTransferTaskAckedReplication -timeout 5m -count=1 | grep "workflow ID extraction"
2025-12-17T08:29:04.827-0800 debug workflow ID extraction: adding workflow ID to context {"cluster-name": "active_odmrd", "host": "127.0.0.1:63201", "operation": "StartWorkflowExecution", "wf-id": "test-replication-40c67c0f-1730-4b61-9a84-fde179771032", "logging-call-at": "/Users/michaely520/projects/temporal/common/rpc/interceptor/workflow_id_interceptor.go:129"}
2025-12-17T08:29:04.837-0800 debug workflow ID extraction: adding workflow ID to context {"cluster-name": "active_odmrd", "host": "127.0.0.1:63201", "operation": "RespondWorkflowTaskCompleted", "wf-id": "test-replication-40c67c0f-1730-4b61-9a84-fde179771032", "logging-call-at": "/Users/michaely520/projects/temporal/common/rpc/interceptor/workflow_id_interceptor.go:129"}
2025-12-17T08:29:04.940-0800 debug workflow ID extraction: adding workflow ID to context {"cluster-name": "active_odmrd", "host": "127.0.0.1:63201", "operation": "DescribeWorkflowExecution", "wf-id": "test-replication-40c67c0f-1730-4b61-9a84-fde179771032", "logging-call-at": "/Users/michaely520/projects/temporal/common/rpc/interceptor/workflow_id_interceptor.go:129"}
2025-12-17T08:29:09.949-0800 debug workflow ID extraction: adding workflow ID to context {"cluster-name": "standby_odmrd", "host": "127.0.0.1:63210", "operation": "DescribeWorkflowExecution", "wf-id": "test-replication-40c67c0f-1730-4b61-9a84-fde179771032", "logging-call-at": "/Users/michaely520/projects/temporal/common/rpc/interceptor/workflow_id_interceptor.go:129"}
2025-12-17T08:29:12.061-0800 debug workflow ID extraction: adding workflow ID to context {"cluster-name": "standby_odmrd", "host": "127.0.0.1:63210", "operation": "DescribeWorkflowExecution", "wf-id": "test-replication-40c67c0f-1730-4b61-9a84-fde179771032", "logging-call-at": "/Users/michaely520/projects/temporal/common/rpc/interceptor/workflow_id_interceptor.go:129"}
```
152 lines
5.7 KiB
Go
152 lines
5.7 KiB
Go
package interceptor
|
|
|
|
import (
|
|
"context"
|
|
|
|
"go.temporal.io/server/common/log"
|
|
"go.temporal.io/server/common/log/tag"
|
|
"go.temporal.io/server/common/namespace"
|
|
"google.golang.org/grpc"
|
|
)
|
|
|
|
type (
|
|
businessIDContextKey struct{}
|
|
|
|
// BusinessIDPattern defines the expected interface pattern for extracting business ID
|
|
BusinessIDPattern int
|
|
|
|
// BusinessIDExtractorFunc extracts business ID from a request.
|
|
// Returns empty string if this extractor doesn't handle the request.
|
|
BusinessIDExtractorFunc func(ctx context.Context, req any, fullMethod string) string
|
|
|
|
// BusinessIDInterceptor extracts business ID from requests and adds it to context.
|
|
// It iterates through a list of extractor functions until one returns a non-empty business ID.
|
|
BusinessIDInterceptor struct {
|
|
extractors []BusinessIDExtractorFunc
|
|
logger log.Logger
|
|
}
|
|
)
|
|
|
|
var businessIDCtxKey = businessIDContextKey{}
|
|
|
|
const (
|
|
// PatternNone indicates no business ID extraction is needed
|
|
PatternNone BusinessIDPattern = iota
|
|
// PatternWorkflowID indicates extraction via GetWorkflowId() method
|
|
PatternWorkflowID
|
|
// PatternWorkflowExecution indicates extraction via GetWorkflowExecution().GetWorkflowId()
|
|
PatternWorkflowExecution
|
|
// PatternExecution indicates extraction via GetExecution().GetWorkflowId()
|
|
PatternExecution
|
|
// PatternTaskToken indicates extraction via deserializing GetTaskToken()
|
|
PatternTaskToken
|
|
// PatternMultiOperation indicates extraction from ExecuteMultiOperationRequest
|
|
PatternMultiOperation
|
|
)
|
|
|
|
// methodToPattern maps API method names to their expected business ID extraction pattern.
|
|
// Methods not in this map are treated as PatternNone (no business ID extraction needed).
|
|
var methodToPattern = map[string]BusinessIDPattern{
|
|
// Pattern: GetWorkflowId() - direct WorkflowId field
|
|
"StartWorkflowExecution": PatternWorkflowID,
|
|
"SignalWithStartWorkflowExecution": PatternWorkflowID,
|
|
"PauseWorkflowExecution": PatternWorkflowID,
|
|
"UnpauseWorkflowExecution": PatternWorkflowID,
|
|
"RecordActivityTaskHeartbeatById": PatternWorkflowID,
|
|
"RespondActivityTaskCompletedById": PatternWorkflowID,
|
|
"RespondActivityTaskCanceledById": PatternWorkflowID,
|
|
"RespondActivityTaskFailedById": PatternWorkflowID,
|
|
|
|
// Pattern: GetWorkflowExecution().GetWorkflowId()
|
|
"DeleteWorkflowExecution": PatternWorkflowExecution,
|
|
"RequestCancelWorkflowExecution": PatternWorkflowExecution,
|
|
"ResetWorkflowExecution": PatternWorkflowExecution,
|
|
"SignalWorkflowExecution": PatternWorkflowExecution,
|
|
"TerminateWorkflowExecution": PatternWorkflowExecution,
|
|
"UpdateWorkflowExecution": PatternWorkflowExecution,
|
|
"UpdateWorkflowExecutionOptions": PatternWorkflowExecution,
|
|
|
|
// Pattern: GetExecution().GetWorkflowId()
|
|
"DescribeWorkflowExecution": PatternExecution,
|
|
"GetWorkflowExecutionHistory": PatternExecution,
|
|
"GetWorkflowExecutionHistoryReverse": PatternExecution,
|
|
"QueryWorkflow": PatternExecution,
|
|
"ResetStickyTaskQueue": PatternExecution,
|
|
"ResetActivity": PatternExecution,
|
|
"PauseActivity": PatternExecution,
|
|
"UnpauseActivity": PatternExecution,
|
|
"UpdateActivityOptions": PatternExecution,
|
|
"TriggerWorkflowRule": PatternExecution,
|
|
|
|
// Pattern: TaskToken deserialization
|
|
"RecordActivityTaskHeartbeat": PatternTaskToken,
|
|
"RespondActivityTaskCompleted": PatternTaskToken,
|
|
"RespondActivityTaskCanceled": PatternTaskToken,
|
|
"RespondActivityTaskFailed": PatternTaskToken,
|
|
"RespondWorkflowTaskCompleted": PatternTaskToken,
|
|
"RespondWorkflowTaskFailed": PatternTaskToken,
|
|
|
|
// Pattern: ExecuteMultiOperation special handling
|
|
"ExecuteMultiOperation": PatternMultiOperation,
|
|
}
|
|
|
|
// NewBusinessIDInterceptor creates a new BusinessIDInterceptor with the given extractor functions.
|
|
// Extractors are called in order until one returns a non-empty business ID.
|
|
func NewBusinessIDInterceptor(
|
|
extractors []BusinessIDExtractorFunc,
|
|
logger log.Logger,
|
|
) *BusinessIDInterceptor {
|
|
return &BusinessIDInterceptor{
|
|
extractors: extractors,
|
|
logger: logger,
|
|
}
|
|
}
|
|
|
|
// WithExtractors returns a new interceptor with additional extractors prepended.
|
|
// The new extractors will be tried before the existing ones.
|
|
func (i *BusinessIDInterceptor) WithExtractors(extractors ...BusinessIDExtractorFunc) *BusinessIDInterceptor {
|
|
return &BusinessIDInterceptor{
|
|
extractors: append(extractors, i.extractors...),
|
|
logger: i.logger,
|
|
}
|
|
}
|
|
|
|
var _ grpc.UnaryServerInterceptor = (*BusinessIDInterceptor)(nil).Intercept
|
|
|
|
// Intercept extracts business ID from the request and adds it to the context.
|
|
// It tries each extractor in order until one returns a non-empty business ID.
|
|
func (i *BusinessIDInterceptor) Intercept(
|
|
ctx context.Context,
|
|
req any,
|
|
info *grpc.UnaryServerInfo,
|
|
handler grpc.UnaryHandler,
|
|
) (any, error) {
|
|
// Try each extractor until one returns a non-empty businessID
|
|
for _, extractor := range i.extractors {
|
|
if businessID := extractor(ctx, req, info.FullMethod); businessID != "" {
|
|
i.logger.Debug("business ID extraction: adding business ID to context",
|
|
tag.WorkflowID(businessID),
|
|
tag.NewStringTag("grpc-method", info.FullMethod),
|
|
)
|
|
ctx = AddBusinessIDToContext(ctx, businessID)
|
|
break
|
|
}
|
|
}
|
|
|
|
return handler(ctx, req)
|
|
}
|
|
|
|
// AddBusinessIDToContext adds the business ID to the context
|
|
func AddBusinessIDToContext(ctx context.Context, businessID string) context.Context {
|
|
return context.WithValue(ctx, businessIDCtxKey, businessID)
|
|
}
|
|
|
|
// GetBusinessIDFromContext retrieves the business ID from the context.
|
|
// Returns namespace.EmptyBusinessID if not found.
|
|
func GetBusinessIDFromContext(ctx context.Context) string {
|
|
if businessID, ok := ctx.Value(businessIDCtxKey).(string); ok {
|
|
return businessID
|
|
}
|
|
return namespace.EmptyBusinessID
|
|
}
|