Skip to content

Commit 3394369

Browse files
author
Thomas Osterbind
committed
feat: added format validators for datetime, date, email, ipv4/6 and some others
1 parent 5689bf7 commit 3394369

File tree

3 files changed

+160
-6
lines changed

3 files changed

+160
-6
lines changed

keywords_format.go

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
package jsonschema
2+
3+
import (
4+
"fmt"
5+
"net"
6+
"regexp"
7+
"strings"
8+
"time"
9+
)
10+
11+
const (
12+
email string = "^(((([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+(\\.([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+)*)|((\\x22)((((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(([\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(\\([\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}]))))*(((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(\\x22)))@((([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])([a-zA-Z]|\\d|-|\\.|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.)+(([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])([a-zA-Z]|\\d|-|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.?$"
13+
hostname string = `^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$`
14+
)
15+
16+
var (
17+
emailPattern = regexp.MustCompile(email)
18+
hostnamePattern = regexp.MustCompile(hostname)
19+
)
20+
21+
// func FormatType(data interface{}) string {
22+
// switch
23+
// }
24+
// Note: Date and time format names are derived from RFC 3339, section
25+
// 5.6 [RFC3339].
26+
// http://json-schema.org/latest/json-schema-validation.html#RFC3339
27+
28+
type format string
29+
30+
func newFormat() Validator {
31+
return new(format)
32+
}
33+
34+
func (f format) Validate(data interface{}) error {
35+
if str, ok := data.(string); ok {
36+
switch f {
37+
case "date-time":
38+
return isValidDateTime(str)
39+
case "date":
40+
return isValidDate(str)
41+
case "email":
42+
return isValidEmail(str)
43+
case "hostname":
44+
return isValidHostname(str)
45+
case "idn-email":
46+
return isValidIdnEmail(str)
47+
case "idn-hostname":
48+
return isValidIdnHostname(str)
49+
case "ipv4":
50+
return isValidIPv4(str)
51+
case "ipv6":
52+
return isValidIPv6(str)
53+
case "iri-reference":
54+
return isValidIriRef(str)
55+
case "iri":
56+
return isValidIri(str)
57+
case "json-pointer":
58+
return isValidJSONPointer(str)
59+
case "regex":
60+
return isValidRegex(str)
61+
case "relative-json-pointer":
62+
return isValidRelJSONPointer(str)
63+
case "time":
64+
return isValidTime(str)
65+
case "uri-reference":
66+
return isValidURIRef(str)
67+
case "uri-template":
68+
return isValidURITemplate(str)
69+
case "uri":
70+
return isValidURI(str)
71+
}
72+
}
73+
return nil
74+
}
75+
76+
func isValidDateTime(dateTime string) error {
77+
if _, err := time.Parse(time.RFC3339, dateTime); err != nil {
78+
return fmt.Errorf("date-time incorrectly formatted: %s", err.Error())
79+
}
80+
return nil
81+
}
82+
83+
func isValidDate(date string) error {
84+
arbitraryTime := "T08:30:06.283185Z"
85+
dateTime := fmt.Sprintf("%s%s", date, arbitraryTime)
86+
return isValidDateTime(dateTime)
87+
}
88+
89+
func isValidEmail(email string) error {
90+
if !emailPattern.MatchString(email) {
91+
return fmt.Errorf("invalid email format")
92+
}
93+
return nil
94+
}
95+
func isValidHostname(hostname string) error {
96+
if !hostnamePattern.MatchString(hostname) || len(hostname) > 255 {
97+
return fmt.Errorf("invalid hostname string")
98+
}
99+
return nil
100+
}
101+
func isValidIdnEmail(idnEmail string) error {
102+
return nil
103+
}
104+
func isValidIdnHostname(idnHostname string) error {
105+
return nil
106+
}
107+
func isValidIPv4(ipv4 string) error {
108+
parsedIP := net.ParseIP(ipv4)
109+
hasDots := strings.Contains(ipv4, ".")
110+
if !hasDots || parsedIP == nil {
111+
return fmt.Errorf("invalid IPv4 address")
112+
}
113+
return nil
114+
}
115+
116+
func isValidIPv6(ipv6 string) error {
117+
parsedIP := net.ParseIP(ipv6)
118+
hasColons := strings.Contains(ipv6, ":")
119+
if !hasColons || parsedIP == nil {
120+
return fmt.Errorf("invalid IPv4 address")
121+
}
122+
return nil
123+
}
124+
125+
func isValidIriRef(iriRef string) error {
126+
return nil
127+
}
128+
func isValidIri(iri string) error {
129+
return nil
130+
}
131+
func isValidJSONPointer(jsonPointer string) error {
132+
return nil
133+
}
134+
func isValidRegex(regex string) error {
135+
return nil
136+
}
137+
func isValidRelJSONPointer(relJsonPointer string) error {
138+
return nil
139+
}
140+
func isValidTime(time string) error {
141+
return nil
142+
}
143+
func isValidURIRef(uriRef string) error {
144+
return nil
145+
}
146+
func isValidURITemplate(uriTemplate string) error {
147+
return nil
148+
}
149+
func isValidURI(uri string) error {
150+
return nil
151+
}

schema_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -283,19 +283,19 @@ func TestDraft7(t *testing.T) {
283283
// "testdata/draft7/optional/content.json",
284284
// "testdata/draft7/optional/ecmascript-regex.json",
285285
// "testdata/draft7/optional/zeroTerminatedFloats.json",
286-
// "testdata/draft7/optional/format/date-time.json",
287-
// "testdata/draft7/optional/format/hostname.json",
288-
// "testdata/draft7/optional/format/ipv4.json",
286+
"testdata/draft7/optional/format/date-time.json",
287+
"testdata/draft7/optional/format/hostname.json",
288+
"testdata/draft7/optional/format/ipv4.json",
289289
// "testdata/draft7/optional/format/iri.json",
290290
// "testdata/draft7/optional/format/relative-json-pointer.json",
291291
// "testdata/draft7/optional/format/uri-template.json",
292-
// "testdata/draft7/optional/format/date.json",
292+
"testdata/draft7/optional/format/date.json",
293293
// "testdata/draft7/optional/format/idn-email.json",
294-
// "testdata/draft7/optional/format/ipv6.json",
294+
"testdata/draft7/optional/format/ipv6.json",
295295
// "testdata/draft7/optional/format/json-pointer.json",
296296
// "testdata/draft7/optional/format/time.json",
297297
// "testdata/draft7/optional/format/uri.json",
298-
// "testdata/draft7/optional/format/email.json",
298+
"testdata/draft7/optional/format/email.json",
299299
// "testdata/draft7/optional/format/idn-hostname.json",
300300
// "testdata/draft7/optional/format/iri-reference.json",
301301
// "testdata/draft7/optional/format/regex.json",

validate.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,4 +88,7 @@ var DefaultValidators = map[string]ValMaker{
8888
"if": newIif,
8989
"then": newThen,
9090
"else": newEls,
91+
92+
//optional formats
93+
"format": newFormat,
9194
}

0 commit comments

Comments
 (0)