Skip to content

Commit d988f0c

Browse files
committed
Incorporate @spolti's review
Signed-off-by: Ricardo Zanini <ricardozanini@gmail.com>
1 parent 0c27f20 commit d988f0c

File tree

3 files changed

+121
-12
lines changed

3 files changed

+121
-12
lines changed

README.md

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ The current status of features implemented in the SDK is listed below:
4141
| [v1.0.0](https://github.com/serverlessworkflow/sdk-go/releases/tag/v1.0.0) | [v0.5](https://github.com/serverlessworkflow/specification/tree/0.5.x) |
4242
| [v2.0.1](https://github.com/serverlessworkflow/sdk-go/releases/tag/v2.0.1) | [v0.6](https://github.com/serverlessworkflow/specification/tree/0.6.x) |
4343
| [v2.1.2](https://github.com/serverlessworkflow/sdk-go/releases/tag/v2.1.2) | [v0.7](https://github.com/serverlessworkflow/specification/tree/0.7.x) |
44-
| [v2.4.1](https://github.com/serverlessworkflow/sdk-go/releases/tag/v2.4.1) | [v0.8](https://github.com/serverlessworkflow/specification/tree/0.8.x) |
44+
| [v2.4.3](https://github.com/serverlessworkflow/sdk-go/releases/tag/v2.4.1) | [v0.8](https://github.com/serverlessworkflow/specification/tree/0.8.x) |
4545
| [v3.0.0](https://github.com/serverlessworkflow/sdk-go/releases/tag/v3.0.0) | [v1.0.0](https://github.com/serverlessworkflow/specification/releases/tag/v1.0.0-alpha5) |
4646

4747
---
@@ -67,7 +67,30 @@ import "github.com/serverlessworkflow/sdk-go/v3/model"
6767
You can now use the SDK types and functions, for example:
6868

6969
```go
70-
myHttpTask := model.CallHTTP{}
70+
package main
71+
72+
import (
73+
"github.com/serverlessworkflow/sdk-go/v3/builder"
74+
"github.com/serverlessworkflow/sdk-go/v3/model"
75+
)
76+
77+
func main() {
78+
workflowBuilder := New().
79+
SetDocument("1.0.0", "examples", "example-workflow", "1.0.0").
80+
AddTask("task1", &model.CallHTTP{
81+
TaskBase: model.TaskBase{
82+
If: &model.RuntimeExpression{Value: "${condition}"},
83+
},
84+
Call: "http",
85+
With: model.HTTPArguments{
86+
Method: "GET",
87+
Endpoint: model.NewEndpoint("http://example.com"),
88+
},
89+
})
90+
workflow, _ := builder.Object(workflowBuilder)
91+
// use your models
92+
}
93+
7194
```
7295

7396
### Parsing Workflow Files

model/validator.go

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,15 @@ package model
1717
import (
1818
"errors"
1919
"fmt"
20-
"regexp"
21-
2220
"github.com/go-playground/validator/v10"
21+
"regexp"
22+
"strings"
2323
)
2424

2525
var (
26-
iso8601DurationPattern = regexp.MustCompile(`^P(\d+Y)?(\d+M)?(\d+W)?(\d+D)?(T(\d+H)?(\d+M)?(\d+S)?)?$`)
26+
iso8601DurationPattern = regexp.MustCompile(`^P(\d+Y)?(\d+M)?(\d+D)?(T(\d+H)?(\d+M)?(\d+S)?)?$`)
2727
semanticVersionPattern = regexp.MustCompile(`^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$`)
28-
hostnameRFC1123Pattern = regexp.MustCompile(`^[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$`)
28+
hostnameRFC1123Pattern = regexp.MustCompile(`^(([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)*[a-zA-Z]{2,63}|[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)$`)
2929
)
3030

3131
var validate *validator.Validate
@@ -47,11 +47,8 @@ func init() {
4747
registerValidator("client_auth_type", validateOptionalOAuthClientAuthentication)
4848
registerValidator("encoding_type", validateOptionalOAuth2TokenRequestEncoding)
4949

50-
registerValidator("semver_pattern", func(fl validator.FieldLevel) bool {
51-
return semanticVersionPattern.MatchString(fl.Field().String())
52-
})
5350
registerValidator("hostname_rfc1123", func(fl validator.FieldLevel) bool {
54-
return hostnameRFC1123Pattern.MatchString(fl.Field().String())
51+
return isHostnameValid(fl.Field().String())
5552
})
5653
registerValidator("uri_pattern", func(fl validator.FieldLevel) bool {
5754
value, ok := fl.Field().Interface().(string)
@@ -67,6 +64,7 @@ func init() {
6764
}
6865
return LiteralUriTemplatePattern.MatchString(value)
6966
})
67+
registerValidator("semver_pattern", validateSemanticVersion)
7068
registerValidator("iso8601_duration", validateISO8601Duration)
7169

7270
registerValidator("object_or_string", validateObjectOrString)
@@ -349,9 +347,43 @@ func validateJsonPointerOrRuntimeExpr(fl validator.FieldLevel) bool {
349347
}
350348

351349
func validateISO8601Duration(fl validator.FieldLevel) bool {
352-
value, ok := fl.Field().Interface().(string)
350+
input, ok := fl.Field().Interface().(string)
351+
if !ok {
352+
return false
353+
}
354+
355+
return isISO8601DurationValid(input)
356+
}
357+
358+
func validateSemanticVersion(fl validator.FieldLevel) bool {
359+
input, ok := fl.Field().Interface().(string)
353360
if !ok {
354361
return false
355362
}
356-
return iso8601DurationPattern.MatchString(value)
363+
364+
return isSemanticVersionValid(input)
365+
}
366+
367+
// isISO8601DurationValid validates if a string is a valid ISO 8601 duration.
368+
func isISO8601DurationValid(input string) bool {
369+
if !iso8601DurationPattern.MatchString(input) {
370+
return false
371+
}
372+
373+
trimmed := strings.TrimPrefix(input, "P")
374+
if trimmed == "" || trimmed == "T" {
375+
return false
376+
}
377+
378+
return true
379+
}
380+
381+
// isSemanticVersionValid validates if a string is a valid semantic version.
382+
func isSemanticVersionValid(input string) bool {
383+
return semanticVersionPattern.MatchString(input)
384+
}
385+
386+
// isHostnameValid validates if a string is a valid RFC 1123 hostname.
387+
func isHostnameValid(input string) bool {
388+
return hostnameRFC1123Pattern.MatchString(input)
357389
}

model/validator_test.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package model
2+
3+
import (
4+
"testing"
5+
)
6+
7+
func TestRegexValidators(t *testing.T) {
8+
testCases := []struct {
9+
name string
10+
validate func(string) bool
11+
input string
12+
expected bool
13+
}{
14+
// ISO 8601 Duration Tests
15+
{"ISO 8601 Duration Valid 1", isISO8601DurationValid, "P2Y", true},
16+
{"ISO 8601 Duration Valid 2", isISO8601DurationValid, "P1DT12H30M", true},
17+
{"ISO 8601 Duration Valid 3", isISO8601DurationValid, "P1Y2M3D", true},
18+
{"ISO 8601 Duration Valid 4", isISO8601DurationValid, "P1Y2M3D4H", false},
19+
{"ISO 8601 Duration Valid 5", isISO8601DurationValid, "P1Y", true},
20+
{"ISO 8601 Duration Valid 6", isISO8601DurationValid, "PT1H", true},
21+
{"ISO 8601 Duration Valid 7", isISO8601DurationValid, "P1Y2M3D4H5M6S", false},
22+
{"ISO 8601 Duration Invalid 1", isISO8601DurationValid, "P", false},
23+
{"ISO 8601 Duration Invalid 2", isISO8601DurationValid, "P1Y2M3D4H5M6S7", false},
24+
{"ISO 8601 Duration Invalid 3", isISO8601DurationValid, "1Y", false},
25+
26+
// Semantic Versioning Tests
27+
{"Semantic Version Valid 1", isSemanticVersionValid, "1.0.0", true},
28+
{"Semantic Version Valid 2", isSemanticVersionValid, "1.2.3", true},
29+
{"Semantic Version Valid 3", isSemanticVersionValid, "1.2.3-beta", true},
30+
{"Semantic Version Valid 4", isSemanticVersionValid, "1.2.3-beta.1", true},
31+
{"Semantic Version Valid 5", isSemanticVersionValid, "1.2.3-beta.1+build.123", true},
32+
{"Semantic Version Invalid 1", isSemanticVersionValid, "v1.2.3", false},
33+
{"Semantic Version Invalid 2", isSemanticVersionValid, "1.2", false},
34+
{"Semantic Version Invalid 3", isSemanticVersionValid, "1.2.3-beta.x", true},
35+
36+
// RFC 1123 Hostname Tests
37+
{"RFC 1123 Hostname Valid 1", isHostnameValid, "example.com", true},
38+
{"RFC 1123 Hostname Valid 2", isHostnameValid, "my-hostname", true},
39+
{"RFC 1123 Hostname Valid 3", isHostnameValid, "subdomain.example.com", true},
40+
{"RFC 1123 Hostname Invalid 1", isHostnameValid, "127.0.0.1", false},
41+
{"RFC 1123 Hostname Invalid 2", isHostnameValid, "example.com.", false},
42+
{"RFC 1123 Hostname Invalid 3", isHostnameValid, "example..com", false},
43+
{"RFC 1123 Hostname Invalid 4", isHostnameValid, "example.com-", false},
44+
}
45+
46+
for _, tc := range testCases {
47+
t.Run(tc.name, func(t *testing.T) {
48+
result := tc.validate(tc.input)
49+
if result != tc.expected {
50+
t.Errorf("Validation failed for '%s': input='%s', expected=%v, got=%v", tc.name, tc.input, tc.expected, result)
51+
}
52+
})
53+
}
54+
}

0 commit comments

Comments
 (0)