From 28b0d59c61cfd4bb5dcad4fdbc02f10894f2d13a Mon Sep 17 00:00:00 2001 From: John Date: Mon, 21 Sep 2020 21:30:58 +0800 Subject: [PATCH] improve package gcfg/gjson for data loading --- encoding/gjson/gjson_api_new_load.go | 20 ++++++++++++++-- encoding/gjson/gjson_z_unit_internal_test.go | 18 ++++++++++++++ os/gcfg/gcfg.go | 15 +++++++++++- os/gcfg/gcfg_z_unit_test.go | 25 ++++++++------------ 4 files changed, 60 insertions(+), 18 deletions(-) diff --git a/encoding/gjson/gjson_api_new_load.go b/encoding/gjson/gjson_api_new_load.go index ff402ba61..f9a52d016 100644 --- a/encoding/gjson/gjson_api_new_load.go +++ b/encoding/gjson/gjson_api_new_load.go @@ -207,6 +207,21 @@ func LoadContentType(dataType string, data interface{}, safe ...bool) (*Json, er return doLoadContent(dataType, content, safe...) } +// IsValidDataType checks and returns whether given an valid data type for loading. +func IsValidDataType(dataType string) bool { + if dataType == "" { + return false + } + if dataType[0] == '.' { + dataType = dataType[1:] + } + switch dataType { + case "json", "js", "xml", "yaml", "yml", "toml", "ini": + return true + } + return false +} + // checkDataType automatically checks and returns the data type for . // Note that it uses regular expression for loose checking, you can use LoadXXX/LoadContentType // functions to load the content for certain content type. @@ -215,8 +230,9 @@ func checkDataType(content []byte) string { return "json" } else if gregex.IsMatch(`^<.+>[\S\s]+<.+>$`, content) { return "xml" - } else if (gregex.IsMatch(`^[\n\r]*[\w\-\s\t]+\s*:\s*".+"`, content) || gregex.IsMatch(`^[\n\r]*[\w\-\s\t]+\s*:\s*\w+`, content)) || - (gregex.IsMatch(`[\n\r]+[\w\-\s\t]+\s*:\s*".+"`, content) || gregex.IsMatch(`[\n\r]+[\w\-\s\t]+\s*:\s*\w+`, content)) { + } else if !gregex.IsMatch(`[\n\r]*[\s\t\w\-\."]+\s*=\s*"""[\s\S]+"""`, content) && !gregex.IsMatch(`[\n\r]*[\s\t\w\-\."]+\s*=\s*'''[\s\S]+'''`, content) && + ((gregex.IsMatch(`^[\n\r]*[\w\-\s\t]+\s*:\s*".+"`, content) || gregex.IsMatch(`^[\n\r]*[\w\-\s\t]+\s*:\s*\w+`, content)) || + (gregex.IsMatch(`[\n\r]+[\w\-\s\t]+\s*:\s*".+"`, content) || gregex.IsMatch(`[\n\r]+[\w\-\s\t]+\s*:\s*\w+`, content))) { return "yml" } else if !gregex.IsMatch(`^[\s\t\n\r]*;.+`, content) && !gregex.IsMatch(`[\s\t\n\r]+;.+`, content) && diff --git a/encoding/gjson/gjson_z_unit_internal_test.go b/encoding/gjson/gjson_z_unit_internal_test.go index 82f8f2acf..1fb508956 100644 --- a/encoding/gjson/gjson_z_unit_internal_test.go +++ b/encoding/gjson/gjson_z_unit_internal_test.go @@ -112,4 +112,22 @@ app_conf = ./config/app.ini `) t.Assert(checkDataType(data), "ini") }) + + gtest.C(t, func(t *gtest.T) { + data := []byte(` +# API Server +[server] + address = ":8199" + +# Jenkins +[jenkins] + url = "https://jenkins-swimlane.com" + nodeJsStaticBuildCmdTpl = """ +npm i --registry=https://registry.npm.taobao.org +wget http://consul.infra:8500/v1/kv/app_{{.SwimlaneName}}/{{.RepoName}}/.env.qa?raw=true -O ./env.qa +npm run build:qa +""" +`) + t.Assert(checkDataType(data), "toml") + }) } diff --git a/os/gcfg/gcfg.go b/os/gcfg/gcfg.go index 16e6e5eb9..7ee724fa6 100644 --- a/os/gcfg/gcfg.go +++ b/os/gcfg/gcfg.go @@ -334,7 +334,10 @@ func (c *Config) getJson(file ...string) *gjson.Json { content = "" filePath = "" ) + // The configured content can be any kind of data type different from its file type. + isFromConfigContent := true if content = GetContent(name); content == "" { + isFromConfigContent = false filePath = c.filePath(name) if filePath == "" { return nil @@ -346,7 +349,17 @@ func (c *Config) getJson(file ...string) *gjson.Json { } } // Note that the underlying configuration json object operations are concurrent safe. - if j, err := gjson.LoadContent(content, true); err == nil { + var ( + j *gjson.Json + err error + ) + dataType := gfile.ExtName(name) + if gjson.IsValidDataType(dataType) && !isFromConfigContent { + j, err = gjson.LoadContentType(dataType, content, true) + } else { + j, err = gjson.LoadContent(content, true) + } + if err == nil { j.SetViolenceCheck(c.vc) // Add monitor for this configuration file, // any changes of this file will refresh its cache in Config object. diff --git a/os/gcfg/gcfg_z_unit_test.go b/os/gcfg/gcfg_z_unit_test.go index 3cec43324..2579f7e9d 100644 --- a/os/gcfg/gcfg_z_unit_test.go +++ b/os/gcfg/gcfg_z_unit_test.go @@ -231,21 +231,16 @@ func Test_SetFileName(t *testing.T) { func Test_Instance(t *testing.T) { config := ` -{ - "array": [ - 1, - 2, - 3 - ], - "redis": { - "cache": "127.0.0.1:6379,1", - "disk": "127.0.0.1:6379,0" - }, - "v1": 1, - "v2": "true", - "v3": "off", - "v4": "1.234" -} +array = [1.0, 2.0, 3.0] +v1 = 1.0 +v2 = "true" +v3 = "off" +v4 = "1.234" + +[redis] + cache = "127.0.0.1:6379,1" + disk = "127.0.0.1:6379,0" + ` gtest.C(t, func(t *gtest.T) { path := gcfg.DEFAULT_CONFIG_FILE