Skip to content

Commit 49155eb

Browse files
authored
fix(44): add validate for ISO8601 time duration field (#72)
Signed-off-by: lsytj0413 <511121939@qq.com> Signed-off-by: lsytj0413 <511121939@qq.com>
1 parent 0ec04ff commit 49155eb

File tree

12 files changed

+442
-10
lines changed

12 files changed

+442
-10
lines changed

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ go 1.19
44

55
require (
66
github.com/go-playground/validator/v10 v10.11.0
7+
github.com/senseyeio/duration v0.0.0-20180430131211-7c2a214ada46
78
github.com/stretchr/testify v1.7.0
89
k8s.io/apimachinery v0.25.0
910
sigs.k8s.io/yaml v1.3.0

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
3535
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
3636
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
3737
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
38+
github.com/senseyeio/duration v0.0.0-20180430131211-7c2a214ada46 h1:Dz0HrI1AtNSGCE8LXLLqoZU4iuOJXPWndenCsZfstA8=
39+
github.com/senseyeio/duration v0.0.0-20180430131211-7c2a214ada46/go.mod h1:is8FVkzSi7PYLWEXT5MgWhglFsyyiW8ffxAoJqfuFZo=
3840
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
3941
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
4042
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=

model/delay_state.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Copyright 2022 The Serverless Workflow Specification Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package model
16+
17+
import (
18+
"reflect"
19+
20+
"github.com/go-playground/validator/v10"
21+
22+
val "github.com/serverlessworkflow/sdk-go/v2/validator"
23+
)
24+
25+
func init() {
26+
val.GetValidator().RegisterStructValidation(
27+
DelayStateStructLevelValidation,
28+
DelayState{},
29+
)
30+
}
31+
32+
// DelayState Causes the workflow execution to delay for a specified duration
33+
type DelayState struct {
34+
BaseState
35+
// Amount of time (ISO 8601 format) to delay
36+
TimeDelay string `json:"timeDelay" validate:"required"`
37+
}
38+
39+
// DelayStateStructLevelValidation custom validator for DelayState Struct
40+
func DelayStateStructLevelValidation(structLevel validator.StructLevel) {
41+
delayStateObj := structLevel.Current().Interface().(DelayState)
42+
43+
err := validateISO8601TimeDuration(delayStateObj.TimeDelay)
44+
if err != nil {
45+
structLevel.ReportError(reflect.ValueOf(delayStateObj.TimeDelay), "TimeDelay", "timeDelay", "reqiso8601duration", "")
46+
}
47+
}

model/delay_state_test.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// Copyright 2022 The Serverless Workflow Specification Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package model
16+
17+
import (
18+
"testing"
19+
20+
"github.com/stretchr/testify/assert"
21+
22+
val "github.com/serverlessworkflow/sdk-go/v2/validator"
23+
)
24+
25+
func TestDelayStateStructLevelValidation(t *testing.T) {
26+
type testCase struct {
27+
desp string
28+
delayStateObj DelayState
29+
err string
30+
}
31+
testCases := []testCase{
32+
{
33+
desp: "normal",
34+
delayStateObj: DelayState{
35+
BaseState: BaseState{
36+
Name: "1",
37+
Type: "delay",
38+
},
39+
TimeDelay: "PT5S",
40+
},
41+
err: ``,
42+
},
43+
{
44+
desp: "missing required timeDelay",
45+
delayStateObj: DelayState{
46+
BaseState: BaseState{
47+
Name: "1",
48+
Type: "delay",
49+
},
50+
TimeDelay: "",
51+
},
52+
err: `Key: 'DelayState.TimeDelay' Error:Field validation for 'TimeDelay' failed on the 'required' tag\nKey: 'DelayState.TimeDelay' Error:Field validation for 'TimeDelay' failed on the 'reqiso8601duration' tag`,
53+
},
54+
{
55+
desp: "invalid timeDelay duration",
56+
delayStateObj: DelayState{
57+
BaseState: BaseState{
58+
Name: "1",
59+
Type: "delay",
60+
},
61+
TimeDelay: "P5S",
62+
},
63+
err: `Key: 'DelayState.TimeDelay' Error:Field validation for 'TimeDelay' failed on the 'reqiso8601duration' tag`,
64+
},
65+
}
66+
for _, tc := range testCases {
67+
t.Run(tc.desp, func(t *testing.T) {
68+
err := val.GetValidator().Struct(tc.delayStateObj)
69+
70+
if tc.err != "" {
71+
assert.Error(t, err)
72+
assert.Regexp(t, tc.err, err)
73+
return
74+
}
75+
76+
assert.NoError(t, err)
77+
})
78+
}
79+
}

model/retry.go

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,22 @@
1515
package model
1616

1717
import (
18-
"github.com/serverlessworkflow/sdk-go/v2/util/floatstr"
18+
"reflect"
19+
20+
"github.com/go-playground/validator/v10"
1921
"k8s.io/apimachinery/pkg/util/intstr"
22+
23+
"github.com/serverlessworkflow/sdk-go/v2/util/floatstr"
24+
val "github.com/serverlessworkflow/sdk-go/v2/validator"
2025
)
2126

27+
func init() {
28+
val.GetValidator().RegisterStructValidation(
29+
RetryStructLevelValidation,
30+
Retry{},
31+
)
32+
}
33+
2234
// Retry ...
2335
type Retry struct {
2436
// Unique retry strategy name
@@ -36,3 +48,36 @@ type Retry struct {
3648
// If float type, maximum amount of random time added or subtracted from the delay between each retry relative to total delay (between 0 and 1). If string type, absolute maximum amount of random time added or subtracted from the delay between each retry (ISO 8601 duration format)
3749
Jitter floatstr.Float32OrString `json:"jitter,omitempty" validate:"omitempty,min=0,max=1"`
3850
}
51+
52+
// RetryStructLevelValidation custom validator for Retry Struct
53+
func RetryStructLevelValidation(structLevel validator.StructLevel) {
54+
retryObj := structLevel.Current().Interface().(Retry)
55+
56+
if retryObj.Delay != "" {
57+
err := validateISO8601TimeDuration(retryObj.Delay)
58+
if err != nil {
59+
structLevel.ReportError(reflect.ValueOf(retryObj.Delay), "Delay", "delay", "reqiso8601duration", "")
60+
}
61+
}
62+
63+
if retryObj.MaxDelay != "" {
64+
err := validateISO8601TimeDuration(retryObj.MaxDelay)
65+
if err != nil {
66+
structLevel.ReportError(reflect.ValueOf(retryObj.MaxDelay), "MaxDelay", "maxDelay", "reqiso8601duration", "")
67+
}
68+
}
69+
70+
if retryObj.Increment != "" {
71+
err := validateISO8601TimeDuration(retryObj.Increment)
72+
if err != nil {
73+
structLevel.ReportError(reflect.ValueOf(retryObj.Increment), "Increment", "increment", "reqiso8601duration", "")
74+
}
75+
}
76+
77+
if retryObj.Jitter.Type == floatstr.String && retryObj.Jitter.StrVal != "" {
78+
err := validateISO8601TimeDuration(retryObj.Jitter.StrVal)
79+
if err != nil {
80+
structLevel.ReportError(reflect.ValueOf(retryObj.Jitter.StrVal), "Jitter", "jitter", "reqiso8601duration", "")
81+
}
82+
}
83+
}

model/retry_test.go

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
// Copyright 2022 The Serverless Workflow Specification Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package model
16+
17+
import (
18+
"testing"
19+
20+
"github.com/stretchr/testify/assert"
21+
22+
"github.com/serverlessworkflow/sdk-go/v2/util/floatstr"
23+
val "github.com/serverlessworkflow/sdk-go/v2/validator"
24+
)
25+
26+
func TestRetryStructLevelValidation(t *testing.T) {
27+
type testCase struct {
28+
desp string
29+
retryObj Retry
30+
err string
31+
}
32+
testCases := []testCase{
33+
{
34+
desp: "normal",
35+
retryObj: Retry{
36+
Name: "1",
37+
Delay: "PT5S",
38+
MaxDelay: "PT5S",
39+
Increment: "PT5S",
40+
Jitter: floatstr.FromString("PT5S"),
41+
},
42+
err: ``,
43+
},
44+
{
45+
desp: "missing required name",
46+
retryObj: Retry{
47+
Name: "",
48+
Delay: "PT5S",
49+
MaxDelay: "PT5S",
50+
Increment: "PT5S",
51+
Jitter: floatstr.FromString("PT5S"),
52+
},
53+
err: `Key: 'Retry.Name' Error:Field validation for 'Name' failed on the 'required' tag`,
54+
},
55+
{
56+
desp: "invalid delay duration",
57+
retryObj: Retry{
58+
Name: "1",
59+
Delay: "P5S",
60+
MaxDelay: "PT5S",
61+
Increment: "PT5S",
62+
Jitter: floatstr.FromString("PT5S"),
63+
},
64+
err: `Key: 'Retry.Delay' Error:Field validation for 'Delay' failed on the 'reqiso8601duration' tag`,
65+
},
66+
{
67+
desp: "invdalid max delay duration",
68+
retryObj: Retry{
69+
Name: "1",
70+
Delay: "PT5S",
71+
MaxDelay: "P5S",
72+
Increment: "PT5S",
73+
Jitter: floatstr.FromString("PT5S"),
74+
},
75+
err: `Key: 'Retry.MaxDelay' Error:Field validation for 'MaxDelay' failed on the 'reqiso8601duration' tag`,
76+
},
77+
{
78+
desp: "invalid increment duration",
79+
retryObj: Retry{
80+
Name: "1",
81+
Delay: "PT5S",
82+
MaxDelay: "PT5S",
83+
Increment: "P5S",
84+
Jitter: floatstr.FromString("PT5S"),
85+
},
86+
err: `Key: 'Retry.Increment' Error:Field validation for 'Increment' failed on the 'reqiso8601duration' tag`,
87+
},
88+
{
89+
desp: "invalid jitter duration",
90+
retryObj: Retry{
91+
Name: "1",
92+
Delay: "PT5S",
93+
MaxDelay: "PT5S",
94+
Increment: "PT5S",
95+
Jitter: floatstr.FromString("P5S"),
96+
},
97+
err: `Key: 'Retry.Jitter' Error:Field validation for 'Jitter' failed on the 'reqiso8601duration' tag`,
98+
},
99+
}
100+
101+
for _, tc := range testCases {
102+
t.Run(tc.desp, func(t *testing.T) {
103+
err := val.GetValidator().Struct(tc.retryObj)
104+
105+
if tc.err != "" {
106+
assert.Error(t, err)
107+
assert.Regexp(t, tc.err, err)
108+
return
109+
}
110+
111+
assert.NoError(t, err)
112+
})
113+
}
114+
}

model/states.go

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -127,13 +127,6 @@ func (s *BaseState) GetStateDataFilter() *StateDataFilter { return s.StateDataFi
127127
// GetMetadata ...
128128
func (s *BaseState) GetMetadata() *Metadata { return s.Metadata }
129129

130-
// DelayState Causes the workflow execution to delay for a specified duration
131-
type DelayState struct {
132-
BaseState
133-
// Amount of time (ISO 8601 format) to delay
134-
TimeDelay string `json:"timeDelay" validate:"required"`
135-
}
136-
137130
// EventState This state is used to wait for events from event sources, then consumes them and invoke one or more actions to run in sequence or parallel
138131
type EventState struct {
139132
BaseState

model/util.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import (
2121
"os"
2222
"path/filepath"
2323
"strings"
24+
25+
"github.com/senseyeio/duration"
2426
)
2527

2628
const prefix = "file:/"
@@ -90,3 +92,8 @@ func unmarshalFile(data []byte) (b []byte, err error) {
9092
}
9193
return file, nil
9294
}
95+
96+
func validateISO8601TimeDuration(s string) error {
97+
_, err := duration.ParseISO8601(s)
98+
return err
99+
}

0 commit comments

Comments
 (0)