mirror of
https://github.com/jc21/nginx-proxy-manager.git
synced 2024-08-30 18:22:48 +00:00
Refactor some reflection
This commit is contained in:
parent
d437c6b743
commit
aae95798b2
@ -9,7 +9,6 @@ import (
|
||||
|
||||
c "npm/internal/api/context"
|
||||
h "npm/internal/api/http"
|
||||
"npm/internal/entity"
|
||||
"npm/internal/model"
|
||||
"npm/internal/tags"
|
||||
"npm/internal/util"
|
||||
@ -23,7 +22,7 @@ import (
|
||||
// After we have determined what the Filters are to be, they are saved on the Context
|
||||
// to be used later in other endpoints.
|
||||
func ListQuery(obj interface{}) func(http.Handler) http.Handler {
|
||||
schemaData := entity.GetFilterSchema(obj, true)
|
||||
schemaData := tags.GetFilterSchema(obj)
|
||||
filterMap := tags.GetFilterMap(obj)
|
||||
|
||||
return func(next http.Handler) http.Handler {
|
||||
|
@ -2,8 +2,8 @@ package accesslist
|
||||
|
||||
import (
|
||||
"npm/internal/database"
|
||||
"npm/internal/entity"
|
||||
"npm/internal/entity/user"
|
||||
"npm/internal/model"
|
||||
"npm/internal/types"
|
||||
|
||||
"github.com/rotisserie/eris"
|
||||
@ -11,7 +11,7 @@ import (
|
||||
|
||||
// Model is the model
|
||||
type Model struct {
|
||||
entity.ModelBase
|
||||
model.ModelBase
|
||||
UserID uint `json:"user_id" gorm:"column:user_id" filter:"user_id,integer"`
|
||||
Name string `json:"name" gorm:"column:name" filter:"name,string"`
|
||||
Meta types.JSONB `json:"meta" gorm:"column:meta"`
|
||||
|
@ -2,7 +2,7 @@ package auth
|
||||
|
||||
import (
|
||||
"npm/internal/database"
|
||||
"npm/internal/entity"
|
||||
"npm/internal/model"
|
||||
|
||||
"github.com/rotisserie/eris"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
@ -15,7 +15,7 @@ const (
|
||||
|
||||
// Model is the model
|
||||
type Model struct {
|
||||
entity.ModelBase
|
||||
model.ModelBase
|
||||
UserID uint `json:"user_id" gorm:"column:user_id"`
|
||||
Type string `json:"type" gorm:"column:type;default:password"`
|
||||
Secret string `json:"secret,omitempty" gorm:"column:secret"`
|
||||
|
@ -10,11 +10,11 @@ import (
|
||||
"npm/internal/acme"
|
||||
"npm/internal/config"
|
||||
"npm/internal/database"
|
||||
"npm/internal/entity"
|
||||
"npm/internal/entity/certificateauthority"
|
||||
"npm/internal/entity/dnsprovider"
|
||||
"npm/internal/entity/user"
|
||||
"npm/internal/logger"
|
||||
"npm/internal/model"
|
||||
"npm/internal/serverevents"
|
||||
"npm/internal/types"
|
||||
"npm/internal/util"
|
||||
@ -44,7 +44,7 @@ const (
|
||||
|
||||
// Model is the model
|
||||
type Model struct {
|
||||
entity.ModelBase
|
||||
model.ModelBase
|
||||
UserID uint `json:"user_id" gorm:"column:user_id" filter:"user_id,integer"`
|
||||
Type string `json:"type" gorm:"column:type" filter:"type,string"`
|
||||
CertificateAuthorityID types.NullableDBUint `json:"certificate_authority_id" gorm:"column:certificate_authority_id" filter:"certificate_authority_id,integer"`
|
||||
|
@ -5,15 +5,15 @@ import (
|
||||
"path/filepath"
|
||||
|
||||
"npm/internal/database"
|
||||
"npm/internal/entity"
|
||||
"npm/internal/errors"
|
||||
"npm/internal/model"
|
||||
|
||||
"github.com/rotisserie/eris"
|
||||
)
|
||||
|
||||
// Model is the model
|
||||
type Model struct {
|
||||
entity.ModelBase
|
||||
model.ModelBase
|
||||
Name string `json:"name" gorm:"column:name" filter:"name,string"`
|
||||
AcmeshServer string `json:"acmesh_server" gorm:"column:acmesh_server" filter:"acmesh_server,string"`
|
||||
CABundle string `json:"ca_bundle" gorm:"column:ca_bundle" filter:"ca_bundle,string"`
|
||||
|
@ -5,8 +5,8 @@ import (
|
||||
|
||||
"npm/internal/database"
|
||||
"npm/internal/dnsproviders"
|
||||
"npm/internal/entity"
|
||||
"npm/internal/logger"
|
||||
"npm/internal/model"
|
||||
"npm/internal/types"
|
||||
|
||||
"github.com/rotisserie/eris"
|
||||
@ -14,7 +14,7 @@ import (
|
||||
|
||||
// Model is the model
|
||||
type Model struct {
|
||||
entity.ModelBase
|
||||
model.ModelBase
|
||||
UserID uint `json:"user_id" gorm:"column:user_id" filter:"user_id,integer"`
|
||||
Name string `json:"name" gorm:"column:name" filter:"name,string"`
|
||||
AcmeshName string `json:"acmesh_name" gorm:"column:acmesh_name" filter:"acmesh_name,string"`
|
||||
|
@ -9,7 +9,7 @@ import (
|
||||
func GetFilterMap(m interface{}, includeBaseEntity bool) map[string]model.FilterMapValue {
|
||||
filterMap := tags.GetFilterMap(m)
|
||||
if includeBaseEntity {
|
||||
return mergeFilterMaps(tags.GetFilterMap(ModelBase{}), filterMap)
|
||||
return mergeFilterMaps(tags.GetFilterMap(model.ModelBase{}), filterMap)
|
||||
}
|
||||
|
||||
return filterMap
|
||||
|
@ -1,252 +0,0 @@
|
||||
package entity
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"npm/internal/logger"
|
||||
"npm/internal/util"
|
||||
|
||||
"github.com/rotisserie/eris"
|
||||
)
|
||||
|
||||
// GetFilterSchema creates a jsonschema for validating filters, based on the model
|
||||
// object given and by reading the struct "filter" tags.
|
||||
func GetFilterSchema(m interface{}, includeBaseEntity bool) string {
|
||||
var schemas []string
|
||||
t := reflect.TypeOf(m)
|
||||
|
||||
if t.Kind() != reflect.Struct {
|
||||
logger.Error("GetFilterSchemaError", eris.Errorf("%v type can't have attributes inspected", t.Kind()))
|
||||
return ""
|
||||
}
|
||||
|
||||
// The base entity model
|
||||
if includeBaseEntity {
|
||||
b := reflect.TypeOf(ModelBase{})
|
||||
for i := 0; i < b.NumField(); i++ {
|
||||
bField := b.Field(i)
|
||||
bFilterTag := bField.Tag.Get("filter")
|
||||
if bFilterTag != "" && bFilterTag != "-" {
|
||||
schemas = append(schemas, getFilterTagSchema(bFilterTag))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The actual interface
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
field := t.Field(i)
|
||||
filterTag := field.Tag.Get("filter")
|
||||
|
||||
if filterTag != "" && filterTag != "-" {
|
||||
schemas = append(schemas, getFilterTagSchema(filterTag))
|
||||
}
|
||||
}
|
||||
|
||||
return util.PrettyPrintJSON(newFilterSchema(schemas))
|
||||
}
|
||||
|
||||
func getFilterTagSchema(filterTag string) string {
|
||||
// split out tag value "field,filtreType"
|
||||
// with a default filter type of string
|
||||
items := strings.Split(filterTag, ",")
|
||||
if len(items) == 1 {
|
||||
items = append(items, "string")
|
||||
}
|
||||
|
||||
switch items[1] {
|
||||
case "number":
|
||||
fallthrough
|
||||
case "int":
|
||||
fallthrough
|
||||
case "integer":
|
||||
return intFieldSchema(items[0])
|
||||
case "bool":
|
||||
fallthrough
|
||||
case "boolean":
|
||||
return boolFieldSchema(items[0])
|
||||
case "date":
|
||||
return dateFieldSchema(items[0])
|
||||
case "regex":
|
||||
if len(items) < 3 {
|
||||
items = append(items, ".*")
|
||||
}
|
||||
return regexFieldSchema(items[0], items[2])
|
||||
|
||||
default:
|
||||
return stringFieldSchema(items[0])
|
||||
}
|
||||
}
|
||||
|
||||
// newFilterSchema is the main method to specify a new Filter Schema for use in Middleware
|
||||
func newFilterSchema(fieldSchemas []string) string {
|
||||
return fmt.Sprintf(baseFilterSchema, strings.Join(fieldSchemas, ", "))
|
||||
}
|
||||
|
||||
// boolFieldSchema returns the Field Schema for a Boolean accepted value field
|
||||
func boolFieldSchema(fieldName string) string {
|
||||
return fmt.Sprintf(`{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"field": {
|
||||
"type": "string",
|
||||
"pattern": "^%s$"
|
||||
},
|
||||
"modifier": %s,
|
||||
"value": {
|
||||
"oneOf": [
|
||||
%s,
|
||||
{
|
||||
"type": "array",
|
||||
"items": %s
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}`, fieldName, boolModifiers, filterBool, filterBool)
|
||||
}
|
||||
|
||||
// intFieldSchema returns the Field Schema for a Integer accepted value field
|
||||
func intFieldSchema(fieldName string) string {
|
||||
return fmt.Sprintf(`{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"field": {
|
||||
"type": "string",
|
||||
"pattern": "^%s$"
|
||||
},
|
||||
"modifier": %s,
|
||||
"value": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"pattern": "^[0-9]+$"
|
||||
},
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9]+$"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}`, fieldName, allModifiers)
|
||||
}
|
||||
|
||||
// stringFieldSchema returns the Field Schema for a String accepted value field
|
||||
func stringFieldSchema(fieldName string) string {
|
||||
return fmt.Sprintf(`{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"field": {
|
||||
"type": "string",
|
||||
"pattern": "^%s$"
|
||||
},
|
||||
"modifier": %s,
|
||||
"value": {
|
||||
"oneOf": [
|
||||
%s,
|
||||
{
|
||||
"type": "array",
|
||||
"items": %s
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}`, fieldName, stringModifiers, filterString, filterString)
|
||||
}
|
||||
|
||||
// regexFieldSchema returns the Field Schema for a String accepted value field matching a Regex
|
||||
func regexFieldSchema(fieldName string, regex string) string {
|
||||
return fmt.Sprintf(`{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"field": {
|
||||
"type": "string",
|
||||
"pattern": "^%s$"
|
||||
},
|
||||
"modifier": %s,
|
||||
"value": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"pattern": "%s"
|
||||
},
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"pattern": "%s"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}`, fieldName, stringModifiers, regex, regex)
|
||||
}
|
||||
|
||||
// dateFieldSchema returns the Field Schema for a String accepted value field matching a Date format
|
||||
func dateFieldSchema(fieldName string) string {
|
||||
return fmt.Sprintf(`{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"field": {
|
||||
"type": "string",
|
||||
"pattern": "^%s$"
|
||||
},
|
||||
"modifier": %s,
|
||||
"value": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"pattern": "^([12]\\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01]))$"
|
||||
},
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"pattern": "^([12]\\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01]))$"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}`, fieldName, allModifiers)
|
||||
}
|
||||
|
||||
const allModifiers = `{
|
||||
"type": "string",
|
||||
"pattern": "^(equals|not|contains|starts|ends|in|notin|min|max|greater|less)$"
|
||||
}`
|
||||
|
||||
const boolModifiers = `{
|
||||
"type": "string",
|
||||
"pattern": "^(equals|not)$"
|
||||
}`
|
||||
|
||||
const stringModifiers = `{
|
||||
"type": "string",
|
||||
"pattern": "^(equals|not|contains|starts|ends|in|notin)$"
|
||||
}`
|
||||
|
||||
const filterBool = `{
|
||||
"type": "string",
|
||||
"pattern": "^(TRUE|true|t|yes|y|on|1|FALSE|f|false|n|no|off|0)$"
|
||||
}`
|
||||
|
||||
const filterString = `{
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
}`
|
||||
|
||||
const baseFilterSchema = `{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"oneOf": [
|
||||
%s
|
||||
]
|
||||
}
|
||||
}`
|
@ -3,11 +3,11 @@ package host
|
||||
import (
|
||||
"fmt"
|
||||
"npm/internal/database"
|
||||
"npm/internal/entity"
|
||||
"npm/internal/entity/certificate"
|
||||
"npm/internal/entity/nginxtemplate"
|
||||
"npm/internal/entity/upstream"
|
||||
"npm/internal/entity/user"
|
||||
"npm/internal/model"
|
||||
"npm/internal/status"
|
||||
"npm/internal/types"
|
||||
"npm/internal/util"
|
||||
@ -26,7 +26,7 @@ const (
|
||||
|
||||
// Model is the model
|
||||
type Model struct {
|
||||
entity.ModelBase
|
||||
model.ModelBase
|
||||
UserID uint `json:"user_id" gorm:"column:user_id" filter:"user_id,integer"`
|
||||
Type string `json:"type" gorm:"column:type" filter:"type,string"`
|
||||
NginxTemplateID uint `json:"nginx_template_id" gorm:"column:nginx_template_id" filter:"nginx_template_id,integer"`
|
||||
|
@ -2,14 +2,14 @@ package nginxtemplate
|
||||
|
||||
import (
|
||||
"npm/internal/database"
|
||||
"npm/internal/entity"
|
||||
"npm/internal/model"
|
||||
|
||||
"github.com/rotisserie/eris"
|
||||
)
|
||||
|
||||
// Model is the model
|
||||
type Model struct {
|
||||
entity.ModelBase
|
||||
model.ModelBase
|
||||
UserID uint `json:"user_id" gorm:"column:user_id" filter:"user_id,integer"`
|
||||
Name string `json:"name" gorm:"column:name" filter:"name,string"`
|
||||
Type string `json:"type" gorm:"column:type" filter:"type,string"`
|
||||
|
@ -4,14 +4,14 @@ import (
|
||||
"strings"
|
||||
|
||||
"npm/internal/database"
|
||||
"npm/internal/entity"
|
||||
"npm/internal/model"
|
||||
|
||||
"gorm.io/datatypes"
|
||||
)
|
||||
|
||||
// Model is the model
|
||||
type Model struct {
|
||||
entity.ModelBase
|
||||
model.ModelBase
|
||||
Name string `json:"name" gorm:"column:name" filter:"name,string"`
|
||||
Description string `json:"description" gorm:"column:description" filter:"description,string"`
|
||||
Value datatypes.JSON `json:"value" gorm:"column:value"`
|
||||
|
@ -2,7 +2,7 @@ package stream
|
||||
|
||||
import (
|
||||
"npm/internal/database"
|
||||
"npm/internal/entity"
|
||||
"npm/internal/model"
|
||||
"npm/internal/types"
|
||||
|
||||
"github.com/rotisserie/eris"
|
||||
@ -10,7 +10,7 @@ import (
|
||||
|
||||
// Model is the model
|
||||
type Model struct {
|
||||
entity.ModelBase
|
||||
model.ModelBase
|
||||
ExpiresOn types.DBDate `json:"expires_on" gorm:"column:expires_on" filter:"expires_on,integer"`
|
||||
UserID uint `json:"user_id" gorm:"column:user_id" filter:"user_id,integer"`
|
||||
Provider string `json:"provider" gorm:"column:provider" filter:"provider,string"`
|
||||
|
@ -4,10 +4,10 @@ import (
|
||||
"strings"
|
||||
|
||||
"npm/internal/database"
|
||||
"npm/internal/entity"
|
||||
"npm/internal/entity/nginxtemplate"
|
||||
"npm/internal/entity/upstreamserver"
|
||||
"npm/internal/entity/user"
|
||||
"npm/internal/model"
|
||||
"npm/internal/status"
|
||||
"npm/internal/util"
|
||||
|
||||
@ -17,7 +17,7 @@ import (
|
||||
// Model is the model
|
||||
// See: http://nginx.org/en/docs/http/ngx_http_upstream_module.html#upstream
|
||||
type Model struct {
|
||||
entity.ModelBase
|
||||
model.ModelBase
|
||||
UserID uint `json:"user_id" gorm:"column:user_id" filter:"user_id,integer"`
|
||||
Name string `json:"name" gorm:"column:name" filter:"name,string"`
|
||||
NginxTemplateID uint `json:"nginx_template_id" gorm:"column:nginx_template_id" filter:"nginx_template_id,integer"`
|
||||
|
@ -2,12 +2,12 @@ package upstreamserver
|
||||
|
||||
import (
|
||||
"npm/internal/database"
|
||||
"npm/internal/entity"
|
||||
"npm/internal/model"
|
||||
)
|
||||
|
||||
// Model is the model
|
||||
type Model struct {
|
||||
entity.ModelBase
|
||||
model.ModelBase
|
||||
UpstreamID uint `json:"upstream_id" gorm:"column:upstream_id" filter:"upstream_id,integer"`
|
||||
Server string `json:"server" gorm:"column:server" filter:"server,string"`
|
||||
Weight int `json:"weight" gorm:"column:weight" filter:"weight,integer"`
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"npm/internal/entity"
|
||||
"npm/internal/entity/auth"
|
||||
"npm/internal/errors"
|
||||
"npm/internal/model"
|
||||
"npm/internal/util"
|
||||
|
||||
"github.com/drexedam/gravatar"
|
||||
@ -15,7 +16,7 @@ import (
|
||||
|
||||
// Model is the model
|
||||
type Model struct {
|
||||
entity.ModelBase
|
||||
model.ModelBase
|
||||
Name string `json:"name" gorm:"column:name" filter:"name,string"`
|
||||
Nickname string `json:"nickname" gorm:"column:nickname" filter:"nickname,string"`
|
||||
Email string `json:"email" gorm:"column:email" filter:"email,email"`
|
||||
|
@ -2,14 +2,14 @@ package jwt
|
||||
|
||||
import (
|
||||
"npm/internal/database"
|
||||
"npm/internal/entity"
|
||||
"npm/internal/model"
|
||||
)
|
||||
|
||||
var currentKeys KeysModel
|
||||
|
||||
// KeysModel is the model
|
||||
type KeysModel struct {
|
||||
entity.ModelBase
|
||||
model.ModelBase
|
||||
PublicKey string `gorm:"column:public_key"`
|
||||
PrivateKey string `gorm:"column:private_key"`
|
||||
}
|
||||
|
@ -9,6 +9,8 @@ type Filter struct {
|
||||
|
||||
// FilterMapValue ...
|
||||
type FilterMapValue struct {
|
||||
Type string
|
||||
Field string
|
||||
Type string
|
||||
Field string
|
||||
Schema string
|
||||
Model string
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
package entity
|
||||
package model
|
||||
|
||||
import (
|
||||
"gorm.io/plugin/soft_delete"
|
@ -3,9 +3,9 @@ package nginx
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"npm/internal/entity"
|
||||
"npm/internal/entity/certificate"
|
||||
"npm/internal/entity/host"
|
||||
"npm/internal/model"
|
||||
"npm/internal/types"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@ -47,7 +47,7 @@ server {
|
||||
IsDisabled: false,
|
||||
},
|
||||
cert: certificate.Model{
|
||||
ModelBase: entity.ModelBase{
|
||||
ModelBase: model.ModelBase{
|
||||
ID: 77,
|
||||
},
|
||||
Status: certificate.StatusProvided,
|
||||
@ -65,7 +65,7 @@ server {
|
||||
IsDisabled: false,
|
||||
},
|
||||
cert: certificate.Model{
|
||||
ModelBase: entity.ModelBase{
|
||||
ModelBase: model.ModelBase{
|
||||
ID: 66,
|
||||
},
|
||||
Status: certificate.StatusProvided,
|
||||
|
@ -1,11 +1,16 @@
|
||||
package tags
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"npm/internal/logger"
|
||||
"npm/internal/model"
|
||||
"npm/internal/util"
|
||||
|
||||
"github.com/rotisserie/eris"
|
||||
)
|
||||
|
||||
func GetFilterMap(m interface{}) map[string]model.FilterMapValue {
|
||||
@ -16,10 +21,21 @@ func GetFilterMap(m interface{}) map[string]model.FilterMapValue {
|
||||
|
||||
var filterMap = make(map[string]model.FilterMapValue)
|
||||
|
||||
// If this is an entity model (and it probably is)
|
||||
// then include the base model as well
|
||||
if strings.Contains(name, ".Model") && !strings.Contains(name, "ModelBase") {
|
||||
filterMap = GetFilterMap(model.ModelBase{})
|
||||
}
|
||||
|
||||
// TypeOf returns the reflection Type that represents the dynamic type of variable.
|
||||
// If variable is a nil interface value, TypeOf returns nil.
|
||||
t := reflect.TypeOf(m)
|
||||
|
||||
if t.Kind() != reflect.Struct {
|
||||
logger.Error("GetFilterMapError", eris.Errorf("%v type can't have attributes inspected", t.Kind()))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Iterate over all available fields and read the tag value
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
// Get the field, returns https://golang.org/pkg/reflect/#StructField
|
||||
@ -28,26 +44,247 @@ func GetFilterMap(m interface{}) map[string]model.FilterMapValue {
|
||||
// Get the field tag value
|
||||
filterTag := field.Tag.Get("filter")
|
||||
dbTag := field.Tag.Get("gorm")
|
||||
if filterTag != "" && dbTag != "" && dbTag != "-" && filterTag != "-" {
|
||||
// db can have many parts, we need to pull out the "column:value" part
|
||||
dbField := field.Name
|
||||
r := regexp.MustCompile(`(?:^|;)column:([^;|$]+)(?:$|;)`)
|
||||
if matches := r.FindStringSubmatch(dbTag); len(matches) > 1 {
|
||||
dbField = matches[1]
|
||||
}
|
||||
// Filter tag can be a 2 part thing: name,type
|
||||
// ie: account_id,integer
|
||||
// So we need to split and use the first part
|
||||
f := model.FilterMapValue{
|
||||
Model: name,
|
||||
}
|
||||
|
||||
// Filter -> Schema mapping
|
||||
if filterTag != "" && filterTag != "-" {
|
||||
f.Schema = getFilterTagSchema(filterTag)
|
||||
parts := strings.Split(filterTag, ",")
|
||||
if len(parts) > 1 {
|
||||
filterMap[parts[0]] = model.FilterMapValue{
|
||||
Type: parts[1],
|
||||
Field: dbField,
|
||||
|
||||
// Filter -> DB Field mapping
|
||||
if dbTag != "" && dbTag != "-" {
|
||||
// db can have many parts, we need to pull out the "column:value" part
|
||||
f.Field = field.Name
|
||||
r := regexp.MustCompile(`(?:^|;)column:([^;|$]+)(?:$|;)`)
|
||||
if matches := r.FindStringSubmatch(dbTag); len(matches) > 1 {
|
||||
f.Field = matches[1]
|
||||
}
|
||||
// Filter tag can be a 2 part thing: name,type
|
||||
// ie: account_id,integer
|
||||
// So we need to split and use the first part
|
||||
if len(parts) > 1 {
|
||||
f.Type = parts[1]
|
||||
}
|
||||
}
|
||||
filterMap[parts[0]] = f
|
||||
}
|
||||
}
|
||||
|
||||
setCache(name, filterMap)
|
||||
return filterMap
|
||||
}
|
||||
|
||||
func getFilterTagSchema(filterTag string) string {
|
||||
// split out tag value "field,filtreType"
|
||||
// with a default filter type of string
|
||||
items := strings.Split(filterTag, ",")
|
||||
if len(items) == 1 {
|
||||
items = append(items, "string")
|
||||
}
|
||||
|
||||
switch items[1] {
|
||||
case "number":
|
||||
fallthrough
|
||||
case "int":
|
||||
fallthrough
|
||||
case "integer":
|
||||
return intFieldSchema(items[0])
|
||||
case "bool":
|
||||
fallthrough
|
||||
case "boolean":
|
||||
return boolFieldSchema(items[0])
|
||||
case "date":
|
||||
return dateFieldSchema(items[0])
|
||||
case "regex":
|
||||
if len(items) < 3 {
|
||||
items = append(items, ".*")
|
||||
}
|
||||
return regexFieldSchema(items[0], items[2])
|
||||
|
||||
default:
|
||||
return stringFieldSchema(items[0])
|
||||
}
|
||||
}
|
||||
|
||||
// GetFilterSchema creates a jsonschema for validating filters, based on the model
|
||||
// object given and by reading the struct "filter" tags.
|
||||
func GetFilterSchema(m interface{}) string {
|
||||
filterMap := GetFilterMap(m)
|
||||
schemas := make([]string, 0)
|
||||
|
||||
for _, f := range filterMap {
|
||||
schemas = append(schemas, f.Schema)
|
||||
}
|
||||
|
||||
str := fmt.Sprintf(baseFilterSchema, strings.Join(schemas, ", "))
|
||||
return util.PrettyPrintJSON(str)
|
||||
}
|
||||
|
||||
// boolFieldSchema returns the Field Schema for a Boolean accepted value field
|
||||
func boolFieldSchema(fieldName string) string {
|
||||
return fmt.Sprintf(`{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"field": {
|
||||
"type": "string",
|
||||
"pattern": "^%s$"
|
||||
},
|
||||
"modifier": %s,
|
||||
"value": {
|
||||
"oneOf": [
|
||||
%s,
|
||||
{
|
||||
"type": "array",
|
||||
"items": %s
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}`, fieldName, boolModifiers, filterBool, filterBool)
|
||||
}
|
||||
|
||||
// intFieldSchema returns the Field Schema for a Integer accepted value field
|
||||
func intFieldSchema(fieldName string) string {
|
||||
return fmt.Sprintf(`{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"field": {
|
||||
"type": "string",
|
||||
"pattern": "^%s$"
|
||||
},
|
||||
"modifier": %s,
|
||||
"value": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"pattern": "^[0-9]+$"
|
||||
},
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9]+$"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}`, fieldName, allModifiers)
|
||||
}
|
||||
|
||||
// stringFieldSchema returns the Field Schema for a String accepted value field
|
||||
func stringFieldSchema(fieldName string) string {
|
||||
return fmt.Sprintf(`{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"field": {
|
||||
"type": "string",
|
||||
"pattern": "^%s$"
|
||||
},
|
||||
"modifier": %s,
|
||||
"value": {
|
||||
"oneOf": [
|
||||
%s,
|
||||
{
|
||||
"type": "array",
|
||||
"items": %s
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}`, fieldName, stringModifiers, filterString, filterString)
|
||||
}
|
||||
|
||||
// regexFieldSchema returns the Field Schema for a String accepted value field matching a Regex
|
||||
func regexFieldSchema(fieldName string, regex string) string {
|
||||
return fmt.Sprintf(`{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"field": {
|
||||
"type": "string",
|
||||
"pattern": "^%s$"
|
||||
},
|
||||
"modifier": %s,
|
||||
"value": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"pattern": "%s"
|
||||
},
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"pattern": "%s"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}`, fieldName, stringModifiers, regex, regex)
|
||||
}
|
||||
|
||||
// dateFieldSchema returns the Field Schema for a String accepted value field matching a Date format
|
||||
func dateFieldSchema(fieldName string) string {
|
||||
return fmt.Sprintf(`{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"field": {
|
||||
"type": "string",
|
||||
"pattern": "^%s$"
|
||||
},
|
||||
"modifier": %s,
|
||||
"value": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"pattern": "^([12]\\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01]))$"
|
||||
},
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"pattern": "^([12]\\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01]))$"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}`, fieldName, allModifiers)
|
||||
}
|
||||
|
||||
const allModifiers = `{
|
||||
"type": "string",
|
||||
"pattern": "^(equals|not|contains|starts|ends|in|notin|min|max|greater|less)$"
|
||||
}`
|
||||
|
||||
const boolModifiers = `{
|
||||
"type": "string",
|
||||
"pattern": "^(equals|not)$"
|
||||
}`
|
||||
|
||||
const stringModifiers = `{
|
||||
"type": "string",
|
||||
"pattern": "^(equals|not|contains|starts|ends|in|notin)$"
|
||||
}`
|
||||
|
||||
const filterBool = `{
|
||||
"type": "string",
|
||||
"pattern": "^(TRUE|true|t|yes|y|on|1|FALSE|f|false|n|no|off|0)$"
|
||||
}`
|
||||
|
||||
const filterString = `{
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
}`
|
||||
|
||||
const baseFilterSchema = `{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"oneOf": [
|
||||
%s
|
||||
]
|
||||
}
|
||||
}`
|
||||
|
Loading…
Reference in New Issue
Block a user