Files
tasks/chasm/lib/workflow/workflow.go
Dan Davison 15edc85462 Post-merge changes to standalone-activity (#8770)
All changes needed to make tests compile and pass after
merging main into standalone-activity.
2025-12-19 11:01:46 -05:00

124 lines
3.8 KiB
Go

package workflow
import (
"fmt"
commonpb "go.temporal.io/api/common/v1"
"go.temporal.io/api/serviceerror"
"go.temporal.io/server/chasm"
"go.temporal.io/server/chasm/lib/callback"
callbackspb "go.temporal.io/server/chasm/lib/callback/gen/callbackpb/v1"
"go.temporal.io/server/common/nexus/nexusrpc"
"google.golang.org/protobuf/types/known/emptypb"
"google.golang.org/protobuf/types/known/timestamppb"
)
type Workflow struct {
chasm.UnimplementedComponent
// For now, workflow state is managed by mutable_state_impl, not CHASM engine, leaving it empty as CHASM expects a
// state object.
*emptypb.Empty
// MSPointer is a special in-memory field for accessing the underlying mutable state.
chasm.MSPointer
// Callbacks map is used to store the callbacks for the workflow.
Callbacks chasm.Map[string, *callback.Callback]
}
func NewWorkflow(
_ chasm.MutableContext,
msPointer chasm.MSPointer,
) *Workflow {
return &Workflow{
MSPointer: msPointer,
}
}
func (w *Workflow) LifecycleState(
_ chasm.Context,
) chasm.LifecycleState {
// NOTE: closeTransactionHandleRootLifecycleChange() is bypassed in tree.go
//
// NOTE: detached mode is not implemented yet, so always return Running here.
// Otherwise, tasks for callback component can't be executed after workflow is closed.
return chasm.LifecycleStateRunning
}
// ProcessCloseCallbacks triggers "WorkflowClosed" callbacks using the CHASM implementation.
// It iterates through all callbacks and schedules WorkflowClosed ones that are in STANDBY state.
func (w *Workflow) ProcessCloseCallbacks(ctx chasm.MutableContext) error {
// Iterate through all callbacks and schedule WorkflowClosed ones
for _, field := range w.Callbacks {
cb := field.Get(ctx)
// Only process callbacks in STANDBY state (not already triggered)
if cb.Status != callbackspb.CALLBACK_STATUS_STANDBY {
continue
}
// Trigger the callback by transitioning to SCHEDULED state
if err := callback.TransitionScheduled.Apply(cb, ctx, callback.EventScheduled{}); err != nil {
return err
}
}
return nil
}
// AddCompletionCallbacks creates completion callbacks using the CHASM implementation.
// maxCallbacksPerWorkflow is the configured maximum number of callbacks allowed per workflow.
func (w *Workflow) AddCompletionCallbacks(
ctx chasm.MutableContext,
eventTime *timestamppb.Timestamp,
requestID string,
completionCallbacks []*commonpb.Callback,
maxCallbacksPerWorkflow int,
) error {
// Check CHASM max callbacks limit
currentCallbackCount := len(w.Callbacks)
if len(completionCallbacks)+currentCallbackCount > maxCallbacksPerWorkflow {
return serviceerror.NewFailedPreconditionf(
"cannot attach more than %d callbacks to a workflow (%d callbacks already attached)",
maxCallbacksPerWorkflow,
currentCallbackCount,
)
}
// Initialize map if needed
if w.Callbacks == nil {
w.Callbacks = make(chasm.Map[string, *callback.Callback], len(completionCallbacks))
}
// Add each callback
for idx, cb := range completionCallbacks {
chasmCB := &callbackspb.Callback{
Links: cb.GetLinks(),
}
switch variant := cb.Variant.(type) {
case *commonpb.Callback_Nexus_:
chasmCB.Variant = &callbackspb.Callback_Nexus_{
Nexus: &callbackspb.Callback_Nexus{
Url: variant.Nexus.GetUrl(),
Header: variant.Nexus.GetHeader(),
},
}
default:
return fmt.Errorf("unsupported callback variant: %T", variant)
}
id := fmt.Sprintf("%s-%d", requestID, idx)
// Create and add callback
callbackObj := callback.NewCallback(requestID, eventTime, &callbackspb.CallbackState{}, chasmCB)
w.Callbacks[id] = chasm.NewComponentField(ctx, callbackObj)
}
return nil
}
func (w *Workflow) GetNexusCompletion(
ctx chasm.Context,
requestID string,
) (nexusrpc.OperationCompletion, error) {
// Retrieve the completion data from the underlying mutable state via MSPointer
return w.MSPointer.GetNexusCompletion(ctx, requestID)
}