@@ -21,26 +21,99 @@ type FormatCallback func(value string) error
21
21
22
22
// Format represents a format validator registered by either DefineStringFormat or DefineStringFormatCallback
23
23
type Format struct {
24
+ versionedFormats []* versionedFormat
25
+ }
26
+
27
+ type versionedFormat struct {
24
28
regexp * regexp.Regexp
25
29
callback FormatCallback
26
30
}
27
31
32
+ func (format * Format ) add (minMinorVersion uint64 , vFormat * versionedFormat ) {
33
+ if format != nil {
34
+ if format .versionedFormats == nil {
35
+ format .versionedFormats = make ([]* versionedFormat , minMinorVersion + 1 )
36
+ format .versionedFormats [minMinorVersion ] = vFormat
37
+ } else {
38
+ numVersionedFormats := uint64 (len (format .versionedFormats ))
39
+ if minMinorVersion >= numVersionedFormats {
40
+ // grow array
41
+ lastValue := format .versionedFormats [numVersionedFormats - 1 ]
42
+ additionalEntries := make ([]* versionedFormat , minMinorVersion + 1 - numVersionedFormats )
43
+ if lastValue != nil {
44
+ for i := 0 ; i < len (additionalEntries ); i ++ {
45
+ additionalEntries [i ] = lastValue
46
+ }
47
+ }
48
+ format .versionedFormats = append (format .versionedFormats , additionalEntries ... )
49
+ format .versionedFormats [minMinorVersion ] = vFormat
50
+ return
51
+ }
52
+ for i := minMinorVersion ; i < numVersionedFormats ; i ++ {
53
+ format .versionedFormats [i ] = vFormat
54
+ }
55
+ }
56
+ }
57
+ }
58
+
59
+ func (format Format ) get (minorVersion uint64 ) * versionedFormat {
60
+ if format .versionedFormats != nil {
61
+ if minorVersion >= uint64 (len (format .versionedFormats )) {
62
+ return format .versionedFormats [len (format .versionedFormats )- 1 ]
63
+ }
64
+ return format .versionedFormats [minorVersion ]
65
+ }
66
+ return nil
67
+ }
68
+
69
+ func (format Format ) DefinedForMinorVersion (minorVersion uint64 ) bool {
70
+ return format .get (minorVersion ) != nil
71
+ }
72
+
28
73
// SchemaStringFormats allows for validating string formats
29
- var SchemaStringFormats = make (map [string ]Format , 4 )
74
+ var SchemaStringFormats = make (map [string ]* Format , 4 )
75
+ var defaultSchemaStringFormats map [string ]* Format
30
76
31
77
// DefineStringFormat defines a new regexp pattern for a given format
32
- func DefineStringFormat (name string , pattern string ) {
78
+ // Will enforce regexp usage for minor versions of OpenAPI (3.Y.Z)
79
+ func DefineStringFormat (name string , pattern string , options ... SchemaFormatOption ) {
80
+ var schemaFormatOptions SchemaFormatOptions
81
+ for _ , option := range options {
82
+ option (& schemaFormatOptions )
83
+ }
33
84
re , err := regexp .Compile (pattern )
34
85
if err != nil {
35
86
err := fmt .Errorf ("format %q has invalid pattern %q: %w" , name , pattern , err )
36
87
panic (err )
37
88
}
38
- SchemaStringFormats [name ] = Format {regexp : re }
89
+ updateSchemaStringFormats (name , schemaFormatOptions .fromOpenAPIMinorVersion , & versionedFormat {regexp : re })
90
+ }
91
+
92
+ func getSchemaStringFormats (name string , minorVersion uint64 ) * versionedFormat {
93
+ if currentStringFormat , found := SchemaStringFormats [name ]; found {
94
+ return currentStringFormat .get (minorVersion )
95
+ }
96
+ return nil
97
+ }
98
+
99
+ func updateSchemaStringFormats (name string , minMinorVersion uint64 , vFormat * versionedFormat ) {
100
+ if currentStringFormat , found := SchemaStringFormats [name ]; found {
101
+ currentStringFormat .add (minMinorVersion , vFormat )
102
+ return
103
+ }
104
+ var newFormat Format
105
+ newFormat .add (minMinorVersion , vFormat )
106
+ SchemaStringFormats [name ] = & newFormat
39
107
}
40
108
41
109
// DefineStringFormatCallback adds a validation function for a specific schema format entry
42
- func DefineStringFormatCallback (name string , callback FormatCallback ) {
43
- SchemaStringFormats [name ] = Format {callback : callback }
110
+ // Will enforce regexp usage for minor versions of OpenAPI (3.Y.Z)
111
+ func DefineStringFormatCallback (name string , callback FormatCallback , options ... SchemaFormatOption ) {
112
+ var schemaFormatOptions SchemaFormatOptions
113
+ for _ , option := range options {
114
+ option (& schemaFormatOptions )
115
+ }
116
+ updateSchemaStringFormats (name , schemaFormatOptions .fromOpenAPIMinorVersion , & versionedFormat {callback : callback })
44
117
}
45
118
46
119
func validateIP (ip string ) error {
@@ -82,6 +155,51 @@ func validateIPv6(ip string) error {
82
155
return nil
83
156
}
84
157
158
+ // SaveStringFormats allows to save (obtain a deep copy) of your current string formats
159
+ // so you can later restore it if needed
160
+ func SaveStringFormats (map [string ]* Format ) map [string ]* Format {
161
+ savedStringFormats := map [string ]* Format {}
162
+ for name , value := range SchemaStringFormats {
163
+ var savedFormat Format
164
+ savedFormat .versionedFormats = make ([]* versionedFormat , len (value .versionedFormats ))
165
+ for index , versionedFormatValue := range value .versionedFormats {
166
+ if versionedFormatValue != nil {
167
+ savedVersionedFormat := versionedFormat {
168
+ regexp : versionedFormatValue .regexp ,
169
+ callback : versionedFormatValue .callback ,
170
+ }
171
+ savedFormat .versionedFormats [index ] = & savedVersionedFormat
172
+ }
173
+ }
174
+ savedStringFormats [name ] = & savedFormat
175
+ }
176
+ return savedStringFormats
177
+ }
178
+
179
+ // RestoreStringFormats allows to restore string format back to default values
180
+ func RestoreStringFormats (formatToRestore map [string ]* Format ) {
181
+ restoredStringFormats := map [string ]* Format {}
182
+ for name , value := range formatToRestore {
183
+ var restoredFormat Format
184
+ restoredFormat .versionedFormats = make ([]* versionedFormat , len (value .versionedFormats ))
185
+ for index , versionedFormatValue := range value .versionedFormats {
186
+ if versionedFormatValue != nil {
187
+ restoredVersionedFormat := versionedFormat {
188
+ regexp : versionedFormatValue .regexp ,
189
+ callback : versionedFormatValue .callback ,
190
+ }
191
+ restoredFormat .versionedFormats [index ] = & restoredVersionedFormat
192
+ }
193
+ }
194
+ restoredStringFormats [name ] = & restoredFormat
195
+ }
196
+ SchemaStringFormats = restoredStringFormats
197
+ }
198
+
199
+ // RestoreDefaultStringFormats allows to restore string format back to default values
200
+ func RestoreDefaultStringFormats () {
201
+ RestoreStringFormats (defaultSchemaStringFormats )
202
+ }
85
203
func init () {
86
204
// Base64
87
205
// The pattern supports base64 and b./ase64url. Padding ('=') is supported.
@@ -93,6 +211,7 @@ func init() {
93
211
// date-time
94
212
DefineStringFormat ("date-time" , `^[0-9]{4}-(0[0-9]|10|11|12)-([0-2][0-9]|30|31)T[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]+)?(Z|(\+|-)[0-9]{2}:[0-9]{2})?$` )
95
213
214
+ defaultSchemaStringFormats = SaveStringFormats (SchemaStringFormats )
96
215
}
97
216
98
217
// DefineIPv4Format opts in ipv4 format validation on top of OAS 3 spec
0 commit comments