Skip to content
Open
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 57 additions & 16 deletions workflow/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"fmt"
"log"
"reflect"
"regexp"
"runtime"
"strings"

Expand Down Expand Up @@ -90,10 +91,15 @@ func getFunctionName(f interface{}) (string, error) {
}

callSplit := strings.Split(runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name(), ".")

funcName := callSplit[len(callSplit)-1]

if funcName == "1" {
const anonymousFunctionRegexp = "^func[0-9]+$"
isAnonymousFunc, err := regexp.MatchString(anonymousFunctionRegexp, funcName)
if err != nil {
return "", fmt.Errorf("failed to match anonymous function regexp: %w", err)
}

if isAnonymousFunc {
return "", errors.New("anonymous function name")
}

Expand All @@ -107,18 +113,43 @@ func wrapWorkflow(w Workflow) task.Orchestrator {
}
}

type registerOptions struct {
Name string
}

type registerOption func(*registerOptions) error

// RegisterWithName allows you to specify a custom name for the workflow or activity being registered.
// Activities and Workflows registered without an explicit name will use the function name as the name.
func RegisterWithName(name string) registerOption {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since it's wholly within the workflow package I think WithName makes sense, wdyt?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can do!
The main reason was that this same file has some worker options too, that were prefixed with Worker

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair point, I definitely think I should have added it as a wrapped function as it's probably never used other than in testing

return func(opts *registerOptions) error {
opts.Name = name
return nil
}
}

// RegisterWorkflow adds a workflow function to the registry
func (ww *WorkflowWorker) RegisterWorkflow(w Workflow) error {
func (ww *WorkflowWorker) RegisterWorkflow(w Workflow, opts ...registerOption) error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This breaks the func API- please can we create new funcs which take an options.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which API do you think this break?
Technically this is backwards compatible 🤔

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is somewhat compatible however there are some cases if you're implementing it within an interface or using reflection it would be a breaking change as the function signature is distinctly different

I think in the interests of time I would be inclined to agree that a new method with the variadic parameters for the time being would be the best path forwards while a deprecation/upgrade path is defined. Wdyt?

Copy link
Contributor Author

@tscolari tscolari Aug 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't mind adding another function for this,
my only concern is that it will mean that there will be 2 functions doing the same work - and removing one in the future will then indeed cause a breaking change, which means a bigger pain long term. The question is how much would add the optional parameter here affect existing users vs the perpetual ergonomic cost of adding to the api.

wrappedOrchestration := wrapWorkflow(w)

// get the function name for the passed workflow
name, err := getFunctionName(w)
if err != nil {
return fmt.Errorf("failed to get workflow decorator: %v", err)
options := registerOptions{}
for _, opt := range opts {
if err := opt(&options); err != nil {
return fmt.Errorf("failed processing options: %w", err)
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this be extracted into a separate function since it's reused?


if options.Name == "" {
// get the function name for the passed workflow if there's
// no explicit name provided.
name, err := getFunctionName(w)
if err != nil {
return fmt.Errorf("failed to get workflow decorator: %v", err)
}
options.Name = name
}

err = ww.tasks.AddOrchestratorN(name, wrappedOrchestration)
return err
return ww.tasks.AddOrchestratorN(options.Name, wrappedOrchestration)
}

func wrapActivity(a Activity) task.Activity {
Expand All @@ -136,17 +167,27 @@ func wrapActivity(a Activity) task.Activity {
}

// RegisterActivity adds an activity function to the registry
func (ww *WorkflowWorker) RegisterActivity(a Activity) error {
func (ww *WorkflowWorker) RegisterActivity(a Activity, opts ...registerOption) error {
wrappedActivity := wrapActivity(a)

// get the function name for the passed activity
name, err := getFunctionName(a)
if err != nil {
return fmt.Errorf("failed to get activity decorator: %v", err)
options := registerOptions{}
for _, opt := range opts {
if err := opt(&options); err != nil {
return fmt.Errorf("failed processing options: %w", err)
}
}

if options.Name == "" {
// get the function name for the passed workflow if there's
// no explicit name provided.
name, err := getFunctionName(a)
if err != nil {
return fmt.Errorf("failed to get activity decorator: %v", err)
}
options.Name = name
}

err = ww.tasks.AddActivityN(name, wrappedActivity)
return err
return ww.tasks.AddActivityN(options.Name, wrappedActivity)
}

// Start initialises a non-blocking worker to handle workflows and activities registered
Expand Down
Loading