From 53bb1a13b9b0e2dc0ff4271189ff1ed2bc7574f4 Mon Sep 17 00:00:00 2001 From: Mateusz Filipowicz Date: Sun, 9 Feb 2025 21:08:21 +0100 Subject: [PATCH] feat: generate fields validation and use it when sending requests to API (#7) * feat: generate fields validation and use it when issuing requests to API with soft (default) or hard modes * chore: apply linter fixes * feat: enable field validation on int fields * feat: add validation for ^[\w]+$ fields * feat: add validation for MAC address fields * fix: trim wrappers for all comments * feat: add validation for IPv4, IPv6 and IP(IPv4/IPv6) fields * feat: add validation for numeric, non-zero based fields * fix: one of validation can contain dot (.) sign in values * feat: add second notation of MAC address validation * fix: one of validation can start with ^( and end with )$ * feat: add option to disable validation and use soft validation by default * chore: fix test * docs: add readme about client-side validation --- README.md | 44 ++- codegen/api.go.tmpl | 2 +- codegen/generator.go | 102 +++--- codegen/generator_test.go | 66 ++-- codegen/validation.go | 182 +++++++++++ codegen/validation_test.go | 360 ++++++++++++++++++++++ unifi/account.generated.go | 10 +- unifi/channel_plan.generated.go | 32 +- unifi/device.generated.go | 154 ++++----- unifi/dhcp_option.generated.go | 4 +- unifi/dns_record.go | 8 +- unifi/dpi_app.generated.go | 6 +- unifi/dpi_group.generated.go | 4 +- unifi/dynamic_dns.generated.go | 16 +- unifi/firewall_group.generated.go | 4 +- unifi/firewall_rule.generated.go | 34 +- unifi/heat_map.generated.go | 4 +- unifi/hotspot_2_conf.generated.go | 58 ++-- unifi/hotspot_op.generated.go | 4 +- unifi/map.generated.go | 10 +- unifi/network.generated.go | 228 +++++++------- unifi/port_forward.generated.go | 18 +- unifi/port_profile.generated.go | 46 +-- unifi/radius_profile.generated.go | 14 +- unifi/routing.generated.go | 18 +- unifi/schedule_task.generated.go | 2 +- unifi/setting_broadcast.generated.go | 4 +- unifi/setting_dashboard.generated.go | 4 +- unifi/setting_doh.generated.go | 2 +- unifi/setting_ether_lighting.generated.go | 4 +- unifi/setting_global_ap.generated.go | 20 +- unifi/setting_global_nat.generated.go | 2 +- unifi/setting_global_switch.generated.go | 4 +- unifi/setting_guest_access.generated.go | 38 +-- unifi/setting_ips.generated.go | 22 +- unifi/setting_mgmt.generated.go | 2 +- unifi/setting_netflow.generated.go | 8 +- unifi/setting_ntp.generated.go | 2 +- unifi/setting_radio_ai.generated.go | 22 +- unifi/setting_rsyslogd.generated.go | 2 +- unifi/setting_snmp.generated.go | 2 +- unifi/setting_ssl_inspection.generated.go | 2 +- unifi/setting_super_fwupdate.generated.go | 4 +- unifi/setting_super_mail.generated.go | 2 +- unifi/setting_super_mgmt.generated.go | 10 +- unifi/setting_usg.generated.go | 38 +-- unifi/spatial_record.generated.go | 4 +- unifi/unifi.go | 30 ++ unifi/unifi_test.go | 43 +++ unifi/user.generated.go | 8 +- unifi/user_group.generated.go | 6 +- unifi/validation.go | 81 ++++- unifi/virtual_device.generated.go | 2 +- unifi/wlan.generated.go | 114 +++---- unifi/wlan_group.generated.go | 2 +- 55 files changed, 1326 insertions(+), 588 deletions(-) create mode 100644 codegen/validation.go create mode 100644 codegen/validation_test.go diff --git a/README.md b/README.md index 4dc486b..8ba26f5 100644 --- a/README.md +++ b/README.md @@ -116,6 +116,47 @@ c, err := unifi.NewClient(&unifi.ClientConfig{ }) ``` +### Client-side validation + +The SDK provides basic validation for the API models. It is recommended to use it to ensure that the data you are sending +to the UniFi Controller is correct. The validation is based on the regex and validation rules provided in +the UniFi Controller API specs extracted from the JAR files. + +Client supports 3 modes of validation: +- `soft`, `unifi.SoftValidation` (_default_) - will log a warning if any of the fields are invalid before sending the request, but will not stop the request +- `hard`, `unifi.HardValidation` - will return an error if any of the fields are invalid before sending the request +- `disable`, `unifi.DisableValidation` - will disable validation completely + +To change the validation mode, you can use the `ValidationMode` field in the client configuration: + +```go +c, err := unifi.NewClient(&unifi.ClientConfig{ + ... + ValidationMode: unifi.HardValidation, +}) +``` + +If you use hard validation, you can get access to `unifi.ValidationError` struct, which contains information about the validation errors: + +```go +n := &unifi.Network{ + Name: "my-network", + Purpose: "invalid-purpose", + IPSubnet: "10.0.0.10/24", +} +_, err = c.CreateNetwork(ctx, "default", n) + +if err != nil { + validationError := &unifi.ValidationError{} + errors.As(err, &validationError) + fmt.Printf("Error: %v\n", validationError) + fmt.Printf("Root: %v\n", validationError.Root) +} +``` + +`Root` error is `validator.ValidationErrors` struct from [go-playground/validator](https://pkg.go.dev/github.com/go-playground/validator/v10#ValidationErrors), +which contains detailed information about the validation errors. + ### Examples List all available networks: @@ -141,7 +182,8 @@ user, err := c.CreateUser(ctx, "site-name", &unifi.User{ - [x] Support API Key authentication - [ ] Generate client code for currently generated API structures, for use within or outside the Terraform provider - [ ] Increase test coverage -- [ ] Implement validation for fields and structures +- [x] Implement validation for fields and structures +- [ ] Extend validators for more complex cases - [ ] Add more documentation and examples - [ ] Bugfixing... diff --git a/codegen/api.go.tmpl b/codegen/api.go.tmpl index d964cb7..91dbe33 100644 --- a/codegen/api.go.tmpl +++ b/codegen/api.go.tmpl @@ -1,7 +1,7 @@ {{- $structName := .StructName }} {{ define "field" }} - {{ .FieldName }} {{ if .IsArray }}[]{{end}}{{ .FieldType }} `json:"{{ .JSONName }}{{ if .OmitEmpty }},omitempty{{ end }}"` {{ if .FieldValidation }}// {{ .FieldValidation }}{{ end }} {{- end }} + {{ .FieldName }} {{ if .IsArray }}[]{{end}}{{ .FieldType }} `json:"{{ .JSONName }}{{ if .OmitEmpty }},omitempty{{ end }}"{{if .FieldValidation }} {{ .FieldValidation }}{{ end }}` {{ if .FieldValidationComment }}// {{ .FieldValidationComment }}{{ end }} {{- end }} {{ define "field-customUnmarshalType" }} {{- if eq .CustomUnmarshalType "" }}{{else}} {{ .FieldName }} {{ if .IsArray }}[]{{end}}{{ .CustomUnmarshalType }} `json:"{{ .JSONName }}"`{{ end }} {{- end }} diff --git a/codegen/generator.go b/codegen/generator.go index 6d380df..07a4265 100644 --- a/codegen/generator.go +++ b/codegen/generator.go @@ -94,19 +94,20 @@ type Resource struct { } type FieldInfo struct { - FieldName string - JSONName string - FieldType string - FieldValidation string - OmitEmpty bool - IsArray bool - Fields map[string]*FieldInfo - CustomUnmarshalType string - CustomUnmarshalFunc string + FieldName string + JSONName string + FieldType string + FieldValidation string + FieldValidationComment string + OmitEmpty bool + IsArray bool + Fields map[string]*FieldInfo + CustomUnmarshalType string + CustomUnmarshalFunc string } func NewResource(structName string, resourcePath string) *Resource { - baseType := NewFieldInfo(structName, resourcePath, "struct", "", false, false, "") + baseType := NewFieldInfo(structName, resourcePath, "struct", "", "", false, false, "") resource := &Resource{ StructName: structName, ResourcePath: resourcePath, @@ -122,14 +123,14 @@ func NewResource(structName string, resourcePath string) *Resource { // // This hack is here for stability of the generated code, but can be removed if desired. baseType.Fields = map[string]*FieldInfo{ - " ID": NewFieldInfo("ID", "_id", "string", "", true, false, ""), - " SiteID": NewFieldInfo("SiteID", "site_id", "string", "", true, false, ""), + " ID": NewFieldInfo("ID", "_id", "string", "", "", true, false, ""), + " SiteID": NewFieldInfo("SiteID", "site_id", "string", "", "", true, false, ""), " _Spacer": nil, - " Hidden": NewFieldInfo("Hidden", "attr_hidden", "bool", "", true, false, ""), - " HiddenID": NewFieldInfo("HiddenID", "attr_hidden_id", "string", "", true, false, ""), - " NoDelete": NewFieldInfo("NoDelete", "attr_no_delete", "bool", "", true, false, ""), - " NoEdit": NewFieldInfo("NoEdit", "attr_no_edit", "bool", "", true, false, ""), + " Hidden": NewFieldInfo("Hidden", "attr_hidden", "bool", "", "", true, false, ""), + " HiddenID": NewFieldInfo("HiddenID", "attr_hidden_id", "string", "", "", true, false, ""), + " NoDelete": NewFieldInfo("NoDelete", "attr_no_delete", "bool", "", "", true, false, ""), + " NoEdit": NewFieldInfo("NoEdit", "attr_no_edit", "bool", "", "", true, false, ""), " _Spacer": nil, " _Spacer": nil, @@ -138,38 +139,39 @@ func NewResource(structName string, resourcePath string) *Resource { switch { case resource.IsSetting(): resource.ResourcePath = strcase.ToSnake(strings.TrimPrefix(structName, "Setting")) - baseType.Fields[" Key"] = NewFieldInfo("Key", "key", "string", "", false, false, "") + baseType.Fields[" Key"] = NewFieldInfo("Key", "key", "string", "", "", false, false, "") if resource.StructName == "SettingUsg" { // Removed in v7, retaining for backwards compatibility - baseType.Fields["MdnsEnabled"] = NewFieldInfo("MdnsEnabled", "mdns_enabled", "bool", "", false, false, "") + baseType.Fields["MdnsEnabled"] = NewFieldInfo("MdnsEnabled", "mdns_enabled", "bool", "", "", false, false, "") } case resource.StructName == "Device": - baseType.Fields[" MAC"] = NewFieldInfo("MAC", "mac", "string", "", true, false, "") - baseType.Fields["Adopted"] = NewFieldInfo("Adopted", "adopted", "bool", "", false, false, "") - baseType.Fields["Model"] = NewFieldInfo("Model", "model", "string", "", true, false, "") - baseType.Fields["State"] = NewFieldInfo("State", "state", "DeviceState", "", false, false, "") - baseType.Fields["Type"] = NewFieldInfo("Type", "type", "string", "", true, false, "") + baseType.Fields[" MAC"] = NewFieldInfo("MAC", "mac", "string", createValidations(validation{v: mac}), "", true, false, "") + baseType.Fields["Adopted"] = NewFieldInfo("Adopted", "adopted", "bool", "", "", false, false, "") + baseType.Fields["Model"] = NewFieldInfo("Model", "model", "string", "", "", true, false, "") + baseType.Fields["State"] = NewFieldInfo("State", "state", "DeviceState", "", "", false, false, "") + baseType.Fields["Type"] = NewFieldInfo("Type", "type", "string", "", "", true, false, "") case resource.StructName == "User": - baseType.Fields[" IP"] = NewFieldInfo("IP", "ip", "string", "non-generated field", true, false, "") - baseType.Fields[" DevIdOverride"] = NewFieldInfo("DevIdOverride", "dev_id_override", "int", "non-generated field", true, false, "") + baseType.Fields[" IP"] = NewFieldInfo("IP", "ip", "string", createValidations(validation{v: ip}), "non-generated field", true, false, "") + baseType.Fields[" DevIdOverride"] = NewFieldInfo("DevIdOverride", "dev_id_override", "int", "", "non-generated field", true, false, "") case resource.StructName == "WLAN": // this field removed in v6, retaining for backwards compatibility - baseType.Fields["WLANGroupID"] = NewFieldInfo("WLANGroupID", "wlangroup_id", "string", "", false, false, "") + baseType.Fields["WLANGroupID"] = NewFieldInfo("WLANGroupID", "wlangroup_id", "string", "", "", false, false, "") } return resource } -func NewFieldInfo(fieldName string, jsonName string, fieldType string, fieldValidation string, omitempty bool, isArray bool, customUnmarshalType string) *FieldInfo { +func NewFieldInfo(fieldName, jsonName, fieldType, fieldValidation, fieldValidationComment string, omitempty bool, isArray bool, customUnmarshalType string) *FieldInfo { return &FieldInfo{ - FieldName: fieldName, - JSONName: jsonName, - FieldType: fieldType, - FieldValidation: fieldValidation, - OmitEmpty: omitempty, - IsArray: isArray, - CustomUnmarshalType: customUnmarshalType, + FieldName: fieldName, + JSONName: jsonName, + FieldType: fieldType, + FieldValidation: fieldValidation, + FieldValidationComment: fieldValidationComment, + OmitEmpty: omitempty, + IsArray: isArray, + CustomUnmarshalType: customUnmarshalType, } } @@ -207,7 +209,7 @@ func (r *Resource) fieldInfoFromValidation(name string, validation interface{}) switch validation := validation.(type) { case []interface{}: if len(validation) == 0 { - fieldInfo = NewFieldInfo(fieldName, name, "string", "", false, true, "") + fieldInfo = NewFieldInfo(fieldName, name, "string", "", "", false, true, "") err := r.FieldProcessor(fieldName, fieldInfo) return fieldInfo, err } @@ -229,7 +231,7 @@ func (r *Resource) fieldInfoFromValidation(name string, validation interface{}) case map[string]interface{}: typeName := r.StructName + fieldName - result := NewFieldInfo(fieldName, name, typeName, "", true, false, "") + result := NewFieldInfo(fieldName, name, typeName, "", "", true, false, "") result.Fields = make(map[string]*FieldInfo) for name, fv := range validation { @@ -246,19 +248,19 @@ func (r *Resource) fieldInfoFromValidation(name string, validation interface{}) return result, err case string: - fieldValidation := validation + fieldValidationComment := validation normalized := normalizeValidation(validation) omitEmpty := false switch { case normalized == "falsetrue" || normalized == "truefalse": - fieldInfo = NewFieldInfo(fieldName, name, "bool", "", omitEmpty, false, "") + fieldInfo = NewFieldInfo(fieldName, name, "bool", "", "", omitEmpty, false, "") return fieldInfo, r.FieldProcessor(fieldName, fieldInfo) default: if _, err := strconv.ParseFloat(normalized, 64); err == nil { if normalized == "09" || normalized == "09.09" { - fieldValidation = "" + fieldValidationComment = "" } if strings.Contains(normalized, ".") { @@ -267,12 +269,13 @@ func (r *Resource) fieldInfoFromValidation(name string, validation interface{}) } omitEmpty = true - fieldInfo = NewFieldInfo(fieldName, name, "float64", fieldValidation, omitEmpty, false, "") + fieldInfo = NewFieldInfo(fieldName, name, "float64", "", fieldValidationComment, omitEmpty, false, "") return fieldInfo, r.FieldProcessor(fieldName, fieldInfo) } + fieldValidation := defineFieldValidation(fieldValidationComment) omitEmpty = true - fieldInfo = NewFieldInfo(fieldName, name, "int", fieldValidation, omitEmpty, false, "") + fieldInfo = NewFieldInfo(fieldName, name, "int", fieldValidation, fieldValidationComment, omitEmpty, false, "") fieldInfo.CustomUnmarshalType = "emptyStringInt" return fieldInfo, r.FieldProcessor(fieldName, fieldInfo) } @@ -281,8 +284,9 @@ func (r *Resource) fieldInfoFromValidation(name string, validation interface{}) log.Tracef("normalize %q to %q", validation, normalized) } + fieldValidation := defineFieldValidation(fieldValidationComment) omitEmpty = omitEmpty || (!strings.Contains(validation, "^$") && !strings.HasSuffix(fieldName, "ID")) - fieldInfo = NewFieldInfo(fieldName, name, "string", fieldValidation, omitEmpty, false, "") + fieldInfo = NewFieldInfo(fieldName, name, "string", fieldValidation, fieldValidationComment, omitEmpty, false, "") return fieldInfo, r.FieldProcessor(fieldName, fieldInfo) } @@ -444,14 +448,14 @@ func generateCode(fieldsDir string, outDir string) error { return nil } case "SettingMgmt": - sshKeyField := NewFieldInfo(resource.StructName+"XSshKeys", "x_ssh_keys", "struct", "", false, false, "") + sshKeyField := NewFieldInfo(resource.StructName+"XSshKeys", "x_ssh_keys", "struct", "", "", false, false, "") sshKeyField.Fields = map[string]*FieldInfo{ - "name": NewFieldInfo("Name", "name", "string", "", false, false, ""), - "keyType": NewFieldInfo("KeyType", "type", "string", "", false, false, ""), - "key": NewFieldInfo("Key", "key", "string", "", false, false, ""), - "comment": NewFieldInfo("Comment", "comment", "string", "", false, false, ""), - "date": NewFieldInfo("Date", "date", "string", "", false, false, ""), - "fingerprint": NewFieldInfo("Fingerprint", "fingerprint", "string", "", false, false, ""), + "name": NewFieldInfo("Name", "name", "string", "", "", false, false, ""), + "keyType": NewFieldInfo("KeyType", "type", "string", "", "", false, false, ""), + "key": NewFieldInfo("Key", "key", "string", "", "", false, false, ""), + "comment": NewFieldInfo("Comment", "comment", "string", "", "", false, false, ""), + "date": NewFieldInfo("Date", "date", "string", "", "", false, false, ""), + "fingerprint": NewFieldInfo("Fingerprint", "fingerprint", "string", "", "", false, false, ""), } resource.Types[sshKeyField.FieldName] = sshKeyField diff --git a/codegen/generator_test.go b/codegen/generator_test.go index 80ef045..7d14fa6 100644 --- a/codegen/generator_test.go +++ b/codegen/generator_test.go @@ -50,8 +50,8 @@ func TestFieldInfoFromValidation(t *testing.T) { if fieldInfo.FieldType != c.expectedType { t.Fatalf("expected type %q got %q", c.expectedType, fieldInfo.FieldType) } - if fieldInfo.FieldValidation != c.expectedComment { - t.Fatalf("expected comment %q got %q", c.expectedComment, fieldInfo.FieldValidation) + if fieldInfo.FieldValidationComment != c.expectedComment { + t.Fatalf("expected comment %q got %q", c.expectedComment, fieldInfo.FieldValidationComment) } if fieldInfo.OmitEmpty != c.expectedOmitEmpty { t.Fatalf("expected omitempty %t got %t", c.expectedOmitEmpty, fieldInfo.OmitEmpty) @@ -79,51 +79,51 @@ func TestResourceTypes(t *testing.T) { } ` expectedFields := map[string]*FieldInfo{ - "Note": NewFieldInfo("Note", "note", "string", ".{0,1024}", true, false, ""), - "Date": NewFieldInfo("Date", "date", "string", "^$|^(20[0-9]{2}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])T([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9])Z?$", false, false, ""), - "MAC": NewFieldInfo("MAC", "mac", "string", "^([0-9A-Fa-f]{2}:){5}([0-9A-Fa-f]{2})$", true, false, ""), - "Number": NewFieldInfo("Number", "number", "int", "", true, false, "emptyStringInt"), - "Boolean": NewFieldInfo("Boolean", "boolean", "bool", "", false, false, ""), + "Note": NewFieldInfo("Note", "note", "string", "validate:\"omitempty,gte=0,lte=1024\"", ".{0,1024}", true, false, ""), + "Date": NewFieldInfo("Date", "date", "string", "", "^$|^(20[0-9]{2}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])T([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9])Z?$", false, false, ""), + "MAC": NewFieldInfo("MAC", "mac", "string", "validate:\"omitempty,mac\"", "^([0-9A-Fa-f]{2}:){5}([0-9A-Fa-f]{2})$", true, false, ""), + "Number": NewFieldInfo("Number", "number", "int", "", "", true, false, "emptyStringInt"), + "Boolean": NewFieldInfo("Boolean", "boolean", "bool", "", "", false, false, ""), "NestedType": { - FieldName: "NestedType", - JSONName: "nested_type", - FieldType: "StructNestedType", - FieldValidation: "", - OmitEmpty: true, - IsArray: false, + FieldName: "NestedType", + JSONName: "nested_type", + FieldType: "StructNestedType", + FieldValidationComment: "", + OmitEmpty: true, + IsArray: false, Fields: map[string]*FieldInfo{ - "NestedFieldModified": NewFieldInfo("NestedFieldModified", "nested_field", "string", "^$", false, false, ""), + "NestedFieldModified": NewFieldInfo("NestedFieldModified", "nested_field", "string", "", "^$", false, false, ""), }, }, "NestedTypeArray": { - FieldName: "NestedTypeArray", - JSONName: "nested_type_array", - FieldType: "StructNestedTypeArray", - FieldValidation: "", - OmitEmpty: true, - IsArray: true, + FieldName: "NestedTypeArray", + JSONName: "nested_type_array", + FieldType: "StructNestedTypeArray", + FieldValidationComment: "", + OmitEmpty: true, + IsArray: true, Fields: map[string]*FieldInfo{ - "NestedFieldModified": NewFieldInfo("NestedFieldModified", "nested_field", "string", "^$", false, false, ""), + "NestedFieldModified": NewFieldInfo("NestedFieldModified", "nested_field", "string", "", "^$", false, false, ""), }, }, } expectedStruct := map[string]*FieldInfo{ "Struct": { - FieldName: "Struct", - JSONName: "path", - FieldType: "struct", - FieldValidation: "", - OmitEmpty: false, - IsArray: false, + FieldName: "Struct", + JSONName: "path", + FieldType: "struct", + FieldValidationComment: "", + OmitEmpty: false, + IsArray: false, Fields: map[string]*FieldInfo{ - " ID": NewFieldInfo("ID", "_id", "string", "", true, false, ""), - " SiteID": NewFieldInfo("SiteID", "site_id", "string", "", true, false, ""), + " ID": NewFieldInfo("ID", "_id", "string", "", "", true, false, ""), + " SiteID": NewFieldInfo("SiteID", "site_id", "string", "", "", true, false, ""), " _Spacer": nil, - " Hidden": NewFieldInfo("Hidden", "attr_hidden", "bool", "", true, false, ""), - " HiddenID": NewFieldInfo("HiddenID", "attr_hidden_id", "string", "", true, false, ""), - " NoDelete": NewFieldInfo("NoDelete", "attr_no_delete", "bool", "", true, false, ""), - " NoEdit": NewFieldInfo("NoEdit", "attr_no_edit", "bool", "", true, false, ""), + " Hidden": NewFieldInfo("Hidden", "attr_hidden", "bool", "", "", true, false, ""), + " HiddenID": NewFieldInfo("HiddenID", "attr_hidden_id", "string", "", "", true, false, ""), + " NoDelete": NewFieldInfo("NoDelete", "attr_no_delete", "bool", "", "", true, false, ""), + " NoEdit": NewFieldInfo("NoEdit", "attr_no_edit", "bool", "", "", true, false, ""), " _Spacer": nil, " _Spacer": nil, }, diff --git a/codegen/validation.go b/codegen/validation.go new file mode 100644 index 0000000..b29dc2b --- /dev/null +++ b/codegen/validation.go @@ -0,0 +1,182 @@ +package main + +import ( + "fmt" + "slices" + "strconv" + "strings" +) + +type validator string + +type validation struct { + v validator + params []string +} + +type validationComment string + +type regexSpecialChars string + +const ( + validateTag = "validate" + mac validator = "mac" + ip validator = "ip" + ipv4 validator = "ipv4" + ipv6 validator = "ipv6" + httpUrl validator = "http_url" + oneOf validator = "oneof" + cidr validator = "cidr" + omitempty validator = "omitempty" + length validator = "len" + gte validator = "gte" + lte validator = "lte" + w_regex validator = "w_regex" + numeric_nonzero validator = "numeric_nonzero" + + regexChars regexSpecialChars = "^$*+?()[]{}\\|." +) + +func createValidations(validations ...validation) string { + if len(validations) == 0 { + return "" + } + validators := make([]string, len(validations)+1) + validators[0] = createValidator(omitempty) + for i, v := range validations { + validators[i+1] = createValidator(v.v, v.params...) + } + joinedValidators := strings.Join(validators, ",") + return fmt.Sprintf("%s:\"%s\"", validateTag, joinedValidators) +} + +func createValidator(v validator, params ...string) string { + var filteredParams []string + for _, p := range params { + if p != "" { + filteredParams = append(filteredParams, p) + } + } + if len(filteredParams) == 0 { + return string(v) + } + return fmt.Sprintf("%s=%s", v, strings.Join(filteredParams, " ")) +} + +func (r regexSpecialChars) In(s string, excludedChars string) bool { + for _, c := range r { + if strings.ContainsRune(s, c) && !strings.ContainsRune(excludedChars, c) { + return true + } + } + return false +} + +func (r regexSpecialChars) NotIn(s string, excludedChars string) bool { + return !r.In(s, excludedChars) +} + +func (vc validationComment) HasDefinedLength() bool { + s := string(vc) + formatOk := strings.HasPrefix(s, ".{") && strings.HasSuffix(s, "}") && regexChars.NotIn(s, ".{}") + if formatOk { + sub := s[2 : len(s)-1] + bounds := strings.Split(sub, ",") + if len(bounds) < 1 || len(bounds) > 2 { + return false + } + for _, b := range bounds { + if _, err := strconv.Atoi(b); err != nil { + return false + } + } + return true + } + return false +} + +func (vc validationComment) IsOneOf() bool { + s := string(vc) + trimmed := strings.TrimPrefix(strings.TrimSuffix(s, ")$"), "^(") + return strings.Contains(trimmed, "|") && regexChars.NotIn(trimmed, "|.") +} + +func (vc validationComment) IsWRegex() bool { + s := string(vc) + return slices.Contains([]string{"[\\d\\w]+", "[\\d\\w]*", "[\\w]+", "[\\w]*"}, s) +} + +func (vc validationComment) IsMAC() bool { + s := string(vc) + // there are validations present in both notations, so we need to check for both + return (strings.Contains(s, "([0-9A-Fa-f]{2}:){5}([0-9A-Fa-f]{2})") || strings.Contains(s, "([0-9A-Fa-f]{2}[:]){5}([0-9A-Fa-f]{2})")) && regexChars.NotIn(s, "(){}[]^$") +} + +func (vc validationComment) IsIPv4() bool { + s := string(vc) + return strings.Contains(s, ipv4Regex) && strings.Count(s, "|") == ipv4RegexGroupsCount // last is sanity check if there are no more validation groups than expected +} + +func (vc validationComment) IsIPv6() bool { + s := string(vc) + return strings.Contains(s, ipv6Regex) && strings.Count(s, "|") == ipv6RegexGroupsCount // last is sanity check if there are no more validation groups than expected +} + +func (vc validationComment) IsIP() bool { + s := string(vc) + return strings.Contains(s, ipv4Regex) && strings.Contains(s, ipv6Regex) && strings.Count(s, "|") == (ipv4RegexGroupsCount+ipv6RegexGroupsCount+1) +} + +func (vc validationComment) IsNumericNonZeroBased() bool { + s := string(vc) + return s == numericNonZeroRegex +} + +func trimWrappers(s string) string { + trimmed := strings.TrimSuffix(strings.TrimPrefix(s, "(^$|"), "|^$)") // remove wrapping parenthesis + trimmed = strings.TrimSuffix(strings.TrimPrefix(trimmed, "^$|"), "|^$") // remove ^$ which allows for empty string and is not needed + return trimmed +} + +const ( + ipv4Regex = "(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])" + ipv6Regex = "(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))" + numericNonZeroRegex = "^[1-9][0-9]*$" +) + +var ( + ipv4RegexGroupsCount = strings.Count(ipv4Regex, "|") + ipv6RegexGroupsCount = strings.Count(ipv6Regex, "|") +) + +func defineFieldValidation(rawValidation string) string { + if rawValidation == "" { + return "" + } + rawValidation = trimWrappers(rawValidation) + vc := validationComment(rawValidation) + if vc.IsOneOf() { + trimmed := strings.TrimPrefix(strings.TrimSuffix(rawValidation, ")$"), "^(") + return createValidations(validation{v: oneOf, params: strings.Split(trimmed, "|")}) + } else if vc.HasDefinedLength() { + sub := rawValidation[2 : len(rawValidation)-1] + bounds := strings.Split(sub, ",") + if len(bounds) == 1 { + return createValidations(validation{v: length, params: []string{bounds[0]}}) + } + return createValidations(validation{v: gte, params: []string{bounds[0]}}, validation{v: lte, params: []string{bounds[1]}}) + } else if vc.IsWRegex() { + return createValidations(validation{v: w_regex}) + } else if vc.IsMAC() { + return createValidations(validation{v: mac}) + } else if vc.IsIPv4() { + return createValidations(validation{v: ipv4}) + } else if vc.IsIPv6() { + return createValidations(validation{v: ipv6}) + } else if vc.IsIP() { + return createValidations(validation{v: ip}) + } else if vc.IsNumericNonZeroBased() { + return createValidations(validation{v: numeric_nonzero}) + } + return "" +} diff --git a/codegen/validation_test.go b/codegen/validation_test.go new file mode 100644 index 0000000..556376e --- /dev/null +++ b/codegen/validation_test.go @@ -0,0 +1,360 @@ +package main + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestCreateValidator(t *testing.T) { + t.Parallel() + var testValidator validator = "test" + testCases := []struct { + params []string + expectedValidation string + }{ + {[]string{"vpn", "802.1x", "custom"}, "test=vpn 802.1x custom"}, + {[]string{"vpn"}, "test=vpn"}, + {[]string{}, "test"}, + {[]string{"0"}, "test=0"}, + {[]string{"2-2"}, "test=2-2"}, + {[]string{""}, "test"}, + } + + for _, tc := range testCases { + t.Run(tc.expectedValidation, func(t *testing.T) { + t.Parallel() + a := assert.New(t) + v := validation{testValidator, tc.params} + result := createValidator(v.v, v.params...) + a.Equal(tc.expectedValidation, result) + }) + } +} + +func TestCreateValidations(t *testing.T) { + t.Parallel() + + var testValidator validator = "test" + testCases := []struct { + params [][]string + expectedValidations string + }{ + {[][]string{{"1", "2", "3"}}, "test=1 2 3"}, + {[][]string{{"1", "2", "3"}, {"4", "5", "6"}}, "test=1 2 3,test=4 5 6"}, + {[][]string{{}}, "test"}, + {[][]string{{}, {}}, "test,test"}, + {[][]string{{}, {"1"}, {}}, "test,test=1,test"}, + {[][]string{}, ""}, + } + + for _, c := range testCases { + var expectedValidationTag string + if len(c.params) == 0 { + expectedValidationTag = "" + } else { + expectedValidationTag = fmt.Sprintf("validate:\"omitempty,%s\"", c.expectedValidations) + } + t.Run(expectedValidationTag, func(t *testing.T) { + t.Parallel() + a := assert.New(t) + var validations []validation + for _, params := range c.params { + validations = append(validations, validation{testValidator, params}) + } + result := createValidations(validations...) + a.Equal(expectedValidationTag, result) + }) + } +} + +func TestDefineValidation(t *testing.T) { + t.Parallel() + + testCases := []struct { + validationComment, expected string + }{ + {"a|b", "oneof=a b"}, + {"1|2", "oneof=1 2"}, + {".{1,2}", "gte=1,lte=2"}, + {".{1}", "len=1"}, + {".{1}", "len=1"}, + {"[\\d\\w]+", "w_regex"}, + {"[\\d\\w]*", "w_regex"}, + {"[\\w]+", "w_regex"}, + {"[\\w]*", "w_regex"}, + {"a", ""}, + {".{1}|.{5,6}", ""}, + {"a|.{5,6}", ""}, + } + + for _, c := range testCases { + var fullExpected string + if c.expected == "" { + fullExpected = "" + } else { + fullExpected = fmt.Sprintf("validate:\"omitempty,%s\"", c.expected) + } + t.Run(c.expected, func(t *testing.T) { + t.Parallel() + a := assert.New(t) + result := defineFieldValidation(c.validationComment) + a.Equal(fullExpected, result) + }) + } +} + +func testValidationCommentCheck(t *testing.T, testCases []struct { + validationComment validationComment + expected bool +}, fn func(validationComment) bool, +) { + t.Helper() + for _, c := range testCases { + t.Run(fmt.Sprintf("%s-%t", c.validationComment, c.expected), func(t *testing.T) { + t.Parallel() + a := assert.New(t) + trimmed := trimWrappers(string(c.validationComment)) + // trimmed := string(c.validationComment) //trimWrappers(string(c.validationComment)) + result := fn(validationComment(trimmed)) + a.Equal(c.expected, result) + }) + } +} + +func TestIsOneOfValidation(t *testing.T) { + t.Parallel() + testCases := []struct { + validationComment validationComment + expected bool + }{ + {"", false}, + {"a", false}, + {"a|b", true}, + {"^(a|b)$", true}, + {"1-2|2-3", true}, + {"1_2|2_3", true}, + {"1|2", true}, + {"%|#", true}, + {".|b", true}, + {".|.", true}, + {"a|.", true}, + {"a|.", true}, + {"^a|b", false}, + {"a|b$", false}, + {"(a)|b", false}, + {"[a]|b", false}, + {"[a]|b", false}, + {"a+|b", false}, + {"a*|b", false}, + {"[a]*|b", false}, + {"a?|b", false}, + {"\\w|b", false}, + {"{a}|b", false}, + {".{0,32}", false}, + } + testValidationCommentCheck(t, testCases, func(v validationComment) bool { return v.IsOneOf() }) +} + +func TestStringLengthValidation(t *testing.T) { + t.Parallel() + testCases := []struct { + validationComment validationComment + expected bool + }{ + {"", false}, + {".{1,2}", true}, + {".{0,9999}", true}, + {".{9999}", true}, + {"{9999}", false}, + {"{9999", false}, + {"9999}", false}, + {".{}", false}, + {".{1,2,3}", false}, + {"a", false}, + {"a,b", false}, + {"1,2", false}, + {"1", false}, + {".{1,b}", false}, + {".{a,2}", false}, + {".{a,b}", false}, + {".{1-2}", false}, + {".{1_2,2_3}", false}, + {".{%,#}", false}, + {".{^1,2}", false}, + {".{1,2$", false}, + {".{(1),2}", false}, + {".{[1],2}", false}, + {".{[1],2}", false}, + {".{1+,2}", false}, + {".{1*,2}", false}, + {".{[1]*,2}", false}, + {".{1?,2}", false}, + {".{\\w,2}", false}, + {".{{1},2}", false}, + {".{.,2}", false}, + } + testValidationCommentCheck(t, testCases, func(v validationComment) bool { return v.HasDefinedLength() }) +} + +func TestIsWRegexValidation(t *testing.T) { + t.Parallel() + testCases := []struct { + validationComment validationComment + expected bool + }{ + {"[\\d\\w]+", true}, + {"[\\d\\w]*", true}, + {"[\\w]+", true}, + {"[\\w]*", true}, + {"", false}, + {"a", false}, + {"[\\d]+", false}, + {"[\\d]*", false}, + {"[\\s]+", false}, + {"[\\s]*", false}, + } + testValidationCommentCheck(t, testCases, func(v validationComment) bool { return v.IsWRegex() }) +} + +func TestIsMACValidation(t *testing.T) { + t.Parallel() + testCases := []struct { + validationComment validationComment + expected bool + }{ + {"([0-9A-Fa-f]{2}:){5}([0-9A-Fa-f]{2})", true}, + {"([0-9A-Fa-f]{2}:){5}([0-9A-Fa-f]{2})$", true}, + {"^([0-9A-Fa-f]{2}:){5}([0-9A-Fa-f]{2})$", true}, + {"^([0-9A-Fa-f]{2}:){5}([0-9A-Fa-f]{2})$", true}, + {"^([0-9A-Fa-f]{2}:){5}([0-9A-Fa-f]{2})", true}, + {"^([0-9A-Fa-f]{2}:){5}([0-9A-Fa-f]{2})", true}, + {"^([0-9A-Fa-f]{2}:){5}([0-9A-Fa-f]{2})|^$", true}, + {"^$|^([0-9A-Fa-f]{2}:){5}([0-9A-Fa-f]{2})|^$", true}, + {"^$|^([0-9A-Fa-f]{2}:){5}([0-9A-Fa-f]{2})", true}, + {"(^$|^([0-9A-Fa-f]{2}:){5}([0-9A-Fa-f]{2})|^$)", true}, + + {"[0-9A-Fa-f]{2}:){5}([0-9A-Fa-f]{2}", false}, + {"[0-9A-Fa-f]{2}:){5}([0-9A-Fa-f]{2})", false}, + {"([0-9A-Fa-f]{2}:){5}([0-9A-Fa-f]{2}", false}, + {"^[0-9A-Fa-f]{2}:){5}([0-9A-Fa-f]{2}$", false}, + {"^$|[0-9A-Fa-f]{2}:){5}([0-9A-Fa-f]{2})", false}, + {"^$|[0-9A-Fa-f]{2}:){5}([0-9A-Fa-f]{2})|^$", false}, + {"(^$|[0-9A-Fa-f]{2}:){5}([0-9A-Fa-f]{2})|^$)", false}, + {"^$|[0-9A-Fa-f]{2}:){5}([0-9A-Fa-f]{2})|^$)", false}, + {"(^$|[0-9A-Fa-f]{2}:){5}([0-9A-Fa-f]{2})|^$", false}, + {"", false}, + {"a", false}, + {"([0-9A-Fa-f]{2}:){5}([0-9A-Fa-f]{2})|(0-9)", false}, + {"[0-9]|([0-9A-Fa-f]{2}:){5}([0-9A-Fa-f]{2})", false}, + {"[0-9]|([0-9A-Fa-f]{2}:){5}", false}, + } + testValidationCommentCheck(t, testCases, func(v validationComment) bool { return v.IsMAC() }) +} + +func (vc validationComment) mutate(prefix, suffix string) validationComment { + return validationComment(fmt.Sprintf("%s%s%s", prefix, string(vc), suffix)) +} + +func generateTestCasesForFixedRegex(regex string) []struct { + validationComment validationComment + expected bool +} { + base := validationComment(regex) + return []struct { + validationComment validationComment + expected bool + }{ + {base, true}, + {base.mutate("^", "$"), true}, + {base.mutate("^", ""), true}, + {base.mutate("", "$"), true}, + {base.mutate("(^", "$)"), true}, + {base.mutate("^", "$)"), true}, + {base.mutate("^(", "$"), true}, + {base.mutate("^$|", "|^$"), true}, + {base.mutate("^$|", ""), true}, + {base.mutate("", "|^$"), true}, + {base.mutate("(^$|", "|^$)"), true}, + {base.mutate("(^$|", ""), true}, + {base.mutate("", "|^$)"), true}, + + // FIXME how to handle these cases? current implementation is dumb and quick, and might fail.. + //{base.mutate("^test", ""), false}, + //{base.mutate("^test", "test$"), false}, + //{base.mutate("", "test$"), false}, + //{base.mutate("test", "test"), false}, + //{base.mutate("", "test"), false}, + //{base.mutate("test", ""), false}, + {base.mutate("^test$|", "|^test$"), false}, + {base.mutate("^test|", "|^test$"), false}, + {base.mutate("test$|", "|^test$"), false}, + {base.mutate("test$|", "|^test$"), false}, + {base.mutate("^test$|", "|test$"), false}, + {base.mutate("^test|", "|^test"), false}, + {base.mutate("test|", "|test"), false}, + {base.mutate("(test|", "|test)"), false}, + {"test", false}, + {"", false}, + } +} + +func TestIsIPv4Validation(t *testing.T) { + t.Parallel() + testCases := generateTestCasesForFixedRegex(ipv4Regex) + testValidationCommentCheck(t, testCases, func(v validationComment) bool { return v.IsIPv4() }) +} + +func TestIsIPv6Validation(t *testing.T) { + t.Parallel() + testCases := generateTestCasesForFixedRegex(ipv6Regex) + testValidationCommentCheck(t, testCases, func(v validationComment) bool { return v.IsIPv6() }) +} + +func TestIsIPValidation(t *testing.T) { + t.Parallel() + testCases := generateTestCasesForFixedRegex(ipv4Regex + "|" + ipv6Regex) + testCases = append(testCases, generateTestCasesForFixedRegex(ipv6Regex+"|"+ipv4Regex)...) + testCases = append(testCases, generateTestCasesForFixedRegex("("+ipv6Regex+")|("+ipv4Regex+")")...) + testCases = append(testCases, generateTestCasesForFixedRegex("("+ipv4Regex+")|("+ipv6Regex+")")...) + testValidationCommentCheck(t, testCases, func(v validationComment) bool { return v.IsIP() }) +} + +func TestIsNumericNonZeroValidation(t *testing.T) { + t.Parallel() + base := validationComment(numericNonZeroRegex) + testCases := []struct { + validationComment validationComment + expected bool + }{ + {base, true}, + {base.mutate("^$|", "|^$"), true}, + {base.mutate("^$|", ""), true}, + {base.mutate("", "|^$"), true}, + {base.mutate("(^$|", "|^$)"), true}, + {base.mutate("(^$|", ""), true}, + {base.mutate("", "|^$)"), true}, + + {base.mutate("(^", "$)"), false}, + {base.mutate("^", "$)"), false}, + {base.mutate("^(", "$"), false}, + {base.mutate("^test", ""), false}, + {base.mutate("^test", "test$"), false}, + {base.mutate("", "test$"), false}, + {base.mutate("test", "test"), false}, + {base.mutate("", "test"), false}, + {base.mutate("test", ""), false}, + {base.mutate("^test$|", "|^test$"), false}, + {base.mutate("^test|", "|^test$"), false}, + {base.mutate("test$|", "|^test$"), false}, + {base.mutate("test$|", "|^test$"), false}, + {base.mutate("^test$|", "|test$"), false}, + {base.mutate("^test|", "|^test"), false}, + {base.mutate("test|", "|test"), false}, + {base.mutate("(test|", "|test)"), false}, + {"test", false}, + {"", false}, + } + testValidationCommentCheck(t, testCases, func(v validationComment) bool { return v.IsNumericNonZeroBased() }) +} diff --git a/unifi/account.generated.go b/unifi/account.generated.go index e78e280..c951c2f 100644 --- a/unifi/account.generated.go +++ b/unifi/account.generated.go @@ -26,12 +26,12 @@ type Account struct { NoEdit bool `json:"attr_no_edit,omitempty"` FilterIDs []string `json:"filter_ids,omitempty"` - IP string `json:"ip,omitempty"` // ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$|^$ - Name string `json:"name,omitempty"` // ^[^"' ]+$ + IP string `json:"ip,omitempty" validate:"omitempty,ipv4"` // ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$|^$ + Name string `json:"name,omitempty"` // ^[^"' ]+$ NetworkID string `json:"networkconf_id,omitempty"` - TunnelConfigType string `json:"tunnel_config_type,omitempty"` // vpn|802.1x|custom - TunnelMediumType int `json:"tunnel_medium_type,omitempty"` // [1-9]|1[0-5]|^$ - TunnelType int `json:"tunnel_type,omitempty"` // [1-9]|1[0-3]|^$ + TunnelConfigType string `json:"tunnel_config_type,omitempty" validate:"omitempty,oneof=vpn 802.1x custom"` // vpn|802.1x|custom + TunnelMediumType int `json:"tunnel_medium_type,omitempty"` // [1-9]|1[0-5]|^$ + TunnelType int `json:"tunnel_type,omitempty"` // [1-9]|1[0-3]|^$ UlpUserID string `json:"ulp_user_id"` VLAN int `json:"vlan,omitempty"` // [2-9]|[1-9][0-9]{1,2}|[1-3][0-9]{3}|400[0-9]|^$ XPassword string `json:"x_password,omitempty"` diff --git a/unifi/channel_plan.generated.go b/unifi/channel_plan.generated.go index ed33dab..371e52c 100644 --- a/unifi/channel_plan.generated.go +++ b/unifi/channel_plan.generated.go @@ -26,12 +26,12 @@ type ChannelPlan struct { NoEdit bool `json:"attr_no_edit,omitempty"` ApBlacklistedChannels []ChannelPlanApBlacklistedChannels `json:"ap_blacklisted_channels,omitempty"` - ConfSource string `json:"conf_source,omitempty"` // manual|radio-ai + ConfSource string `json:"conf_source,omitempty" validate:"omitempty,oneof=manual radio-ai"` // manual|radio-ai Coupling []ChannelPlanCoupling `json:"coupling,omitempty"` Date string `json:"date"` // ^$|^(20[0-9]{2}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])T([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9])Z?$ Fitness float64 `json:"fitness,omitempty"` - Note string `json:"note,omitempty"` // .{0,1024} - Radio string `json:"radio,omitempty"` // na|ng|ng\+na + Note string `json:"note,omitempty" validate:"omitempty,gte=0,lte=1024"` // .{0,1024} + Radio string `json:"radio,omitempty"` // na|ng|ng\+na RadioTable []ChannelPlanRadioTable `json:"radio_table,omitempty"` Satisfaction float64 `json:"satisfaction,omitempty"` SatisfactionTable []ChannelPlanSatisfactionTable `json:"satisfaction_table,omitempty"` @@ -55,9 +55,9 @@ func (dst *ChannelPlan) UnmarshalJSON(b []byte) error { } type ChannelPlanApBlacklistedChannels struct { - Channel int `json:"channel,omitempty"` // 36|38|40|42|44|46|48|52|56|60|64|100|104|108|112|116|120|124|128|132|136|140|144|149|153|157|161|165|183|184|185|187|188|189|192|196 - MAC string `json:"mac,omitempty"` // ^([0-9A-Fa-f]{2}:){5}([0-9A-Fa-f]{2})$ - Timestamp int `json:"timestamp,omitempty"` // [1-9][0-9]{12} + Channel int `json:"channel,omitempty" validate:"omitempty,oneof=36 38 40 42 44 46 48 52 56 60 64 100 104 108 112 116 120 124 128 132 136 140 144 149 153 157 161 165 183 184 185 187 188 189 192 196"` // 36|38|40|42|44|46|48|52|56|60|64|100|104|108|112|116|120|124|128|132|136|140|144|149|153|157|161|165|183|184|185|187|188|189|192|196 + MAC string `json:"mac,omitempty" validate:"omitempty,mac"` // ^([0-9A-Fa-f]{2}:){5}([0-9A-Fa-f]{2})$ + Timestamp int `json:"timestamp,omitempty"` // [1-9][0-9]{12} } func (dst *ChannelPlanApBlacklistedChannels) UnmarshalJSON(b []byte) error { @@ -107,13 +107,13 @@ func (dst *ChannelPlanCoupling) UnmarshalJSON(b []byte) error { } type ChannelPlanRadioTable struct { - BackupChannel string `json:"backup_channel,omitempty"` // [0-9]|[1][0-4]|16|34|36|38|40|42|44|46|48|52|56|60|64|100|104|108|112|116|120|124|128|132|136|140|144|149|153|157|161|165|183|184|185|187|188|189|192|196|auto - Channel string `json:"channel,omitempty"` // [0-9]|[1][0-4]|16|34|36|38|40|42|44|46|48|52|56|60|64|100|104|108|112|116|120|124|128|132|136|140|144|149|153|157|161|165|183|184|185|187|188|189|192|196|auto - DeviceMAC string `json:"device_mac,omitempty"` // ^([0-9A-Fa-f]{2}:){5}([0-9A-Fa-f]{2})$ - Name string `json:"name,omitempty"` // [a-z]*[0-9]* - TxPower string `json:"tx_power,omitempty"` // [\d]+|auto - TxPowerMode string `json:"tx_power_mode,omitempty"` // auto|medium|high|low|custom - Width int `json:"width,omitempty"` // 20|40|80|160 + BackupChannel string `json:"backup_channel,omitempty"` // [0-9]|[1][0-4]|16|34|36|38|40|42|44|46|48|52|56|60|64|100|104|108|112|116|120|124|128|132|136|140|144|149|153|157|161|165|183|184|185|187|188|189|192|196|auto + Channel string `json:"channel,omitempty"` // [0-9]|[1][0-4]|16|34|36|38|40|42|44|46|48|52|56|60|64|100|104|108|112|116|120|124|128|132|136|140|144|149|153|157|161|165|183|184|185|187|188|189|192|196|auto + DeviceMAC string `json:"device_mac,omitempty" validate:"omitempty,mac"` // ^([0-9A-Fa-f]{2}:){5}([0-9A-Fa-f]{2})$ + Name string `json:"name,omitempty"` // [a-z]*[0-9]* + TxPower string `json:"tx_power,omitempty"` // [\d]+|auto + TxPowerMode string `json:"tx_power_mode,omitempty" validate:"omitempty,oneof=auto medium high low custom"` // auto|medium|high|low|custom + Width int `json:"width,omitempty" validate:"omitempty,oneof=20 40 80 160"` // 20|40|80|160 } func (dst *ChannelPlanRadioTable) UnmarshalJSON(b []byte) error { @@ -142,7 +142,7 @@ func (dst *ChannelPlanRadioTable) UnmarshalJSON(b []byte) error { } type ChannelPlanSatisfactionTable struct { - DeviceMAC string `json:"device_mac,omitempty"` // ^([0-9A-Fa-f]{2}:){5}([0-9A-Fa-f]{2})$ + DeviceMAC string `json:"device_mac,omitempty" validate:"omitempty,mac"` // ^([0-9A-Fa-f]{2}:){5}([0-9A-Fa-f]{2})$ Satisfaction float64 `json:"satisfaction,omitempty"` } @@ -163,8 +163,8 @@ func (dst *ChannelPlanSatisfactionTable) UnmarshalJSON(b []byte) error { } type ChannelPlanSiteBlacklistedChannels struct { - Channel int `json:"channel,omitempty"` // 36|38|40|42|44|46|48|52|56|60|64|100|104|108|112|116|120|124|128|132|136|140|144|149|153|157|161|165|183|184|185|187|188|189|192|196 - Timestamp int `json:"timestamp,omitempty"` // [1-9][0-9]{12} + Channel int `json:"channel,omitempty" validate:"omitempty,oneof=36 38 40 42 44 46 48 52 56 60 64 100 104 108 112 116 120 124 128 132 136 140 144 149 153 157 161 165 183 184 185 187 188 189 192 196"` // 36|38|40|42|44|46|48|52|56|60|64|100|104|108|112|116|120|124|128|132|136|140|144|149|153|157|161|165|183|184|185|187|188|189|192|196 + Timestamp int `json:"timestamp,omitempty"` // [1-9][0-9]{12} } func (dst *ChannelPlanSiteBlacklistedChannels) UnmarshalJSON(b []byte) error { diff --git a/unifi/device.generated.go b/unifi/device.generated.go index 7405b67..3bf901d 100644 --- a/unifi/device.generated.go +++ b/unifi/device.generated.go @@ -25,13 +25,13 @@ type Device struct { NoDelete bool `json:"attr_no_delete,omitempty"` NoEdit bool `json:"attr_no_edit,omitempty"` - MAC string `json:"mac,omitempty"` + MAC string `json:"mac,omitempty" validate:"omitempty,mac"` Adopted bool `json:"adopted"` AfcEnabled bool `json:"afc_enabled,omitempty"` AtfEnabled bool `json:"atf_enabled,omitempty"` - BandsteeringMode string `json:"bandsteering_mode,omitempty"` // off|equal|prefer_5g - BaresipAuthUser string `json:"baresip_auth_user,omitempty"` // ^\+?[a-zA-Z0-9_.\-!~*'()]* + BandsteeringMode string `json:"bandsteering_mode,omitempty" validate:"omitempty,oneof=off equal prefer_5g"` // off|equal|prefer_5g + BaresipAuthUser string `json:"baresip_auth_user,omitempty"` // ^\+?[a-zA-Z0-9_.\-!~*'()]* BaresipEnabled bool `json:"baresip_enabled,omitempty"` BaresipExtension string `json:"baresip_extension,omitempty"` // ^\+?[a-zA-Z0-9_.\-!~*'()]* ConfigNetwork DeviceConfigNetwork `json:"config_network,omitempty"` @@ -42,29 +42,29 @@ type Device struct { EtherLighting DeviceEtherLighting `json:"ether_lighting,omitempty"` EthernetOverrides []DeviceEthernetOverrides `json:"ethernet_overrides,omitempty"` FlowctrlEnabled bool `json:"flowctrl_enabled,omitempty"` - GatewayVrrpMode string `json:"gateway_vrrp_mode,omitempty"` // primary|secondary - GatewayVrrpPriority int `json:"gateway_vrrp_priority,omitempty"` // [1-9][0-9]|[1-9][0-9][0-9] + GatewayVrrpMode string `json:"gateway_vrrp_mode,omitempty" validate:"omitempty,oneof=primary secondary"` // primary|secondary + GatewayVrrpPriority int `json:"gateway_vrrp_priority,omitempty"` // [1-9][0-9]|[1-9][0-9][0-9] GreenApEnabled bool `json:"green_ap_enabled,omitempty"` HeightInMeters float64 `json:"heightInMeters,omitempty"` - Hostname string `json:"hostname,omitempty"` // .{1,128} + Hostname string `json:"hostname,omitempty" validate:"omitempty,gte=1,lte=128"` // .{1,128} JumboframeEnabled bool `json:"jumboframe_enabled,omitempty"` LcmBrightness int `json:"lcm_brightness,omitempty"` // [1-9]|[1-9][0-9]|100 LcmBrightnessOverride bool `json:"lcm_brightness_override,omitempty"` LcmIDleTimeout int `json:"lcm_idle_timeout,omitempty"` // [1-9][0-9]|[1-9][0-9][0-9]|[1-2][0-9][0-9][0-9]|3[0-5][0-9][0-9]|3600 LcmIDleTimeoutOverride bool `json:"lcm_idle_timeout_override,omitempty"` - LcmNightModeBegins string `json:"lcm_night_mode_begins,omitempty"` // (^$)|(^(0[1-9])|(1[0-9])|(2[0-3])):([0-5][0-9]$) - LcmNightModeEnds string `json:"lcm_night_mode_ends,omitempty"` // (^$)|(^(0[1-9])|(1[0-9])|(2[0-3])):([0-5][0-9]$) - LcmOrientationOverride int `json:"lcm_orientation_override,omitempty"` // 0|90|180|270 + LcmNightModeBegins string `json:"lcm_night_mode_begins,omitempty"` // (^$)|(^(0[1-9])|(1[0-9])|(2[0-3])):([0-5][0-9]$) + LcmNightModeEnds string `json:"lcm_night_mode_ends,omitempty"` // (^$)|(^(0[1-9])|(1[0-9])|(2[0-3])):([0-5][0-9]$) + LcmOrientationOverride int `json:"lcm_orientation_override,omitempty" validate:"omitempty,oneof=0 90 180 270"` // 0|90|180|270 LcmSettingsRestrictedAccess bool `json:"lcm_settings_restricted_access,omitempty"` LcmTrackerEnabled bool `json:"lcm_tracker_enabled,omitempty"` - LcmTrackerSeed string `json:"lcm_tracker_seed,omitempty"` // .{0,50} - LedOverride string `json:"led_override,omitempty"` // default|on|off - LedOverrideColor string `json:"led_override_color,omitempty"` // ^#(?:[0-9a-fA-F]{3}){1,2}$ - LedOverrideColorBrightness int `json:"led_override_color_brightness,omitempty"` // ^[0-9][0-9]?$|^100$ + LcmTrackerSeed string `json:"lcm_tracker_seed,omitempty" validate:"omitempty,gte=0,lte=50"` // .{0,50} + LedOverride string `json:"led_override,omitempty" validate:"omitempty,oneof=default on off"` // default|on|off + LedOverrideColor string `json:"led_override_color,omitempty"` // ^#(?:[0-9a-fA-F]{3}){1,2}$ + LedOverrideColorBrightness int `json:"led_override_color_brightness,omitempty"` // ^[0-9][0-9]?$|^100$ Locked bool `json:"locked,omitempty"` LowpfmodeOverride bool `json:"lowpfmode_override,omitempty"` - LteApn string `json:"lte_apn,omitempty"` // .{1,128} - LteAuthType string `json:"lte_auth_type,omitempty"` // PAP|CHAP|PAP-CHAP|NONE + LteApn string `json:"lte_apn,omitempty" validate:"omitempty,gte=1,lte=128"` // .{1,128} + LteAuthType string `json:"lte_auth_type,omitempty" validate:"omitempty,oneof=PAP CHAP PAP-CHAP NONE"` // PAP|CHAP|PAP-CHAP|NONE LteDataLimitEnabled bool `json:"lte_data_limit_enabled,omitempty"` LteDataWarningEnabled bool `json:"lte_data_warning_enabled,omitempty"` LteExtAnt bool `json:"lte_ext_ant,omitempty"` @@ -77,35 +77,35 @@ type Device struct { LteUsername string `json:"lte_username,omitempty"` MapID string `json:"map_id,omitempty"` MeshStaVapEnabled bool `json:"mesh_sta_vap_enabled,omitempty"` - MgmtNetworkID string `json:"mgmt_network_id,omitempty"` // [\d\w]+ + MgmtNetworkID string `json:"mgmt_network_id,omitempty" validate:"omitempty,w_regex"` // [\d\w]+ Model string `json:"model,omitempty"` - Name string `json:"name,omitempty"` // .{0,128} - OutdoorModeOverride string `json:"outdoor_mode_override,omitempty"` // default|on|off + Name string `json:"name,omitempty" validate:"omitempty,gte=0,lte=128"` // .{0,128} + OutdoorModeOverride string `json:"outdoor_mode_override,omitempty" validate:"omitempty,oneof=default on off"` // default|on|off OutletEnabled bool `json:"outlet_enabled,omitempty"` OutletOverrides []DeviceOutletOverrides `json:"outlet_overrides,omitempty"` OutletPowerCycleEnabled bool `json:"outlet_power_cycle_enabled,omitempty"` - PeerToPeerMode string `json:"peer_to_peer_mode,omitempty"` // ap|sta - PoeMode string `json:"poe_mode,omitempty"` // auto|pasv24|passthrough|off + PeerToPeerMode string `json:"peer_to_peer_mode,omitempty" validate:"omitempty,oneof=ap sta"` // ap|sta + PoeMode string `json:"poe_mode,omitempty" validate:"omitempty,oneof=auto pasv24 passthrough off"` // auto|pasv24|passthrough|off PortOverrides []DevicePortOverrides `json:"port_overrides"` - PowerSourceCtrl string `json:"power_source_ctrl,omitempty"` // auto|8023af|8023at|8023bt-type3|8023bt-type4|pasv24|poe-injector|ac|adapter|dc|rps - PowerSourceCtrlBudget int `json:"power_source_ctrl_budget,omitempty"` // [0-9]|[1-9][0-9]|[1-9][0-9][0-9] + PowerSourceCtrl string `json:"power_source_ctrl,omitempty" validate:"omitempty,oneof=auto 8023af 8023at 8023bt-type3 8023bt-type4 pasv24 poe-injector ac adapter dc rps"` // auto|8023af|8023at|8023bt-type3|8023bt-type4|pasv24|poe-injector|ac|adapter|dc|rps + PowerSourceCtrlBudget int `json:"power_source_ctrl_budget,omitempty"` // [0-9]|[1-9][0-9]|[1-9][0-9][0-9] PowerSourceCtrlEnabled bool `json:"power_source_ctrl_enabled,omitempty"` - PtmpApMAC string `json:"ptmp_ap_mac,omitempty"` // ^([0-9A-Fa-f]{2}[:]){5}([0-9A-Fa-f]{2})$ - PtpApMAC string `json:"ptp_ap_mac,omitempty"` // ^([0-9A-Fa-f]{2}[:]){5}([0-9A-Fa-f]{2})$ + PtmpApMAC string `json:"ptmp_ap_mac,omitempty" validate:"omitempty,mac"` // ^([0-9A-Fa-f]{2}[:]){5}([0-9A-Fa-f]{2})$ + PtpApMAC string `json:"ptp_ap_mac,omitempty" validate:"omitempty,mac"` // ^([0-9A-Fa-f]{2}[:]){5}([0-9A-Fa-f]{2})$ RADIUSProfileID string `json:"radiusprofile_id,omitempty"` RadioTable []DeviceRadioTable `json:"radio_table,omitempty"` - ResetbtnEnabled string `json:"resetbtn_enabled,omitempty"` // on|off + ResetbtnEnabled string `json:"resetbtn_enabled,omitempty" validate:"omitempty,oneof=on off"` // on|off RpsOverride DeviceRpsOverride `json:"rps_override,omitempty"` - SnmpContact string `json:"snmp_contact,omitempty"` // .{0,255} - SnmpLocation string `json:"snmp_location,omitempty"` // .{0,255} + SnmpContact string `json:"snmp_contact,omitempty" validate:"omitempty,gte=0,lte=255"` // .{0,255} + SnmpLocation string `json:"snmp_location,omitempty" validate:"omitempty,gte=0,lte=255"` // .{0,255} State DeviceState `json:"state"` - StationMode string `json:"station_mode,omitempty"` // ptp|ptmp|wifi - StpPriority string `json:"stp_priority,omitempty"` // 0|4096|8192|12288|16384|20480|24576|28672|32768|36864|40960|45056|49152|53248|57344|61440 - StpVersion string `json:"stp_version,omitempty"` // stp|rstp|disabled + StationMode string `json:"station_mode,omitempty" validate:"omitempty,oneof=ptp ptmp wifi"` // ptp|ptmp|wifi + StpPriority string `json:"stp_priority,omitempty" validate:"omitempty,oneof=0 4096 8192 12288 16384 20480 24576 28672 32768 36864 40960 45056 49152 53248 57344 61440"` // 0|4096|8192|12288|16384|20480|24576|28672|32768|36864|40960|45056|49152|53248|57344|61440 + StpVersion string `json:"stp_version,omitempty" validate:"omitempty,oneof=stp rstp disabled"` // stp|rstp|disabled SwitchVLANEnabled bool `json:"switch_vlan_enabled,omitempty"` Type string `json:"type,omitempty"` - UbbPairName string `json:"ubb_pair_name,omitempty"` // .{1,128} - Volume int `json:"volume,omitempty"` // [0-9]|[1-9][0-9]|100 + UbbPairName string `json:"ubb_pair_name,omitempty" validate:"omitempty,gte=1,lte=128"` // .{1,128} + Volume int `json:"volume,omitempty"` // [0-9]|[1-9][0-9]|100 X float64 `json:"x,omitempty"` XBaresipPassword string `json:"x_baresip_password,omitempty"` // ^[a-zA-Z0-9_.\-!~*'()]* Y float64 `json:"y,omitempty"` @@ -156,13 +156,13 @@ func (dst *Device) UnmarshalJSON(b []byte) error { type DeviceConfigNetwork struct { BondingEnabled bool `json:"bonding_enabled,omitempty"` - DNS1 string `json:"dns1,omitempty"` // ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$|^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$|^$ - DNS2 string `json:"dns2,omitempty"` // ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$|^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$|^$ + DNS1 string `json:"dns1,omitempty" validate:"omitempty,ip"` // ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$|^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$|^$ + DNS2 string `json:"dns2,omitempty" validate:"omitempty,ip"` // ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$|^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$|^$ DNSsuffix string `json:"dnssuffix,omitempty"` - Gateway string `json:"gateway,omitempty"` // ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$|^$ - IP string `json:"ip,omitempty"` // ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$ - Netmask string `json:"netmask,omitempty"` // ^((128|192|224|240|248|252|254)\.0\.0\.0)|(255\.(((0|128|192|224|240|248|252|254)\.0\.0)|(255\.(((0|128|192|224|240|248|252|254)\.0)|255\.(0|128|192|224|240|248|252|254)))))$ - Type string `json:"type,omitempty"` // dhcp|static + Gateway string `json:"gateway,omitempty" validate:"omitempty,ipv4"` // ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$|^$ + IP string `json:"ip,omitempty" validate:"omitempty,ipv4"` // ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$ + Netmask string `json:"netmask,omitempty"` // ^((128|192|224|240|248|252|254)\.0\.0\.0)|(255\.(((0|128|192|224|240|248|252|254)\.0\.0)|(255\.(((0|128|192|224|240|248|252|254)\.0)|255\.(0|128|192|224|240|248|252|254)))))$ + Type string `json:"type,omitempty" validate:"omitempty,oneof=dhcp static"` // dhcp|static } func (dst *DeviceConfigNetwork) UnmarshalJSON(b []byte) error { @@ -182,10 +182,10 @@ func (dst *DeviceConfigNetwork) UnmarshalJSON(b []byte) error { } type DeviceEtherLighting struct { - Behavior string `json:"behavior,omitempty"` // breath|steady - Brightness int `json:"brightness,omitempty"` // [1-9]|[1-9][0-9]|100 - LedMode string `json:"led_mode,omitempty"` // standard|etherlighting - Mode string `json:"mode,omitempty"` // speed|network + Behavior string `json:"behavior,omitempty" validate:"omitempty,oneof=breath steady"` // breath|steady + Brightness int `json:"brightness,omitempty"` // [1-9]|[1-9][0-9]|100 + LedMode string `json:"led_mode,omitempty" validate:"omitempty,oneof=standard etherlighting"` // standard|etherlighting + Mode string `json:"mode,omitempty" validate:"omitempty,oneof=speed network"` // speed|network } func (dst *DeviceEtherLighting) UnmarshalJSON(b []byte) error { @@ -231,7 +231,7 @@ func (dst *DeviceEthernetOverrides) UnmarshalJSON(b []byte) error { type DeviceOutletOverrides struct { CycleEnabled bool `json:"cycle_enabled,omitempty"` Index int `json:"index,omitempty"` - Name string `json:"name,omitempty"` // .{0,128} + Name string `json:"name,omitempty" validate:"omitempty,gte=0,lte=128"` // .{0,128} RelayState bool `json:"relay_state,omitempty"` } @@ -257,13 +257,13 @@ func (dst *DeviceOutletOverrides) UnmarshalJSON(b []byte) error { type DevicePortOverrides struct { AggregateNumPorts int `json:"aggregate_num_ports,omitempty"` // [1-8] Autoneg bool `json:"autoneg,omitempty"` - Dot1XCtrl string `json:"dot1x_ctrl,omitempty"` // auto|force_authorized|force_unauthorized|mac_based|multi_host - Dot1XIDleTimeout int `json:"dot1x_idle_timeout,omitempty"` // [0-9]|[1-9][0-9]{1,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5] - EgressRateLimitKbps int `json:"egress_rate_limit_kbps,omitempty"` // 6[4-9]|[7-9][0-9]|[1-9][0-9]{2,6} + Dot1XCtrl string `json:"dot1x_ctrl,omitempty" validate:"omitempty,oneof=auto force_authorized force_unauthorized mac_based multi_host"` // auto|force_authorized|force_unauthorized|mac_based|multi_host + Dot1XIDleTimeout int `json:"dot1x_idle_timeout,omitempty"` // [0-9]|[1-9][0-9]{1,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5] + EgressRateLimitKbps int `json:"egress_rate_limit_kbps,omitempty"` // 6[4-9]|[7-9][0-9]|[1-9][0-9]{2,6} EgressRateLimitKbpsEnabled bool `json:"egress_rate_limit_kbps_enabled,omitempty"` ExcludedNetworkIDs []string `json:"excluded_networkconf_ids,omitempty"` - FecMode string `json:"fec_mode,omitempty"` // rs-fec|fc-fec|default|disabled - Forward string `json:"forward,omitempty"` // all|native|customize|disabled + FecMode string `json:"fec_mode,omitempty" validate:"omitempty,oneof=rs-fec fc-fec default disabled"` // rs-fec|fc-fec|default|disabled + Forward string `json:"forward,omitempty" validate:"omitempty,oneof=all native customize disabled"` // all|native|customize|disabled FullDuplex bool `json:"full_duplex,omitempty"` Isolation bool `json:"isolation,omitempty"` LldpmedEnabled bool `json:"lldpmed_enabled,omitempty"` @@ -271,33 +271,33 @@ type DevicePortOverrides struct { MirrorPortIDX int `json:"mirror_port_idx,omitempty"` // [1-9]|[1-4][0-9]|5[0-6] MulticastRouterNetworkIDs []string `json:"multicast_router_networkconf_ids,omitempty"` NATiveNetworkID string `json:"native_networkconf_id,omitempty"` - Name string `json:"name,omitempty"` // .{0,128} - OpMode string `json:"op_mode,omitempty"` // switch|mirror|aggregate - PoeMode string `json:"poe_mode,omitempty"` // auto|pasv24|passthrough|off - PortIDX int `json:"port_idx,omitempty"` // [1-9]|[1-4][0-9]|5[0-6] + Name string `json:"name,omitempty" validate:"omitempty,gte=0,lte=128"` // .{0,128} + OpMode string `json:"op_mode,omitempty" validate:"omitempty,oneof=switch mirror aggregate"` // switch|mirror|aggregate + PoeMode string `json:"poe_mode,omitempty" validate:"omitempty,oneof=auto pasv24 passthrough off"` // auto|pasv24|passthrough|off + PortIDX int `json:"port_idx,omitempty"` // [1-9]|[1-4][0-9]|5[0-6] PortKeepaliveEnabled bool `json:"port_keepalive_enabled,omitempty"` - PortProfileID string `json:"portconf_id,omitempty"` // [\d\w]+ + PortProfileID string `json:"portconf_id,omitempty" validate:"omitempty,w_regex"` // [\d\w]+ PortSecurityEnabled bool `json:"port_security_enabled,omitempty"` - PortSecurityMACAddress []string `json:"port_security_mac_address,omitempty"` // ^([0-9A-Fa-f]{2}[:]){5}([0-9A-Fa-f]{2})$ - PriorityQueue1Level int `json:"priority_queue1_level,omitempty"` // [0-9]|[1-9][0-9]|100 - PriorityQueue2Level int `json:"priority_queue2_level,omitempty"` // [0-9]|[1-9][0-9]|100 - PriorityQueue3Level int `json:"priority_queue3_level,omitempty"` // [0-9]|[1-9][0-9]|100 - PriorityQueue4Level int `json:"priority_queue4_level,omitempty"` // [0-9]|[1-9][0-9]|100 + PortSecurityMACAddress []string `json:"port_security_mac_address,omitempty" validate:"omitempty,mac"` // ^([0-9A-Fa-f]{2}[:]){5}([0-9A-Fa-f]{2})$ + PriorityQueue1Level int `json:"priority_queue1_level,omitempty"` // [0-9]|[1-9][0-9]|100 + PriorityQueue2Level int `json:"priority_queue2_level,omitempty"` // [0-9]|[1-9][0-9]|100 + PriorityQueue3Level int `json:"priority_queue3_level,omitempty"` // [0-9]|[1-9][0-9]|100 + PriorityQueue4Level int `json:"priority_queue4_level,omitempty"` // [0-9]|[1-9][0-9]|100 QOSProfile DeviceQOSProfile `json:"qos_profile,omitempty"` - SettingPreference string `json:"setting_preference,omitempty"` // auto|manual - Speed int `json:"speed,omitempty"` // 10|100|1000|2500|5000|10000|20000|25000|40000|50000|100000 + SettingPreference string `json:"setting_preference,omitempty" validate:"omitempty,oneof=auto manual"` // auto|manual + Speed int `json:"speed,omitempty" validate:"omitempty,oneof=10 100 1000 2500 5000 10000 20000 25000 40000 50000 100000"` // 10|100|1000|2500|5000|10000|20000|25000|40000|50000|100000 StormctrlBroadcastastEnabled bool `json:"stormctrl_bcast_enabled,omitempty"` StormctrlBroadcastastLevel int `json:"stormctrl_bcast_level,omitempty"` // [0-9]|[1-9][0-9]|100 StormctrlBroadcastastRate int `json:"stormctrl_bcast_rate,omitempty"` // [0-9]|[1-9][0-9]{1,6}|1[0-3][0-9]{6}|14[0-7][0-9]{5}|148[0-7][0-9]{4}|14880000 StormctrlMcastEnabled bool `json:"stormctrl_mcast_enabled,omitempty"` - StormctrlMcastLevel int `json:"stormctrl_mcast_level,omitempty"` // [0-9]|[1-9][0-9]|100 - StormctrlMcastRate int `json:"stormctrl_mcast_rate,omitempty"` // [0-9]|[1-9][0-9]{1,6}|1[0-3][0-9]{6}|14[0-7][0-9]{5}|148[0-7][0-9]{4}|14880000 - StormctrlType string `json:"stormctrl_type,omitempty"` // level|rate + StormctrlMcastLevel int `json:"stormctrl_mcast_level,omitempty"` // [0-9]|[1-9][0-9]|100 + StormctrlMcastRate int `json:"stormctrl_mcast_rate,omitempty"` // [0-9]|[1-9][0-9]{1,6}|1[0-3][0-9]{6}|14[0-7][0-9]{5}|148[0-7][0-9]{4}|14880000 + StormctrlType string `json:"stormctrl_type,omitempty" validate:"omitempty,oneof=level rate"` // level|rate StormctrlUcastEnabled bool `json:"stormctrl_ucast_enabled,omitempty"` StormctrlUcastLevel int `json:"stormctrl_ucast_level,omitempty"` // [0-9]|[1-9][0-9]|100 StormctrlUcastRate int `json:"stormctrl_ucast_rate,omitempty"` // [0-9]|[1-9][0-9]{1,6}|1[0-3][0-9]{6}|14[0-7][0-9]{5}|148[0-7][0-9]{4}|14880000 StpPortMode bool `json:"stp_port_mode,omitempty"` - TaggedVLANMgmt string `json:"tagged_vlan_mgmt,omitempty"` // auto|block_all|custom + TaggedVLANMgmt string `json:"tagged_vlan_mgmt,omitempty" validate:"omitempty,oneof=auto block_all custom"` // auto|block_all|custom VoiceNetworkID string `json:"voice_networkconf_id,omitempty"` } @@ -351,10 +351,10 @@ func (dst *DevicePortOverrides) UnmarshalJSON(b []byte) error { } type DeviceQOSMarking struct { - CosCode int `json:"cos_code,omitempty"` // [0-7] - DscpCode int `json:"dscp_code,omitempty"` // 0|8|16|24|32|40|48|56|10|12|14|18|20|22|26|28|30|34|36|38|44|46 - IPPrecedenceCode int `json:"ip_precedence_code,omitempty"` // [0-7] - Queue int `json:"queue,omitempty"` // [0-7] + CosCode int `json:"cos_code,omitempty"` // [0-7] + DscpCode int `json:"dscp_code,omitempty" validate:"omitempty,oneof=0 8 16 24 32 40 48 56 10 12 14 18 20 22 26 28 30 34 36 38 44 46"` // 0|8|16|24|32|40|48|56|10|12|14|18|20|22|26|28|30|34|36|38|44|46 + IPPrecedenceCode int `json:"ip_precedence_code,omitempty"` // [0-7] + Queue int `json:"queue,omitempty"` // [0-7] } func (dst *DeviceQOSMarking) UnmarshalJSON(b []byte) error { @@ -441,7 +441,7 @@ func (dst *DeviceQOSPolicies) UnmarshalJSON(b []byte) error { type DeviceQOSProfile struct { QOSPolicies []DeviceQOSPolicies `json:"qos_policies,omitempty"` - QOSProfileMode string `json:"qos_profile_mode,omitempty"` // custom|unifi_play|aes67_audio|crestron_audio_video|dante_audio|ndi_aes67_audio|ndi_dante_audio|qsys_audio_video|qsys_video_dante_audio|sdvoe_aes67_audio|sdvoe_dante_audio|shure_audio + QOSProfileMode string `json:"qos_profile_mode,omitempty" validate:"omitempty,oneof=custom unifi_play aes67_audio crestron_audio_video dante_audio ndi_aes67_audio ndi_dante_audio qsys_audio_video qsys_video_dante_audio sdvoe_aes67_audio sdvoe_dante_audio shure_audio"` // custom|unifi_play|aes67_audio|crestron_audio_video|dante_audio|ndi_aes67_audio|ndi_dante_audio|qsys_audio_video|qsys_video_dante_audio|sdvoe_aes67_audio|sdvoe_dante_audio|shure_audio } func (dst *DeviceQOSProfile) UnmarshalJSON(b []byte) error { @@ -488,18 +488,18 @@ type DeviceRadioTable struct { Channel string `json:"channel,omitempty"` // [0-9]|[1][0-4]|4.5|5|16|17|21|25|29|33|34|36|37|38|40|41|42|44|45|46|48|49|52|53|56|57|60|61|64|65|69|73|77|81|85|89|93|97|100|101|104|105|108|109|112|113|117|116|120|121|124|125|128|129|132|133|136|137|140|141|144|145|149|153|157|161|165|169|173|177|181|183|184|185|187|188|189|192|193|196|197|201|205|209|213|217|221|225|229|233|auto ChannelOptimizationEnabled bool `json:"channel_optimization_enabled,omitempty"` HardNoiseFloorEnabled bool `json:"hard_noise_floor_enabled,omitempty"` - Ht int `json:"ht,omitempty"` // 20|40|80|160|240|320|1080|2160|4320 + Ht int `json:"ht,omitempty" validate:"omitempty,oneof=20 40 80 160 240 320 1080 2160 4320"` // 20|40|80|160|240|320|1080|2160|4320 LoadbalanceEnabled bool `json:"loadbalance_enabled,omitempty"` Maxsta int `json:"maxsta,omitempty"` // [1-9]|[1-9][0-9]|1[0-9]{2}|200|^$ MinRssi int `json:"min_rssi,omitempty"` // ^-(6[7-9]|[7-8][0-9]|90)$ MinRssiEnabled bool `json:"min_rssi_enabled,omitempty"` Name string `json:"name,omitempty"` - Radio string `json:"radio,omitempty"` // ng|na|ad|6e + Radio string `json:"radio,omitempty" validate:"omitempty,oneof=ng na ad 6e"` // ng|na|ad|6e RadioIDentifiers []DeviceRadioIDentifiers `json:"radio_identifiers,omitempty"` SensLevel int `json:"sens_level,omitempty"` // ^-([5-8][0-9]|90)$ SensLevelEnabled bool `json:"sens_level_enabled,omitempty"` - TxPower string `json:"tx_power,omitempty"` // [\d]+|auto - TxPowerMode string `json:"tx_power_mode,omitempty"` // auto|medium|high|low|custom + TxPower string `json:"tx_power,omitempty"` // [\d]+|auto + TxPowerMode string `json:"tx_power_mode,omitempty" validate:"omitempty,oneof=auto medium high low custom"` // auto|medium|high|low|custom VwireEnabled bool `json:"vwire_enabled,omitempty"` } @@ -539,7 +539,7 @@ func (dst *DeviceRadioTable) UnmarshalJSON(b []byte) error { } type DeviceRpsOverride struct { - PowerManagementMode string `json:"power_management_mode,omitempty"` // dynamic|static + PowerManagementMode string `json:"power_management_mode,omitempty" validate:"omitempty,oneof=dynamic static"` // dynamic|static RpsPortTable []DeviceRpsPortTable `json:"rps_port_table,omitempty"` } @@ -560,9 +560,9 @@ func (dst *DeviceRpsOverride) UnmarshalJSON(b []byte) error { } type DeviceRpsPortTable struct { - Name string `json:"name,omitempty"` // .{0,32} - PortIDX int `json:"port_idx,omitempty"` // [1-8] - PortMode string `json:"port_mode,omitempty"` // auto|force_active|manual|disabled + Name string `json:"name,omitempty" validate:"omitempty,gte=0,lte=32"` // .{0,32} + PortIDX int `json:"port_idx,omitempty"` // [1-8] + PortMode string `json:"port_mode,omitempty" validate:"omitempty,oneof=auto force_active manual disabled"` // auto|force_active|manual|disabled } func (dst *DeviceRpsPortTable) UnmarshalJSON(b []byte) error { diff --git a/unifi/dhcp_option.generated.go b/unifi/dhcp_option.generated.go index d5c2377..baa92bf 100644 --- a/unifi/dhcp_option.generated.go +++ b/unifi/dhcp_option.generated.go @@ -28,8 +28,8 @@ type DHCPOption struct { Code string `json:"code,omitempty"` // ^(?!(?:15|42|43|44|51|66|67|252)$)([7-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-4])$ Name string `json:"name,omitempty"` // ^[A-Za-z0-9-_]{1,25}$ Signed bool `json:"signed"` - Type string `json:"type,omitempty"` // ^(boolean|hexarray|integer|ipaddress|macaddress|text)$ - Width int `json:"width,omitempty"` // ^(8|16|32)$ + Type string `json:"type,omitempty" validate:"omitempty,oneof=boolean hexarray integer ipaddress macaddress text"` // ^(boolean|hexarray|integer|ipaddress|macaddress|text)$ + Width int `json:"width,omitempty" validate:"omitempty,oneof=8 16 32"` // ^(8|16|32)$ } func (dst *DHCPOption) UnmarshalJSON(b []byte) error { diff --git a/unifi/dns_record.go b/unifi/dns_record.go index a49cbab..43c10fe 100644 --- a/unifi/dns_record.go +++ b/unifi/dns_record.go @@ -18,12 +18,12 @@ type DNSRecord struct { NoEdit bool `json:"attr_no_edit,omitempty"` Enabled bool `json:"enabled"` - Key string `json:"key,omitempty"` // .{1,128} + Key string `json:"key,omitempty" validate:"required,gte=1,lte=128"` // .{1,128} Port int `json:"port,omitempty"` - Priority int `json:"priority,omitempty"` // .{1,128} - RecordType string `json:"record_type,omitempty"` // A|AAAA|CNAME|MX|NS|PTR|SOA|SRV|TXT + Priority int `json:"priority,omitempty" validate:"omitempty,gte=1,lte=128"` // .{1,128} + RecordType string `json:"record_type,omitempty" validate:"required,oneof=A AAAA MX NS PTR SOA SRV TXT"` // A|AAAA|CNAME|MX|NS|PTR|SOA|SRV|TXT Ttl int `json:"ttl,omitempty"` - Value string `json:"value,omitempty"` // .{1,256} + Value string `json:"value,omitempty" validate:"required,gte=1,lte=256"` // .{1,256} Weight int `json:"weight,omitempty"` } diff --git a/unifi/dpi_app.generated.go b/unifi/dpi_app.generated.go index 9a46c56..df13c77 100644 --- a/unifi/dpi_app.generated.go +++ b/unifi/dpi_app.generated.go @@ -30,9 +30,9 @@ type DpiApp struct { Cats []int `json:"cats,omitempty"` Enabled bool `json:"enabled"` Log bool `json:"log"` - Name string `json:"name,omitempty"` // .{1,128} - QOSRateMaxDown int `json:"qos_rate_max_down,omitempty"` // -1|[2-9]|[1-9][0-9]{1,4}|100000|10[0-1][0-9]{3}|102[0-3][0-9]{2}|102400 - QOSRateMaxUp int `json:"qos_rate_max_up,omitempty"` // -1|[2-9]|[1-9][0-9]{1,4}|100000|10[0-1][0-9]{3}|102[0-3][0-9]{2}|102400 + Name string `json:"name,omitempty" validate:"omitempty,gte=1,lte=128"` // .{1,128} + QOSRateMaxDown int `json:"qos_rate_max_down,omitempty"` // -1|[2-9]|[1-9][0-9]{1,4}|100000|10[0-1][0-9]{3}|102[0-3][0-9]{2}|102400 + QOSRateMaxUp int `json:"qos_rate_max_up,omitempty"` // -1|[2-9]|[1-9][0-9]{1,4}|100000|10[0-1][0-9]{3}|102[0-3][0-9]{2}|102400 } func (dst *DpiApp) UnmarshalJSON(b []byte) error { diff --git a/unifi/dpi_group.generated.go b/unifi/dpi_group.generated.go index 9534d39..242ea8f 100644 --- a/unifi/dpi_group.generated.go +++ b/unifi/dpi_group.generated.go @@ -25,9 +25,9 @@ type DpiGroup struct { NoDelete bool `json:"attr_no_delete,omitempty"` NoEdit bool `json:"attr_no_edit,omitempty"` - DPIappIDs []string `json:"dpiapp_ids,omitempty"` // [\d\w]+ + DPIappIDs []string `json:"dpiapp_ids,omitempty" validate:"omitempty,w_regex"` // [\d\w]+ Enabled bool `json:"enabled"` - Name string `json:"name,omitempty"` // .{1,128} + Name string `json:"name,omitempty" validate:"omitempty,gte=1,lte=128"` // .{1,128} } func (dst *DpiGroup) UnmarshalJSON(b []byte) error { diff --git a/unifi/dynamic_dns.generated.go b/unifi/dynamic_dns.generated.go index 56d59a2..1cdcc4d 100644 --- a/unifi/dynamic_dns.generated.go +++ b/unifi/dynamic_dns.generated.go @@ -25,14 +25,14 @@ type DynamicDNS struct { NoDelete bool `json:"attr_no_delete,omitempty"` NoEdit bool `json:"attr_no_edit,omitempty"` - CustomService string `json:"custom_service,omitempty"` // ^[^"' ]+$ - HostName string `json:"host_name,omitempty"` // ^[^"' ]+$ - Interface string `json:"interface,omitempty"` // wan|wan2 - Login string `json:"login,omitempty"` // ^[^"' ]+$ - Options []string `json:"options,omitempty"` // ^[^"' ]+$ - Server string `json:"server"` // ^[^"' ]+$|^$ - Service string `json:"service,omitempty"` // afraid|changeip|cloudflare|cloudxns|ddnss|dhis|dnsexit|dnsomatic|dnspark|dnspod|dslreports|dtdns|duckdns|duiadns|dyn|dyndns|dynv6|easydns|freemyip|googledomains|loopia|namecheap|noip|nsupdate|ovh|sitelutions|spdyn|strato|tunnelbroker|zoneedit|custom - XPassword string `json:"x_password,omitempty"` // ^[^"' ]+$ + CustomService string `json:"custom_service,omitempty"` // ^[^"' ]+$ + HostName string `json:"host_name,omitempty"` // ^[^"' ]+$ + Interface string `json:"interface,omitempty" validate:"omitempty,oneof=wan wan2"` // wan|wan2 + Login string `json:"login,omitempty"` // ^[^"' ]+$ + Options []string `json:"options,omitempty"` // ^[^"' ]+$ + Server string `json:"server"` // ^[^"' ]+$|^$ + Service string `json:"service,omitempty" validate:"omitempty,oneof=afraid changeip cloudflare cloudxns ddnss dhis dnsexit dnsomatic dnspark dnspod dslreports dtdns duckdns duiadns dyn dyndns dynv6 easydns freemyip googledomains loopia namecheap noip nsupdate ovh sitelutions spdyn strato tunnelbroker zoneedit custom"` // afraid|changeip|cloudflare|cloudxns|ddnss|dhis|dnsexit|dnsomatic|dnspark|dnspod|dslreports|dtdns|duckdns|duiadns|dyn|dyndns|dynv6|easydns|freemyip|googledomains|loopia|namecheap|noip|nsupdate|ovh|sitelutions|spdyn|strato|tunnelbroker|zoneedit|custom + XPassword string `json:"x_password,omitempty"` // ^[^"' ]+$ } func (dst *DynamicDNS) UnmarshalJSON(b []byte) error { diff --git a/unifi/firewall_group.generated.go b/unifi/firewall_group.generated.go index 4c2f524..ec888cd 100644 --- a/unifi/firewall_group.generated.go +++ b/unifi/firewall_group.generated.go @@ -26,8 +26,8 @@ type FirewallGroup struct { NoEdit bool `json:"attr_no_edit,omitempty"` GroupMembers []string `json:"group_members,omitempty"` - GroupType string `json:"group_type,omitempty"` // address-group|port-group|ipv6-address-group - Name string `json:"name,omitempty"` // .{1,64} + GroupType string `json:"group_type,omitempty" validate:"omitempty,oneof=address-group port-group ipv6-address-group"` // address-group|port-group|ipv6-address-group + Name string `json:"name,omitempty" validate:"omitempty,gte=1,lte=64"` // .{1,64} } func (dst *FirewallGroup) UnmarshalJSON(b []byte) error { diff --git a/unifi/firewall_rule.generated.go b/unifi/firewall_rule.generated.go index 7210b97..d2e4924 100644 --- a/unifi/firewall_rule.generated.go +++ b/unifi/firewall_rule.generated.go @@ -25,31 +25,31 @@ type FirewallRule struct { NoDelete bool `json:"attr_no_delete,omitempty"` NoEdit bool `json:"attr_no_edit,omitempty"` - Action string `json:"action,omitempty"` // drop|reject|accept + Action string `json:"action,omitempty" validate:"omitempty,oneof=drop reject accept"` // drop|reject|accept DstAddress string `json:"dst_address,omitempty"` DstAddressIPV6 string `json:"dst_address_ipv6,omitempty"` - DstFirewallGroupIDs []string `json:"dst_firewallgroup_ids,omitempty"` // [\d\w]+ - DstNetworkID string `json:"dst_networkconf_id"` // [\d\w]+|^$ - DstNetworkType string `json:"dst_networkconf_type,omitempty"` // ADDRv4|NETv4 + DstFirewallGroupIDs []string `json:"dst_firewallgroup_ids,omitempty" validate:"omitempty,w_regex"` // [\d\w]+ + DstNetworkID string `json:"dst_networkconf_id" validate:"omitempty,w_regex"` // [\d\w]+|^$ + DstNetworkType string `json:"dst_networkconf_type,omitempty" validate:"omitempty,oneof=ADDRv4 NETv4"` // ADDRv4|NETv4 DstPort string `json:"dst_port,omitempty"` Enabled bool `json:"enabled"` - ICMPTypename string `json:"icmp_typename"` // ^$|address-mask-reply|address-mask-request|any|communication-prohibited|destination-unreachable|echo-reply|echo-request|fragmentation-needed|host-precedence-violation|host-prohibited|host-redirect|host-unknown|host-unreachable|ip-header-bad|network-prohibited|network-redirect|network-unknown|network-unreachable|parameter-problem|port-unreachable|precedence-cutoff|protocol-unreachable|redirect|required-option-missing|router-advertisement|router-solicitation|source-quench|source-route-failed|time-exceeded|timestamp-reply|timestamp-request|TOS-host-redirect|TOS-host-unreachable|TOS-network-redirect|TOS-network-unreachable|ttl-zero-during-reassembly|ttl-zero-during-transit - ICMPv6Typename string `json:"icmpv6_typename"` // ^$|address-unreachable|bad-header|beyond-scope|communication-prohibited|destination-unreachable|echo-reply|echo-request|failed-policy|neighbor-advertisement|neighbor-solicitation|no-route|packet-too-big|parameter-problem|port-unreachable|redirect|reject-route|router-advertisement|router-solicitation|time-exceeded|ttl-zero-during-reassembly|ttl-zero-during-transit|unknown-header-type|unknown-option - IPSec string `json:"ipsec"` // match-ipsec|match-none|^$ + ICMPTypename string `json:"icmp_typename" validate:"omitempty,oneof=address-mask-reply address-mask-request any communication-prohibited destination-unreachable echo-reply echo-request fragmentation-needed host-precedence-violation host-prohibited host-redirect host-unknown host-unreachable ip-header-bad network-prohibited network-redirect network-unknown network-unreachable parameter-problem port-unreachable precedence-cutoff protocol-unreachable redirect required-option-missing router-advertisement router-solicitation source-quench source-route-failed time-exceeded timestamp-reply timestamp-request TOS-host-redirect TOS-host-unreachable TOS-network-redirect TOS-network-unreachable ttl-zero-during-reassembly ttl-zero-during-transit"` // ^$|address-mask-reply|address-mask-request|any|communication-prohibited|destination-unreachable|echo-reply|echo-request|fragmentation-needed|host-precedence-violation|host-prohibited|host-redirect|host-unknown|host-unreachable|ip-header-bad|network-prohibited|network-redirect|network-unknown|network-unreachable|parameter-problem|port-unreachable|precedence-cutoff|protocol-unreachable|redirect|required-option-missing|router-advertisement|router-solicitation|source-quench|source-route-failed|time-exceeded|timestamp-reply|timestamp-request|TOS-host-redirect|TOS-host-unreachable|TOS-network-redirect|TOS-network-unreachable|ttl-zero-during-reassembly|ttl-zero-during-transit + ICMPv6Typename string `json:"icmpv6_typename" validate:"omitempty,oneof=address-unreachable bad-header beyond-scope communication-prohibited destination-unreachable echo-reply echo-request failed-policy neighbor-advertisement neighbor-solicitation no-route packet-too-big parameter-problem port-unreachable redirect reject-route router-advertisement router-solicitation time-exceeded ttl-zero-during-reassembly ttl-zero-during-transit unknown-header-type unknown-option"` // ^$|address-unreachable|bad-header|beyond-scope|communication-prohibited|destination-unreachable|echo-reply|echo-request|failed-policy|neighbor-advertisement|neighbor-solicitation|no-route|packet-too-big|parameter-problem|port-unreachable|redirect|reject-route|router-advertisement|router-solicitation|time-exceeded|ttl-zero-during-reassembly|ttl-zero-during-transit|unknown-header-type|unknown-option + IPSec string `json:"ipsec" validate:"omitempty,oneof=match-ipsec match-none"` // match-ipsec|match-none|^$ Logging bool `json:"logging"` - Name string `json:"name,omitempty"` // .{1,128} - Protocol string `json:"protocol"` // ^$|all|([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])|tcp_udp|ah|ax.25|dccp|ddp|egp|eigrp|encap|esp|etherip|fc|ggp|gre|hip|hmp|icmp|idpr-cmtp|idrp|igmp|igp|ip|ipcomp|ipencap|ipip|ipv6|ipv6-frag|ipv6-icmp|ipv6-nonxt|ipv6-opts|ipv6-route|isis|iso-tp4|l2tp|manet|mobility-header|mpls-in-ip|ospf|pim|pup|rdp|rohc|rspf|rsvp|sctp|shim6|skip|st|tcp|udp|udplite|vmtp|vrrp|wesp|xns-idp|xtp + Name string `json:"name,omitempty" validate:"omitempty,gte=1,lte=128"` // .{1,128} + Protocol string `json:"protocol"` // ^$|all|([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])|tcp_udp|ah|ax.25|dccp|ddp|egp|eigrp|encap|esp|etherip|fc|ggp|gre|hip|hmp|icmp|idpr-cmtp|idrp|igmp|igp|ip|ipcomp|ipencap|ipip|ipv6|ipv6-frag|ipv6-icmp|ipv6-nonxt|ipv6-opts|ipv6-route|isis|iso-tp4|l2tp|manet|mobility-header|mpls-in-ip|ospf|pim|pup|rdp|rohc|rspf|rsvp|sctp|shim6|skip|st|tcp|udp|udplite|vmtp|vrrp|wesp|xns-idp|xtp ProtocolMatchExcepted bool `json:"protocol_match_excepted"` - ProtocolV6 string `json:"protocol_v6"` // ^$|([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])|ah|all|dccp|eigrp|esp|gre|icmpv6|ipcomp|ipv6|ipv6-frag|ipv6-icmp|ipv6-nonxt|ipv6-opts|ipv6-route|isis|l2tp|manet|mobility-header|mpls-in-ip|ospf|pim|rsvp|sctp|shim6|tcp|tcp_udp|udp|vrrp - RuleIndex int `json:"rule_index,omitempty"` // 2[0-9]{3,4}|4[0-9]{3,4} - Ruleset string `json:"ruleset,omitempty"` // WAN_IN|WAN_OUT|WAN_LOCAL|LAN_IN|LAN_OUT|LAN_LOCAL|GUEST_IN|GUEST_OUT|GUEST_LOCAL|WANv6_IN|WANv6_OUT|WANv6_LOCAL|LANv6_IN|LANv6_OUT|LANv6_LOCAL|GUESTv6_IN|GUESTv6_OUT|GUESTv6_LOCAL - SettingPreference string `json:"setting_preference,omitempty"` // auto|manual + ProtocolV6 string `json:"protocol_v6"` // ^$|([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])|ah|all|dccp|eigrp|esp|gre|icmpv6|ipcomp|ipv6|ipv6-frag|ipv6-icmp|ipv6-nonxt|ipv6-opts|ipv6-route|isis|l2tp|manet|mobility-header|mpls-in-ip|ospf|pim|rsvp|sctp|shim6|tcp|tcp_udp|udp|vrrp + RuleIndex int `json:"rule_index,omitempty"` // 2[0-9]{3,4}|4[0-9]{3,4} + Ruleset string `json:"ruleset,omitempty" validate:"omitempty,oneof=WAN_IN WAN_OUT WAN_LOCAL LAN_IN LAN_OUT LAN_LOCAL GUEST_IN GUEST_OUT GUEST_LOCAL WANv6_IN WANv6_OUT WANv6_LOCAL LANv6_IN LANv6_OUT LANv6_LOCAL GUESTv6_IN GUESTv6_OUT GUESTv6_LOCAL"` // WAN_IN|WAN_OUT|WAN_LOCAL|LAN_IN|LAN_OUT|LAN_LOCAL|GUEST_IN|GUEST_OUT|GUEST_LOCAL|WANv6_IN|WANv6_OUT|WANv6_LOCAL|LANv6_IN|LANv6_OUT|LANv6_LOCAL|GUESTv6_IN|GUESTv6_OUT|GUESTv6_LOCAL + SettingPreference string `json:"setting_preference,omitempty" validate:"omitempty,oneof=auto manual"` // auto|manual SrcAddress string `json:"src_address,omitempty"` SrcAddressIPV6 string `json:"src_address_ipv6,omitempty"` - SrcFirewallGroupIDs []string `json:"src_firewallgroup_ids,omitempty"` // [\d\w]+ - SrcMACAddress string `json:"src_mac_address"` // ^([0-9A-Fa-f]{2}:){5}([0-9A-Fa-f]{2})$|^$ - SrcNetworkID string `json:"src_networkconf_id"` // [\d\w]+|^$ - SrcNetworkType string `json:"src_networkconf_type,omitempty"` // ADDRv4|NETv4 + SrcFirewallGroupIDs []string `json:"src_firewallgroup_ids,omitempty" validate:"omitempty,w_regex"` // [\d\w]+ + SrcMACAddress string `json:"src_mac_address" validate:"omitempty,mac"` // ^([0-9A-Fa-f]{2}:){5}([0-9A-Fa-f]{2})$|^$ + SrcNetworkID string `json:"src_networkconf_id" validate:"omitempty,w_regex"` // [\d\w]+|^$ + SrcNetworkType string `json:"src_networkconf_type,omitempty" validate:"omitempty,oneof=ADDRv4 NETv4"` // ADDRv4|NETv4 SrcPort string `json:"src_port,omitempty"` StateEstablished bool `json:"state_established"` StateInvalid bool `json:"state_invalid"` diff --git a/unifi/heat_map.generated.go b/unifi/heat_map.generated.go index e85c9f4..e69d923 100644 --- a/unifi/heat_map.generated.go +++ b/unifi/heat_map.generated.go @@ -27,8 +27,8 @@ type HeatMap struct { Description string `json:"description,omitempty"` MapID string `json:"map_id"` - Name string `json:"name,omitempty"` // .*[^\s]+.* - Type string `json:"type,omitempty"` // download|upload + Name string `json:"name,omitempty"` // .*[^\s]+.* + Type string `json:"type,omitempty" validate:"omitempty,oneof=download upload"` // download|upload } func (dst *HeatMap) UnmarshalJSON(b []byte) error { diff --git a/unifi/hotspot_2_conf.generated.go b/unifi/hotspot_2_conf.generated.go index 94c566e..2bed80d 100644 --- a/unifi/hotspot_2_conf.generated.go +++ b/unifi/hotspot_2_conf.generated.go @@ -30,22 +30,22 @@ type Hotspot2Conf struct { CellularNetworkList []Hotspot2ConfCellularNetworkList `json:"cellular_network_list,omitempty"` DeauthReqTimeout int `json:"deauth_req_timeout,omitempty"` // [1-9][0-9]|[1-9][0-9][0-9]|[1-2][0-9][0-9][0-9]|3[0-5][0-9][0-9]|3600 DisableDgaf bool `json:"disable_dgaf"` - DomainNameList []string `json:"domain_name_list,omitempty"` // .{1,128} + DomainNameList []string `json:"domain_name_list,omitempty" validate:"omitempty,gte=1,lte=128"` // .{1,128} FriendlyName []Hotspot2ConfFriendlyName `json:"friendly_name,omitempty"` GasAdvanced bool `json:"gas_advanced"` GasComebackDelay int `json:"gas_comeback_delay,omitempty"` GasFragLimit int `json:"gas_frag_limit,omitempty"` - Hessid string `json:"hessid"` // ^([0-9A-Fa-f]{2}:){5}([0-9A-Fa-f]{2})$|^$ + Hessid string `json:"hessid" validate:"omitempty,mac"` // ^([0-9A-Fa-f]{2}:){5}([0-9A-Fa-f]{2})$|^$ HessidUsed bool `json:"hessid_used"` - IPaddrTypeAvailV4 int `json:"ipaddr_type_avail_v4,omitempty"` // 0|1|2|3|4|5|6|7 - IPaddrTypeAvailV6 int `json:"ipaddr_type_avail_v6,omitempty"` // 0|1|2 + IPaddrTypeAvailV4 int `json:"ipaddr_type_avail_v4,omitempty" validate:"omitempty,oneof=0 1 2 3 4 5 6 7"` // 0|1|2|3|4|5|6|7 + IPaddrTypeAvailV6 int `json:"ipaddr_type_avail_v6,omitempty" validate:"omitempty,oneof=0 1 2"` // 0|1|2 Icons []Hotspot2ConfIcons `json:"icons,omitempty"` MetricsDownlinkLoad int `json:"metrics_downlink_load,omitempty"` MetricsDownlinkLoadSet bool `json:"metrics_downlink_load_set"` MetricsDownlinkSpeed int `json:"metrics_downlink_speed,omitempty"` MetricsDownlinkSpeedSet bool `json:"metrics_downlink_speed_set"` MetricsInfoAtCapacity bool `json:"metrics_info_at_capacity"` - MetricsInfoLinkStatus string `json:"metrics_info_link_status,omitempty"` // up|down|test + MetricsInfoLinkStatus string `json:"metrics_info_link_status,omitempty" validate:"omitempty,oneof=up down test"` // up|down|test MetricsInfoSymmetric bool `json:"metrics_info_symmetric"` MetricsMeasurement int `json:"metrics_measurement,omitempty"` MetricsMeasurementSet bool `json:"metrics_measurement_set"` @@ -55,14 +55,14 @@ type Hotspot2Conf struct { MetricsUplinkSpeed int `json:"metrics_uplink_speed,omitempty"` MetricsUplinkSpeedSet bool `json:"metrics_uplink_speed_set"` NaiRealmList []Hotspot2ConfNaiRealmList `json:"nai_realm_list,omitempty"` - Name string `json:"name,omitempty"` // .{1,128} + Name string `json:"name,omitempty" validate:"omitempty,gte=1,lte=128"` // .{1,128} NetworkAccessAsra bool `json:"network_access_asra"` NetworkAccessEsr bool `json:"network_access_esr"` NetworkAccessInternet bool `json:"network_access_internet"` NetworkAccessUesa bool `json:"network_access_uesa"` - NetworkAuthType int `json:"network_auth_type,omitempty"` // -1|0|1|2|3 + NetworkAuthType int `json:"network_auth_type,omitempty" validate:"omitempty,oneof=-1 0 1 2 3"` // -1|0|1|2|3 NetworkAuthUrl string `json:"network_auth_url,omitempty"` - NetworkType int `json:"network_type,omitempty"` // 0|1|2|3|4|5|14|15 + NetworkType int `json:"network_type,omitempty" validate:"omitempty,oneof=0 1 2 3 4 5 14 15"` // 0|1|2|3|4|5|14|15 Osu []Hotspot2ConfOsu `json:"osu,omitempty"` OsuSSID string `json:"osu_ssid"` QOSMapDcsp []Hotspot2ConfQOSMapDcsp `json:"qos_map_dcsp,omitempty"` @@ -70,11 +70,11 @@ type Hotspot2Conf struct { QOSMapStatus bool `json:"qos_map_status"` RoamingConsortiumList []Hotspot2ConfRoamingConsortiumList `json:"roaming_consortium_list,omitempty"` SaveTimestamp string `json:"save_timestamp,omitempty"` - TCFilename string `json:"t_c_filename,omitempty"` // .{1,256} + TCFilename string `json:"t_c_filename,omitempty" validate:"omitempty,gte=1,lte=256"` // .{1,256} TCTimestamp int `json:"t_c_timestamp,omitempty"` - VenueGroup int `json:"venue_group,omitempty"` // 0|1|2|3|4|5|6|7|8|9|10|11 + VenueGroup int `json:"venue_group,omitempty" validate:"omitempty,oneof=0 1 2 3 4 5 6 7 8 9 10 11"` // 0|1|2|3|4|5|6|7|8|9|10|11 VenueName []Hotspot2ConfVenueName `json:"venue_name,omitempty"` - VenueType int `json:"venue_type,omitempty"` // 0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15 + VenueType int `json:"venue_type,omitempty" validate:"omitempty,oneof=0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15"` // 0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15 } func (dst *Hotspot2Conf) UnmarshalJSON(b []byte) error { @@ -127,9 +127,9 @@ func (dst *Hotspot2Conf) UnmarshalJSON(b []byte) error { } type Hotspot2ConfCapab struct { - Port int `json:"port,omitempty"` // ^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])|$ - Protocol string `json:"protocol,omitempty"` // icmp|tcp_udp|tcp|udp|esp - Status string `json:"status,omitempty"` // closed|open|unknown + Port int `json:"port,omitempty"` // ^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])|$ + Protocol string `json:"protocol,omitempty" validate:"omitempty,oneof=icmp tcp_udp tcp udp esp"` // icmp|tcp_udp|tcp|udp|esp + Status string `json:"status,omitempty" validate:"omitempty,oneof=closed open unknown"` // closed|open|unknown } func (dst *Hotspot2ConfCapab) UnmarshalJSON(b []byte) error { @@ -154,7 +154,7 @@ func (dst *Hotspot2ConfCapab) UnmarshalJSON(b []byte) error { type Hotspot2ConfCellularNetworkList struct { Mcc int `json:"mcc,omitempty"` Mnc int `json:"mnc,omitempty"` - Name string `json:"name,omitempty"` // .{1,128} + Name string `json:"name,omitempty" validate:"omitempty,gte=1,lte=128"` // .{1,128} } func (dst *Hotspot2ConfCellularNetworkList) UnmarshalJSON(b []byte) error { @@ -179,8 +179,8 @@ func (dst *Hotspot2ConfCellularNetworkList) UnmarshalJSON(b []byte) error { } type Hotspot2ConfDescription struct { - Language string `json:"language,omitempty"` // [a-z]{3} - Text string `json:"text,omitempty"` // .{1,128} + Language string `json:"language,omitempty"` // [a-z]{3} + Text string `json:"text,omitempty" validate:"omitempty,gte=1,lte=128"` // .{1,128} } func (dst *Hotspot2ConfDescription) UnmarshalJSON(b []byte) error { @@ -200,8 +200,8 @@ func (dst *Hotspot2ConfDescription) UnmarshalJSON(b []byte) error { } type Hotspot2ConfFriendlyName struct { - Language string `json:"language,omitempty"` // [a-z]{3} - Text string `json:"text,omitempty"` // .{1,128} + Language string `json:"language,omitempty"` // [a-z]{3} + Text string `json:"text,omitempty" validate:"omitempty,gte=1,lte=128"` // .{1,128} } func (dst *Hotspot2ConfFriendlyName) UnmarshalJSON(b []byte) error { @@ -221,7 +221,7 @@ func (dst *Hotspot2ConfFriendlyName) UnmarshalJSON(b []byte) error { } type Hotspot2ConfIcon struct { - Name string `json:"name,omitempty"` // .{1,128} + Name string `json:"name,omitempty" validate:"omitempty,gte=1,lte=128"` // .{1,128} } func (dst *Hotspot2ConfIcon) UnmarshalJSON(b []byte) error { @@ -242,11 +242,11 @@ func (dst *Hotspot2ConfIcon) UnmarshalJSON(b []byte) error { type Hotspot2ConfIcons struct { Data string `json:"data,omitempty"` - Filename string `json:"filename,omitempty"` // .{1,256} + Filename string `json:"filename,omitempty" validate:"omitempty,gte=1,lte=256"` // .{1,256} Height int `json:"height,omitempty"` - Language string `json:"language,omitempty"` // [a-z]{3} - Media string `json:"media,omitempty"` // .{1,256} - Name string `json:"name,omitempty"` // .{1,256} + Language string `json:"language,omitempty"` // [a-z]{3} + Media string `json:"media,omitempty" validate:"omitempty,gte=1,lte=256"` // .{1,256} + Name string `json:"name,omitempty" validate:"omitempty,gte=1,lte=256"` // .{1,256} Size int `json:"size,omitempty"` Width int `json:"width,omitempty"` } @@ -277,9 +277,9 @@ func (dst *Hotspot2ConfIcons) UnmarshalJSON(b []byte) error { type Hotspot2ConfNaiRealmList struct { AuthIDs string `json:"auth_ids,omitempty"` AuthVals string `json:"auth_vals,omitempty"` - EapMethod int `json:"eap_method,omitempty"` // 13|21|18|23|50 - Encoding int `json:"encoding,omitempty"` // 0|1 - Name string `json:"name,omitempty"` // .{1,128} + EapMethod int `json:"eap_method,omitempty" validate:"omitempty,oneof=13 21 18 23 50"` // 13|21|18|23|50 + Encoding int `json:"encoding,omitempty" validate:"omitempty,oneof=0 1"` // 0|1 + Name string `json:"name,omitempty" validate:"omitempty,gte=1,lte=128"` // .{1,128} Status bool `json:"status"` } @@ -385,8 +385,8 @@ func (dst *Hotspot2ConfQOSMapExceptions) UnmarshalJSON(b []byte) error { } type Hotspot2ConfRoamingConsortiumList struct { - Name string `json:"name,omitempty"` // .{1,128} - Oid string `json:"oid,omitempty"` // .{1,128} + Name string `json:"name,omitempty" validate:"omitempty,gte=1,lte=128"` // .{1,128} + Oid string `json:"oid,omitempty" validate:"omitempty,gte=1,lte=128"` // .{1,128} } func (dst *Hotspot2ConfRoamingConsortiumList) UnmarshalJSON(b []byte) error { diff --git a/unifi/hotspot_op.generated.go b/unifi/hotspot_op.generated.go index 13c44ca..506c523 100644 --- a/unifi/hotspot_op.generated.go +++ b/unifi/hotspot_op.generated.go @@ -25,9 +25,9 @@ type HotspotOp struct { NoDelete bool `json:"attr_no_delete,omitempty"` NoEdit bool `json:"attr_no_edit,omitempty"` - Name string `json:"name,omitempty"` // .{1,256} + Name string `json:"name,omitempty" validate:"omitempty,gte=1,lte=256"` // .{1,256} Note string `json:"note,omitempty"` - XPassword string `json:"x_password,omitempty"` // .{1,256} + XPassword string `json:"x_password,omitempty" validate:"omitempty,gte=1,lte=256"` // .{1,256} } func (dst *HotspotOp) UnmarshalJSON(b []byte) error { diff --git a/unifi/map.generated.go b/unifi/map.generated.go index 1d85cac..e86dc03 100644 --- a/unifi/map.generated.go +++ b/unifi/map.generated.go @@ -25,17 +25,17 @@ type Map struct { NoDelete bool `json:"attr_no_delete,omitempty"` NoEdit bool `json:"attr_no_edit,omitempty"` - Lat string `json:"lat,omitempty"` // ^([-]?[\d]+[.]?[\d]*([eE][-+]?[\d]+)?)$ - Lng string `json:"lng,omitempty"` // ^([-]?[\d]+[.]?[\d]*([eE][-+]?[\d]+)?)$ - MapTypeID string `json:"mapTypeId"` // satellite|roadmap|hybrid|terrain + Lat string `json:"lat,omitempty"` // ^([-]?[\d]+[.]?[\d]*([eE][-+]?[\d]+)?)$ + Lng string `json:"lng,omitempty"` // ^([-]?[\d]+[.]?[\d]*([eE][-+]?[\d]+)?)$ + MapTypeID string `json:"mapTypeId" validate:"omitempty,oneof=satellite roadmap hybrid terrain"` // satellite|roadmap|hybrid|terrain Name string `json:"name,omitempty"` OffsetLeft float64 `json:"offset_left,omitempty"` OffsetTop float64 `json:"offset_top,omitempty"` Opacity float64 `json:"opacity,omitempty"` // ^(0(\.[\d]{1,2})?|1)$|^$ Selected bool `json:"selected"` Tilt int `json:"tilt,omitempty"` - Type string `json:"type,omitempty"` // designerMap|imageMap|googleMap - Unit string `json:"unit,omitempty"` // m|f + Type string `json:"type,omitempty" validate:"omitempty,oneof=designerMap imageMap googleMap"` // designerMap|imageMap|googleMap + Unit string `json:"unit,omitempty" validate:"omitempty,oneof=m f"` // m|f Upp float64 `json:"upp,omitempty"` Zoom int `json:"zoom,omitempty"` } diff --git a/unifi/network.generated.go b/unifi/network.generated.go index 7c202b7..c5eaa45 100644 --- a/unifi/network.generated.go +++ b/unifi/network.generated.go @@ -27,33 +27,33 @@ type Network struct { AutoScaleEnabled bool `json:"auto_scale_enabled"` DHCPDBootEnabled bool `json:"dhcpd_boot_enabled"` - DHCPDBootFilename string `json:"dhcpd_boot_filename,omitempty"` // .{1,256} - DHCPDBootServer string `json:"dhcpd_boot_server"` // ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$|^$|(?=^.{3,253}$)(^((?!-)[a-zA-Z0-9-]{1,63}(?