Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add UUID as a well-known string type #217

Merged
merged 1 commit into from
Jun 20, 2019
Merged
Show file tree
Hide file tree
Changes from all 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
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,9 @@ Check the [constraint rule comparison matrix](rule_comparison.md) for language-s

// x must be a valid URI reference (either absolute or relative)
string x = 1 [(validate.rules).string.uri_ref = true];

// x must be a valid UUID (via RFC 4122)
string x = 1 [(validate.rules).string.uuid = true];
```

### Bytes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,14 @@ public static void uriRef(String field, String value) throws ValidationException
}
}

private static final Pattern uuidPattern = Pattern.compile("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$");
public static void uuid(String field, String value) throws ValidationException {
Matcher matcher = uuidPattern.matcher(value);
if (!matcher.matches()) {
throw new ValidationException(field, enquote(value), "should be a valid uuid");
}
}

private static String enquote(String value) {
return "\"" + value + "\"";
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,4 +209,23 @@ public void uriRefWorks() throws ValidationException {
// No Match
assertThatThrownBy(() -> StringValidation.uri("x", "this is not a uri")).isInstanceOf(ValidationException.class);
}

@Test
public void uuidWorks() throws ValidationException {
// Match
StringValidation.uuid("x", "00000000-0000-0000-0000-000000000000");
StringValidation.uuid("x", "b45c0c80-8880-11e9-a5b1-000000000000");
StringValidation.uuid("x", "B45C0C80-8880-11E9-A5B1-000000000000");
StringValidation.uuid("x", "b45c0c80-8880-21e9-a5b1-000000000000");
StringValidation.uuid("x", "B45C0C80-8880-21E9-A5B1-000000000000");
StringValidation.uuid("x", "a3bb189e-8bf9-3888-9912-ace4e6543002");
StringValidation.uuid("x", "A3BB189E-8BF9-3888-9912-ACE4E6543002");
StringValidation.uuid("x", "8b208305-00e8-4460-a440-5e0dcd83bb0a");
StringValidation.uuid("x", "8B208305-00E8-4460-A440-5E0DCD83BB0A");
StringValidation.uuid("x", "a6edc906-2f9f-5fb2-a373-efac406f0ef2");
StringValidation.uuid("x", "A6EDC906-2F9F-5FB2-A373-EFAC406F0EF2");
// No Match
assertThatThrownBy(() -> StringValidation.uuid("x", "foobar")).isInstanceOf(ValidationException.class);
assertThatThrownBy(() -> StringValidation.uuid("x", "ffffffff-ffff-ffff-ffff-fffffffffffff")).isInstanceOf(ValidationException.class);
}
}
1 change: 1 addition & 0 deletions rule_comparison.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
| ipv6 |✅|✅|✅|✅|
| uri |✅|✅|✅|✅|
| uri_ref |✅|✅|✅|✅|
| uuid |✅|✅|❌|✅|

## Bytes
| Constraint Rule | Go | GoGo | C++ | Java |
Expand Down
10 changes: 10 additions & 0 deletions templates/cc/known.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,13 @@ const emailTpl = `
return m._validateHostname(parts[1])
}
`

const uuidTpl = `
func (m {{ .TypeName.Pointer }}) _validateUuid(uuid string) error {
if matched := _{{ .File.InputPath.BaseName }}_uuidPattern.MatchString(uuid); !matched {
return errors.New("invalid uuid format")
}

return nil
}
`
2 changes: 2 additions & 0 deletions templates/cc/msg.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ bool Validate(const {{ class . }}& m, pgv::ValidationMsg* err) {
{{ if needs . "hostname" }}{{ template "hostname" . }}{{ end }}

{{ if needs . "email" }}{{ template "email" . }}{{ end }}

{{ if needs . "uuid" }}{{ template "uuid" . }}{{ end }}
*/}}

`
1 change: 1 addition & 0 deletions templates/cc/register.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ func RegisterModule(tpl *template.Template, params pgs.Parameters) {
template.Must(tpl.New("email").Parse(emailTpl))
template.Must(tpl.New("hostname").Parse(hostTpl))
template.Must(tpl.New("address").Parse(hostTpl))
template.Must(tpl.New("uuid").Parse(uuidTpl))

template.Must(tpl.New("enum").Parse(enumTpl))
template.Must(tpl.New("message").Parse(messageTpl))
Expand Down
7 changes: 7 additions & 0 deletions templates/cc/string.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,13 @@ const strTpl = `
return {{ errCause . "err" "value must be a valid URI" }}
}
*/}}
{{ else if $r.GetUuid }}
{{ unimplemented }}
{{/* TODO(akonradi) implement UUID constraints
if err := m._validateUuid({{ accessor . }}); err != nil {
return {{ errCause . "err" "value must be a valid UUID" }}
}
*/}}
{{ end }}

{{ if $r.Pattern }}
Expand Down
3 changes: 3 additions & 0 deletions templates/go/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ var (
{{ end }}
)

// define the regex for a UUID once up-front
var _{{ .File.InputPath.BaseName }}_uuidPattern = regexp.MustCompile("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$")

{{ range .AllMessages }}
{{ template "msg" . }}
{{ end }}
Expand Down
3 changes: 3 additions & 0 deletions templates/gogo/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ var (
{{ end }}
)

// define the regex for a UUID once up-front
var _{{ .File.InputPath.BaseName }}_uuidPattern = regexp.MustCompile("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$")

{{ range .AllMessages }}
{{ template "msg" . }}
{{ end }}
Expand Down
10 changes: 10 additions & 0 deletions templates/goshared/known.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,13 @@ const emailTpl = `
return m._validateHostname(parts[1])
}
`

const uuidTpl = `
func (m {{ (msgTyp .).Pointer }}) _validateUuid(uuid string) error {
if matched := _{{ .File.InputPath.BaseName }}_uuidPattern.MatchString(uuid); !matched {
return errors.New("invalid uuid format")
}

return nil
}
`
2 changes: 2 additions & 0 deletions templates/goshared/msg.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ func (m {{ (msgTyp .).Pointer }}) Validate() error {

{{ if needs . "email" }}{{ template "email" . }}{{ end }}

{{ if needs . "uuid" }}{{ template "uuid" . }}{{ end }}

{{ cmt (errname .) " is the validation error returned by " (msgTyp .) ".Validate if the designated constraints aren't met." -}}
type {{ errname . }} struct {
field string
Expand Down
1 change: 1 addition & 0 deletions templates/goshared/register.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ func Register(tpl *template.Template, params pgs.Parameters) {
template.Must(tpl.New("email").Parse(emailTpl))
template.Must(tpl.New("hostname").Parse(hostTpl))
template.Must(tpl.New("address").Parse(hostTpl))
template.Must(tpl.New("uuid").Parse(uuidTpl))

template.Must(tpl.New("enum").Parse(enumTpl))
template.Must(tpl.New("repeated").Parse(repTpl))
Expand Down
4 changes: 4 additions & 0 deletions templates/goshared/string.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@ const strTpl = `
if _, err := url.Parse({{ accessor . }}); err != nil {
return {{ errCause . "err" "value must be a valid URI" }}
}
{{ else if $r.GetUuid }}
if err := m._validateUuid({{ accessor . }}); err != nil {
return {{ errCause . "err" "value must be a valid UUID" }}
}
{{ end }}

