Skip to content

Commit

Permalink
Incorporate @spolti's review
Browse files Browse the repository at this point in the history
Signed-off-by: Ricardo Zanini <[email protected]>
  • Loading branch information
ricardozanini committed Jan 22, 2025
1 parent 0c27f20 commit d988f0c
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 12 deletions.
27 changes: 25 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ The current status of features implemented in the SDK is listed below:
| [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) |
| [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) |
| [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) |
| [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) |
| [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) |
| [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) |

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

```go
myHttpTask := model.CallHTTP{}
package main

import (
"github.com/serverlessworkflow/sdk-go/v3/builder"
"github.com/serverlessworkflow/sdk-go/v3/model"
)

func main() {
workflowBuilder := New().
SetDocument("1.0.0", "examples", "example-workflow", "1.0.0").
AddTask("task1", &model.CallHTTP{
TaskBase: model.TaskBase{
If: &model.RuntimeExpression{Value: "${condition}"},
},
Call: "http",
With: model.HTTPArguments{
Method: "GET",
Endpoint: model.NewEndpoint("http://example.com"),
},
})
workflow, _ := builder.Object(workflowBuilder)
// use your models
}

```

### Parsing Workflow Files
Expand Down
52 changes: 42 additions & 10 deletions model/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@ package model
import (
"errors"
"fmt"
"regexp"

"github.com/go-playground/validator/v10"
"regexp"
"strings"
)

var (
iso8601DurationPattern = regexp.MustCompile(`^P(\d+Y)?(\d+M)?(\d+W)?(\d+D)?(T(\d+H)?(\d+M)?(\d+S)?)?$`)
iso8601DurationPattern = regexp.MustCompile(`^P(\d+Y)?(\d+M)?(\d+D)?(T(\d+H)?(\d+M)?(\d+S)?)?$`)
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-]+)*))?$`)
hostnameRFC1123Pattern = regexp.MustCompile(`^[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$`)
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])?)$`)
)

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

registerValidator("semver_pattern", func(fl validator.FieldLevel) bool {
return semanticVersionPattern.MatchString(fl.Field().String())
})
registerValidator("hostname_rfc1123", func(fl validator.FieldLevel) bool {
return hostnameRFC1123Pattern.MatchString(fl.Field().String())
return isHostnameValid(fl.Field().String())
})
registerValidator("uri_pattern", func(fl validator.FieldLevel) bool {
value, ok := fl.Field().Interface().(string)
Expand All @@ -67,6 +64,7 @@ func init() {
}
return LiteralUriTemplatePattern.MatchString(value)
})
registerValidator("semver_pattern", validateSemanticVersion)
registerValidator("iso8601_duration", validateISO8601Duration)

registerValidator("object_or_string", validateObjectOrString)
Expand Down Expand Up @@ -349,9 +347,43 @@ func validateJsonPointerOrRuntimeExpr(fl validator.FieldLevel) bool {
}

func validateISO8601Duration(fl validator.FieldLevel) bool {
value, ok := fl.Field().Interface().(string)
input, ok := fl.Field().Interface().(string)
if !ok {
return false
}

return isISO8601DurationValid(input)
}

func validateSemanticVersion(fl validator.FieldLevel) bool {
input, ok := fl.Field().Interface().(string)
if !ok {
return false
}
return iso8601DurationPattern.MatchString(value)

return isSemanticVersionValid(input)
}

// isISO8601DurationValid validates if a string is a valid ISO 8601 duration.
func isISO8601DurationValid(input string) bool {
if !iso8601DurationPattern.MatchString(input) {
return false
}

trimmed := strings.TrimPrefix(input, "P")
if trimmed == "" || trimmed == "T" {
return false
}

return true
}

// isSemanticVersionValid validates if a string is a valid semantic version.
func isSemanticVersionValid(input string) bool {
return semanticVersionPattern.MatchString(input)
}

// isHostnameValid validates if a string is a valid RFC 1123 hostname.
func isHostnameValid(input string) bool {
return hostnameRFC1123Pattern.MatchString(input)
}
54 changes: 54 additions & 0 deletions model/validator_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package model

import (
"testing"
)

func TestRegexValidators(t *testing.T) {
testCases := []struct {
name string
validate func(string) bool
input string
expected bool
}{
// ISO 8601 Duration Tests
{"ISO 8601 Duration Valid 1", isISO8601DurationValid, "P2Y", true},
{"ISO 8601 Duration Valid 2", isISO8601DurationValid, "P1DT12H30M", true},
{"ISO 8601 Duration Valid 3", isISO8601DurationValid, "P1Y2M3D", true},
{"ISO 8601 Duration Valid 4", isISO8601DurationValid, "P1Y2M3D4H", false},
{"ISO 8601 Duration Valid 5", isISO8601DurationValid, "P1Y", true},
{"ISO 8601 Duration Valid 6", isISO8601DurationValid, "PT1H", true},
{"ISO 8601 Duration Valid 7", isISO8601DurationValid, "P1Y2M3D4H5M6S", false},
{"ISO 8601 Duration Invalid 1", isISO8601DurationValid, "P", false},
{"ISO 8601 Duration Invalid 2", isISO8601DurationValid, "P1Y2M3D4H5M6S7", false},
{"ISO 8601 Duration Invalid 3", isISO8601DurationValid, "1Y", false},

// Semantic Versioning Tests
{"Semantic Version Valid 1", isSemanticVersionValid, "1.0.0", true},
{"Semantic Version Valid 2", isSemanticVersionValid, "1.2.3", true},
{"Semantic Version Valid 3", isSemanticVersionValid, "1.2.3-beta", true},
{"Semantic Version Valid 4", isSemanticVersionValid, "1.2.3-beta.1", true},
{"Semantic Version Valid 5", isSemanticVersionValid, "1.2.3-beta.1+build.123", true},
{"Semantic Version Invalid 1", isSemanticVersionValid, "v1.2.3", false},
{"Semantic Version Invalid 2", isSemanticVersionValid, "1.2", false},
{"Semantic Version Invalid 3", isSemanticVersionValid, "1.2.3-beta.x", true},

// RFC 1123 Hostname Tests
{"RFC 1123 Hostname Valid 1", isHostnameValid, "example.com", true},
{"RFC 1123 Hostname Valid 2", isHostnameValid, "my-hostname", true},
{"RFC 1123 Hostname Valid 3", isHostnameValid, "subdomain.example.com", true},
{"RFC 1123 Hostname Invalid 1", isHostnameValid, "127.0.0.1", false},
{"RFC 1123 Hostname Invalid 2", isHostnameValid, "example.com.", false},
{"RFC 1123 Hostname Invalid 3", isHostnameValid, "example..com", false},
{"RFC 1123 Hostname Invalid 4", isHostnameValid, "example.com-", false},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
result := tc.validate(tc.input)
if result != tc.expected {
t.Errorf("Validation failed for '%s': input='%s', expected=%v, got=%v", tc.name, tc.input, tc.expected, result)
}
})
}
}

0 comments on commit d988f0c

Please sign in to comment.