diff --git a/examples/go-hello.go b/examples/go-hello.go index f7f26f5..7131e17 100644 --- a/examples/go-hello.go +++ b/examples/go-hello.go @@ -21,7 +21,7 @@ var Version = "0.2" var Priority = 1 type Config struct { - Message string + Message string `json:"message" kong:"default=hello,required=true"` } func New() interface{} { diff --git a/server/rpc.go b/server/rpc.go index ebbd168..aa8eac9 100644 --- a/server/rpc.go +++ b/server/rpc.go @@ -6,6 +6,8 @@ import ( "strings" "sync" "time" + + "slices" ) type rpcHandler struct { @@ -131,7 +133,9 @@ func getSchemaDict(t reflect.Type) schemaDict { if name == "" { name = strings.ToLower(field.Name) } - fieldsArray = append(fieldsArray, schemaDict{name: typeDecl}) + // Apply Kong tags to the field's type declaration + typeDeclWithKong := withKongTagFields(typeDecl, field) + fieldsArray = append(fieldsArray, schemaDict{name: typeDeclWithKong}) } return schemaDict{ "type": "record", @@ -142,6 +146,33 @@ func getSchemaDict(t reflect.Type) schemaDict { return nil } +func withKongTagFields(current schemaDict, field reflect.StructField) schemaDict { + var validFields = []string{"required", "default"} + var boolFields = []string{"required"} + result := current + tag := field.Tag.Get("kong") + if tag == "" { + return result + } + + tagMap := strings.Split(tag, ",") + for _, tag := range tagMap { + parts := strings.Split(tag, "=") + if len(parts) != 2 { + continue + } + if slices.Contains(validFields, parts[0]) { + result[parts[0]] = parts[1] + } + + if slices.Contains(boolFields, parts[0]) { + result[parts[0]] = parts[1] == "true" + } + } + + return result +} + type pluginInfo struct { Name string // plugin name ModTime time.Time `codec:",omitempty"` // plugin file modification time @@ -158,18 +189,27 @@ func (rh *rpcHandler) getInfo() (info pluginInfo, err error) { return } + schema, err := rh.getSchema(name) + if err != nil { + return + } + info = pluginInfo{ - Name: name, - Phases: getHandlerNames(rh.configType), - Schema: schemaDict{ - "name": name, - "fields": []schemaDict{ - {"config": getSchemaDict(rh.configType)}, - }, - }, + Name: name, + Phases: getHandlerNames(rh.configType), + Schema: schema, Version: rh.version, Priority: rh.priority, } return } + +func (rh *rpcHandler) getSchema(name string) (schema schemaDict, err error) { + return schemaDict{ + "name": name, + "fields": []schemaDict{ + {"config": getSchemaDict(rh.configType)}, + }, + }, nil +} diff --git a/server/rpc_test.go b/server/rpc_test.go new file mode 100644 index 0000000..dd4f398 --- /dev/null +++ b/server/rpc_test.go @@ -0,0 +1,28 @@ +package server + +import ( + "reflect" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestGetSchemaDict(t *testing.T) { + type Config struct { + JWKSURL string `json:"jwks_url" kong:"required=true,default=https://example.com/.well-known/jwks.json"` + CacheTTL int `json:"cache_ttl" kong:"default=3600"` + AuthorizationHeader string `json:"authorization_header"` + NoJsonTag string `kong:"default=no_json_tag"` + } + + schema := GetSchemaDict(reflect.TypeOf(Config{})) + assert.Equal(t, schema, schemaDict{ + "type": "record", + "fields": []schemaDict{ + {"jwks_url": schemaDict{"type": "string", "required": true, "default": "https://example.com/.well-known/jwks.json"}}, + {"cache_ttl": schemaDict{"type": "integer", "default": "3600"}}, + {"authorization_header": schemaDict{"type": "string"}}, + {"nojsontag": schemaDict{"type": "string", "default": "no_json_tag"}}, + }, + }) +}