{{ if $r.Pattern }}
Expand Down
3 changes: 3 additions & 0 deletions templates/java/string.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,7 @@ const stringTpl = `{{ $f := .Field }}{{ $r := .Rules -}}
{{- if $r.GetUriRef }}
io.envoyproxy.pgv.StringValidation.uriRef("{{ $f.FullyQualifiedName }}", {{ accessor . }});
{{- end -}}
{{- if $r.GetUuid }}
io.envoyproxy.pgv.StringValidation.uuid("{{ $f.FullyQualifiedName }}", {{ accessor . }});
{{- end -}}
`
5 changes: 5 additions & 0 deletions templates/shared/well_known.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ type WellKnown string
const (
Email WellKnown = "email"
Hostname WellKnown = "hostname"
UUID WellKnown = "uuid"
)

// Needs returns true if a well-known string validator is needed for this
Expand Down Expand Up @@ -56,6 +57,10 @@ func strRulesNeeds(rules *validate.StringRules, wk WellKnown) bool {
if rules.GetEmail() || rules.GetHostname() || rules.GetAddress() {
return true
}
case UUID:
if rules.GetUuid() {
return true
}
}

return false
Expand Down
1 change: 1 addition & 0 deletions tests/harness/cases/strings.proto
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,4 @@ message StringIPv4 { string val = 1 [(validate.rules).string.ipv4 = tr
message StringIPv6 { string val = 1 [(validate.rules).string.ipv6 = true]; }
message StringURI { string val = 1 [(validate.rules).string.uri = true]; }
message StringURIRef { string val = 1 [(validate.rules).string.uri_ref = true]; }
message StringUUID { string val = 1 [(validate.rules).string.uuid = true]; }
14 changes: 14 additions & 0 deletions tests/harness/executor/cases.go
Original file line number Diff line number Diff line change
Expand Up @@ -843,6 +843,20 @@ var stringCases = []TestCase{
{"string - URI - valid", &cases.StringURIRef{Val: "http://example.com/foo/bar?baz=quux"}, true},
{"string - URI - valid (relative)", &cases.StringURIRef{Val: "/foo/bar?baz=quux"}, true},
{"string - URI - invalid", &cases.StringURIRef{Val: "!@#$%^&*%$#"}, false},

{"string - UUID - valid (nil)", &cases.StringUUID{Val: "00000000-0000-0000-0000-000000000000"}, true},
{"string - UUID - valid (v1)", &cases.StringUUID{Val: "b45c0c80-8880-11e9-a5b1-000000000000"}, true},
{"string - UUID - valid (v1 - case-insensitive)", &cases.StringUUID{Val: "B45C0C80-8880-11E9-A5B1-000000000000"}, true},
{"string - UUID - valid (v2)", &cases.StringUUID{Val: "b45c0c80-8880-21e9-a5b1-000000000000"}, true},
{"string - UUID - valid (v2 - case-insensitive)", &cases.StringUUID{Val: "B45C0C80-8880-21E9-A5B1-000000000000"}, true},
{"string - UUID - valid (v3)", &cases.StringUUID{Val: "a3bb189e-8bf9-3888-9912-ace4e6543002"}, true},
{"string - UUID - valid (v3 - case-insensitive)", &cases.StringUUID{Val: "A3BB189E-8BF9-3888-9912-ACE4E6543002"}, true},
{"string - UUID - valid (v4)", &cases.StringUUID{Val: "8b208305-00e8-4460-a440-5e0dcd83bb0a"}, true},
{"string - UUID - valid (v4 - case-insensitive)", &cases.StringUUID{Val: "8B208305-00E8-4460-A440-5E0DCD83BB0A"}, true},
{"string - UUID - valid (v5)", &cases.StringUUID{Val: "a6edc906-2f9f-5fb2-a373-efac406f0ef2"}, true},
{"string - UUID - valid (v5 - case-insensitive)", &cases.StringUUID{Val: "A6EDC906-2F9F-5FB2-A373-EFAC406F0EF2"}, true},
{"string - UUID - invalid", &cases.StringUUID{Val: "foobar"}, false},
{"string - UUID - invalid (bad UUID)", &cases.StringUUID{Val: "ffffffff-ffff-ffff-ffff-fffffffffffff"}, false},
}

var bytesCases = []TestCase{
Expand Down
Loading