Skip to content

Commit 9592ebc

Browse files
authored
Options validation removal (#102)
Service already validates all the passed options. Validating them on the client leads to situations when workflows fail due to a bad option when they could be recovered. And even worse it leads to situations when validation logic on the client gets out of sync with the service. Also fixed TestWorkflowEnvironment to support mock testing of activities by their string name without any registration. Also removed initialization of a session worker in TestWorkflowEnvironment unless requested.
1 parent 6d109bf commit 9592ebc

12 files changed

+283
-235
lines changed

internal/client.go

+6-6
Original file line numberDiff line numberDiff line change
@@ -551,13 +551,13 @@ const (
551551

552552
// NewClient creates an instance of a workflow client
553553
func NewClient(options ClientOptions) (Client, error) {
554-
if len(options.Namespace) == 0 {
554+
if options.Namespace == "" {
555555
options.Namespace = DefaultNamespace
556556
}
557557

558558
options.MetricsScope = tagScope(options.MetricsScope, tagNamespace, options.Namespace, clientImplHeaderName, clientImplHeaderValue)
559559

560-
if len(options.HostPort) == 0 {
560+
if options.HostPort == "" {
561561
options.HostPort = LocalHostPort
562562
}
563563

@@ -581,11 +581,11 @@ func NewClient(options ClientOptions) (Client, error) {
581581
// NewServiceClient creates workflow client from workflowservice.WorkflowServiceClient. Must be used internally in unit tests only.
582582
func NewServiceClient(workflowServiceClient workflowservice.WorkflowServiceClient, connectionCloser io.Closer, options ClientOptions) *WorkflowClient {
583583
// Namespace can be empty in unit tests.
584-
if len(options.Namespace) == 0 {
584+
if options.Namespace == "" {
585585
options.Namespace = DefaultNamespace
586586
}
587587

588-
if len(options.Identity) == 0 {
588+
if options.Identity == "" {
589589
options.Identity = getWorkerIdentity("")
590590
}
591591

@@ -616,7 +616,7 @@ func NewServiceClient(workflowServiceClient workflowservice.WorkflowServiceClien
616616
func NewNamespaceClient(options ClientOptions) (NamespaceClient, error) {
617617
options.MetricsScope = tagScope(options.MetricsScope, clientImplHeaderName, clientImplHeaderValue)
618618

619-
if len(options.HostPort) == 0 {
619+
if options.HostPort == "" {
620620
options.HostPort = LocalHostPort
621621
}
622622

@@ -638,7 +638,7 @@ func NewNamespaceClient(options ClientOptions) (NamespaceClient, error) {
638638
}
639639

640640
func newNamespaceServiceClient(workflowServiceClient workflowservice.WorkflowServiceClient, clientConn *grpc.ClientConn, options ClientOptions) NamespaceClient {
641-
if len(options.Identity) == 0 {
641+
if options.Identity == "" {
642642
options.Identity = getWorkerIdentity("")
643643
}
644644

internal/internal_activity.go

-64
Original file line numberDiff line numberDiff line change
@@ -170,39 +170,6 @@ func getLocalActivityOptions(ctx Context) *localActivityOptions {
170170
return opts.(*localActivityOptions)
171171
}
172172

173-
func getValidatedActivityOptions(ctx Context) (*activityOptions, error) {
174-
p := getActivityOptions(ctx)
175-
if p == nil {
176-
// We need task list as a compulsory parameter. This can be removed after registration
177-
return nil, errActivityParamsBadRequest
178-
}
179-
if p.TaskListName == "" {
180-
// We default to origin task list name.
181-
p.TaskListName = p.OriginalTaskListName
182-
}
183-
if p.ScheduleToStartTimeoutSeconds <= 0 {
184-
return nil, errors.New("missing or negative ScheduleToStartTimeoutSeconds")
185-
}
186-
if p.StartToCloseTimeoutSeconds <= 0 {
187-
return nil, errors.New("missing or negative StartToCloseTimeoutSeconds")
188-
}
189-
if p.ScheduleToCloseTimeoutSeconds < 0 {
190-
return nil, errors.New("missing or negative ScheduleToCloseTimeoutSeconds")
191-
}
192-
if p.ScheduleToCloseTimeoutSeconds == 0 {
193-
// This is a optional parameter, we default to sum of the other two timeouts.
194-
p.ScheduleToCloseTimeoutSeconds = p.ScheduleToStartTimeoutSeconds + p.StartToCloseTimeoutSeconds
195-
}
196-
if p.HeartbeatTimeoutSeconds < 0 {
197-
return nil, errors.New("invalid negative HeartbeatTimeoutSeconds")
198-
}
199-
if err := validateRetryPolicy(p.RetryPolicy); err != nil {
200-
return nil, err
201-
}
202-
203-
return p, nil
204-
}
205-
206173
func getValidatedLocalActivityOptions(ctx Context) (*localActivityOptions, error) {
207174
p := getLocalActivityOptions(ctx)
208175
if p == nil {
@@ -215,37 +182,6 @@ func getValidatedLocalActivityOptions(ctx Context) (*localActivityOptions, error
215182
return p, nil
216183
}
217184

218-
func validateRetryPolicy(p *commonpb.RetryPolicy) error {
219-
if p == nil {
220-
return nil
221-
}
222-
223-
if p.GetInitialIntervalInSeconds() <= 0 {
224-
return errors.New("missing or negative InitialIntervalInSeconds on retry policy")
225-
}
226-
if p.GetMaximumIntervalInSeconds() < 0 {
227-
return errors.New("negative MaximumIntervalInSeconds on retry policy is invalid")
228-
}
229-
if p.GetMaximumIntervalInSeconds() == 0 {
230-
// if not set, default to 100x of initial interval
231-
p.MaximumIntervalInSeconds = 100 * p.GetInitialIntervalInSeconds()
232-
}
233-
if p.GetMaximumAttempts() < 0 {
234-
return errors.New("negative MaximumAttempts on retry policy is invalid")
235-
}
236-
if p.GetExpirationIntervalInSeconds() < 0 {
237-
return errors.New("ExpirationIntervalInSeconds cannot be less than 0 on retry policy")
238-
}
239-
if p.GetBackoffCoefficient() < 1 {
240-
return errors.New("BackoffCoefficient on retry policy cannot be less than 1.0")
241-
}
242-
if p.GetMaximumAttempts() == 0 && p.GetExpirationIntervalInSeconds() == 0 {
243-
return errors.New("both MaximumAttempts and ExpirationIntervalInSeconds on retry policy are not set, at least one of them must be set")
244-
}
245-
246-
return nil
247-
}
248-
249185
func validateFunctionArgs(f interface{}, args []interface{}, isWorkflow bool) error {
250186
fType := reflect.TypeOf(f)
251187
if fType == nil || fType.Kind() != reflect.Func {

internal/internal_coroutines_test.go

+4-6
Original file line numberDiff line numberDiff line change
@@ -1256,17 +1256,15 @@ func TestChainedFuture(t *testing.T) {
12561256
activityFn := func(arg int) (int, error) {
12571257
return arg, nil
12581258
}
1259-
workflowFn := func(ctx Context) (int, error) {
1259+
workflowFn := func(ctx Context) (out int, err error) {
12601260
ctx = WithActivityOptions(ctx, ActivityOptions{
1261-
ScheduleToStartTimeout: time.Minute,
1262-
StartToCloseTimeout: time.Minute,
1261+
ScheduleToCloseTimeout: time.Minute,
12631262
})
12641263
f := ExecuteActivity(ctx, activityFn, 5)
1265-
var out int
12661264
fut, set := NewFuture(ctx)
12671265
set.Chain(f)
1268-
require.NoError(t, fut.Get(ctx, &out))
1269-
return out, nil
1266+
err = fut.Get(ctx, &out)
1267+
return
12701268
}
12711269

12721270
s := WorkflowTestSuite{}

internal/internal_task_handlers.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -598,8 +598,8 @@ func (wth *workflowTaskHandlerImpl) createWorkflowContext(task *workflowservice.
598598
return nil, errors.New("first history event is not WorkflowExecutionStarted")
599599
}
600600
taskList := attributes.TaskList
601-
if taskList == nil {
602-
return nil, errors.New("nil TaskList in WorkflowExecutionStarted event")
601+
if taskList == nil || taskList.Name == "" {
602+
return nil, errors.New("nil or empty TaskList in WorkflowExecutionStarted event")
603603
}
604604

605605
runID := task.WorkflowExecution.GetRunId()

internal/internal_worker.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ func verifyNamespaceExist(client workflowservice.WorkflowServiceClient, namespac
257257
return nil
258258
}
259259

260-
if len(namespace) == 0 {
260+
if namespace == "" {
261261
return errors.New("namespace cannot be empty")
262262
}
263263

@@ -1536,7 +1536,7 @@ func setClientDefaults(client *WorkflowClient) {
15361536
if client.dataConverter == nil {
15371537
client.dataConverter = getDefaultDataConverter()
15381538
}
1539-
if len(client.namespace) == 0 {
1539+
if client.namespace == "" {
15401540
client.namespace = DefaultNamespace
15411541
}
15421542
if client.tracer == nil {

internal/internal_workflow.go

-44
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ import (
3636
"time"
3737
"unicode"
3838

39-
"github.com/robfig/cron"
4039
"go.uber.org/atomic"
4140
"go.uber.org/zap"
4241

@@ -1150,49 +1149,6 @@ func getValidatedWorkflowFunction(workflowFunc interface{}, args []interface{},
11501149
return &WorkflowType{Name: fnName}, input, nil
11511150
}
11521151

1153-
func getValidatedWorkflowOptions(ctx Context) (*workflowOptions, error) {
1154-
p := getWorkflowEnvOptions(ctx)
1155-
if p == nil {
1156-
// We need task list as a compulsory parameter. This can be removed after registration
1157-
return nil, errWorkflowOptionBadRequest
1158-
}
1159-
info := GetWorkflowInfo(ctx)
1160-
if p.namespace == "" {
1161-
// default to use current workflow's namespace
1162-
p.namespace = info.Namespace
1163-
}
1164-
if p.taskListName == "" {
1165-
// default to use current workflow's task list
1166-
p.taskListName = info.TaskListName
1167-
}
1168-
if p.taskStartToCloseTimeoutSeconds < 0 {
1169-
return nil, errors.New("missing or negative DecisionTaskStartToCloseTimeout")
1170-
}
1171-
if p.taskStartToCloseTimeoutSeconds == 0 {
1172-
p.taskStartToCloseTimeoutSeconds = defaultDecisionTaskTimeoutInSecs
1173-
}
1174-
if p.executionStartToCloseTimeoutSeconds <= 0 {
1175-
return nil, errors.New("missing or invalid ExecutionStartToCloseTimeout")
1176-
}
1177-
if err := validateRetryPolicy(p.retryPolicy); err != nil {
1178-
return nil, err
1179-
}
1180-
if err := validateCronSchedule(p.cronSchedule); err != nil {
1181-
return nil, err
1182-
}
1183-
1184-
return p, nil
1185-
}
1186-
1187-
func validateCronSchedule(cronSchedule string) error {
1188-
if len(cronSchedule) == 0 {
1189-
return nil
1190-
}
1191-
1192-
_, err := cron.ParseStandard(cronSchedule)
1193-
return err
1194-
}
1195-
11961152
func getWorkflowEnvOptions(ctx Context) *workflowOptions {
11971153
options := ctx.Value(workflowEnvOptionsContextKey)
11981154
if options != nil {

internal/internal_workflow_client.go

+7-36
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,7 @@ var _ Client = (*WorkflowClient)(nil)
5656
var _ NamespaceClient = (*namespaceClient)(nil)
5757

5858
const (
59-
defaultDecisionTaskTimeoutInSecs = 10
60-
defaultGetHistoryTimeoutInSecs = 25
59+
defaultGetHistoryTimeoutInSecs = 65
6160
)
6261

6362
var (
@@ -167,22 +166,8 @@ func (wc *WorkflowClient) StartWorkflow(
167166
workflowID = uuid.NewRandom().String()
168167
}
169168

170-
if options.TaskList == "" {
171-
return nil, errors.New("missing TaskList")
172-
}
173-
174169
executionTimeout := common.Int32Ceil(options.ExecutionStartToCloseTimeout.Seconds())
175-
if executionTimeout <= 0 {
176-
return nil, errors.New("missing or invalid ExecutionStartToCloseTimeout")
177-
}
178-
179170
decisionTaskTimeout := common.Int32Ceil(options.DecisionTaskStartToCloseTimeout.Seconds())
180-
if decisionTaskTimeout < 0 {
181-
return nil, errors.New("negative DecisionTaskStartToCloseTimeout provided")
182-
}
183-
if decisionTaskTimeout == 0 {
184-
decisionTaskTimeout = defaultDecisionTaskTimeoutInSecs
185-
}
186171

187172
// Validate type and its arguments.
188173
workflowType, input, err := getValidatedWorkflowFunction(workflowFunc, args, wc.dataConverter, wc.registry)
@@ -364,22 +349,8 @@ func (wc *WorkflowClient) SignalWithStartWorkflow(ctx context.Context, workflowI
364349
workflowID = uuid.NewRandom().String()
365350
}
366351

367-
if options.TaskList == "" {
368-
return nil, errors.New("missing TaskList")
369-
}
370-
371352
executionTimeout := common.Int32Ceil(options.ExecutionStartToCloseTimeout.Seconds())
372-
if executionTimeout <= 0 {
373-
return nil, errors.New("missing or invalid ExecutionStartToCloseTimeout")
374-
}
375-
376353
decisionTaskTimeout := common.Int32Ceil(options.DecisionTaskStartToCloseTimeout.Seconds())
377-
if decisionTaskTimeout < 0 {
378-
return nil, errors.New("negative DecisionTaskStartToCloseTimeout provided")
379-
}
380-
if decisionTaskTimeout == 0 {
381-
decisionTaskTimeout = defaultDecisionTaskTimeoutInSecs
382-
}
383354

384355
// Validate type and its arguments.
385356
workflowType, input, err := getValidatedWorkflowFunction(workflowFunc, workflowArgs, wc.dataConverter, wc.registry)
@@ -620,7 +591,7 @@ func (wc *WorkflowClient) RecordActivityHeartbeatByID(ctx context.Context,
620591
// - InternalServiceError
621592
// - EntityNotExistError
622593
func (wc *WorkflowClient) ListClosedWorkflow(ctx context.Context, request *workflowservice.ListClosedWorkflowExecutionsRequest) (*workflowservice.ListClosedWorkflowExecutionsResponse, error) {
623-
if len(request.GetNamespace()) == 0 {
594+
if request.GetNamespace() == "" {
624595
request.Namespace = wc.namespace
625596
}
626597
var response *workflowservice.ListClosedWorkflowExecutionsResponse
@@ -644,7 +615,7 @@ func (wc *WorkflowClient) ListClosedWorkflow(ctx context.Context, request *workf
644615
// - InternalServiceError
645616
// - EntityNotExistError
646617
func (wc *WorkflowClient) ListOpenWorkflow(ctx context.Context, request *workflowservice.ListOpenWorkflowExecutionsRequest) (*workflowservice.ListOpenWorkflowExecutionsResponse, error) {
647-
if len(request.GetNamespace()) == 0 {
618+
if request.GetNamespace() == "" {
648619
request.Namespace = wc.namespace
649620
}
650621
var response *workflowservice.ListOpenWorkflowExecutionsResponse
@@ -664,7 +635,7 @@ func (wc *WorkflowClient) ListOpenWorkflow(ctx context.Context, request *workflo
664635

665636
// ListWorkflow implementation
666637
func (wc *WorkflowClient) ListWorkflow(ctx context.Context, request *workflowservice.ListWorkflowExecutionsRequest) (*workflowservice.ListWorkflowExecutionsResponse, error) {
667-
if len(request.GetNamespace()) == 0 {
638+
if request.GetNamespace() == "" {
668639
request.Namespace = wc.namespace
669640
}
670641
var response *workflowservice.ListWorkflowExecutionsResponse
@@ -684,7 +655,7 @@ func (wc *WorkflowClient) ListWorkflow(ctx context.Context, request *workflowser
684655

685656
// ListArchivedWorkflow implementation
686657
func (wc *WorkflowClient) ListArchivedWorkflow(ctx context.Context, request *workflowservice.ListArchivedWorkflowExecutionsRequest) (*workflowservice.ListArchivedWorkflowExecutionsResponse, error) {
687-
if len(request.GetNamespace()) == 0 {
658+
if request.GetNamespace() == "" {
688659
request.Namespace = wc.namespace
689660
}
690661
var response *workflowservice.ListArchivedWorkflowExecutionsResponse
@@ -716,7 +687,7 @@ func (wc *WorkflowClient) ListArchivedWorkflow(ctx context.Context, request *wor
716687

717688
// ScanWorkflow implementation
718689
func (wc *WorkflowClient) ScanWorkflow(ctx context.Context, request *workflowservice.ScanWorkflowExecutionsRequest) (*workflowservice.ScanWorkflowExecutionsResponse, error) {
719-
if len(request.GetNamespace()) == 0 {
690+
if request.GetNamespace() == "" {
720691
request.Namespace = wc.namespace
721692
}
722693
var response *workflowservice.ScanWorkflowExecutionsResponse
@@ -736,7 +707,7 @@ func (wc *WorkflowClient) ScanWorkflow(ctx context.Context, request *workflowser
736707

737708
// CountWorkflow implementation
738709
func (wc *WorkflowClient) CountWorkflow(ctx context.Context, request *workflowservice.CountWorkflowExecutionsRequest) (*workflowservice.CountWorkflowExecutionsResponse, error) {
739-
if len(request.GetNamespace()) == 0 {
710+
if request.GetNamespace() == "" {
740711
request.Namespace = wc.namespace
741712
}
742713
var response *workflowservice.CountWorkflowExecutionsResponse

internal/internal_workflow_client_test.go

+1-29
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ package internal
2727
import (
2828
"context"
2929
"encoding/json"
30-
"errors"
3130
"fmt"
3231
"log"
3332
"os"
@@ -232,7 +231,7 @@ func (s *historyEventIteratorSuite) TestIterator_NoError_EmptyPage() {
232231
s.Equal(1, len(events))
233232
}
234233

235-
func (s *historyEventIteratorSuite) TestIterator_Error() {
234+
func (s *historyEventIteratorSuite) TestIteratorError() {
236235
filterType := filterpb.HistoryEventFilterType_AllEvent
237236
request1 := getGetWorkflowExecutionHistoryRequest(filterType)
238237
response1 := &workflowservice.GetWorkflowExecutionHistoryResponse{
@@ -804,33 +803,6 @@ func (s *workflowClientTestSuite) TestSignalWithStartWorkflow() {
804803
s.Equal(createResponse.GetRunId(), resp.RunID)
805804
}
806805

807-
func (s *workflowClientTestSuite) TestSignalWithStartWorkflow_Error() {
808-
signalName := "my signal"
809-
signalInput := []byte("my signal input")
810-
options := StartWorkflowOptions{}
811-
812-
resp, err := s.client.SignalWithStartWorkflow(context.Background(), workflowID, signalName, signalInput,
813-
options, workflowType)
814-
s.Equal(errors.New("missing TaskList"), err)
815-
s.Nil(resp)
816-
817-
options.TaskList = tasklist
818-
resp, err = s.client.SignalWithStartWorkflow(context.Background(), workflowID, signalName, signalInput,
819-
options, workflowType)
820-
s.NotNil(err)
821-
s.Nil(resp)
822-
823-
options.ExecutionStartToCloseTimeout = timeoutInSeconds
824-
createResponse := &workflowservice.SignalWithStartWorkflowExecutionResponse{
825-
RunId: runID,
826-
}
827-
s.service.EXPECT().SignalWithStartWorkflowExecution(gomock.Any(), gomock.Any(), gomock.Any()).Return(createResponse, nil)
828-
resp, err = s.client.SignalWithStartWorkflow(context.Background(), workflowID, signalName, signalInput,
829-
options, workflowType)
830-
s.Nil(err)
831-
s.Equal(createResponse.GetRunId(), resp.RunID)
832-
}
833-
834806
func (s *workflowClientTestSuite) TestStartWorkflow() {
835807
client, ok := s.client.(*WorkflowClient)
836808
s.True(ok)

0 commit comments

Comments
 (0)