github.com/oam-dev/kubevela@v1.9.11/pkg/velaql/parse.go (about) 1 /* 2 Copyright 2021. The KubeVela Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package velaql 18 19 import ( 20 "regexp" 21 "strconv" 22 "strings" 23 24 "github.com/pkg/errors" 25 26 "github.com/kubevela/workflow/pkg/cue/model/value" 27 28 "github.com/oam-dev/kubevela/pkg/utils" 29 ) 30 31 // QueryView contains query data 32 type QueryView struct { 33 View string 34 Parameter map[string]interface{} 35 Export string 36 } 37 38 const ( 39 // PatternQL is the pattern string of velaQL, velaQL's query syntax is `ViewName{key1=value1 ,key2="value2",}.Export` 40 PatternQL = `(?P<view>[a-z0-9](?:[a-z0-9\-]{0,61}[a-z0-9])?)(?P<parameter>{.*?})?\.?(?P<export>[_a-zA-Z][\._a-zA-Z0-9\[\]]*)?` 41 // PatternKV is the pattern string of parameter 42 PatternKV = `(?P<key>[^=]+)=(?P<value>[^=]*?)(?:,|$)` 43 // KeyWordView represent view keyword 44 KeyWordView = "view" 45 // KeyWordParameter represent parameter keyword 46 KeyWordParameter = "parameter" 47 // KeyWordTemplate represents template keyword 48 KeyWordTemplate = "template" 49 // KeyWordExport represent export keyword 50 KeyWordExport = "export" 51 // DefaultExportValue is the default Export value 52 DefaultExportValue = "status" 53 ) 54 55 var ( 56 qlRegexp *regexp.Regexp 57 kvRegexp *regexp.Regexp 58 ) 59 60 func init() { 61 qlRegexp = regexp.MustCompile(PatternQL) 62 kvRegexp = regexp.MustCompile(PatternKV) 63 } 64 65 // ParseVelaQL parse velaQL to QueryView 66 func ParseVelaQL(ql string) (QueryView, error) { 67 qv := QueryView{ 68 Export: DefaultExportValue, 69 } 70 71 groupNames := qlRegexp.SubexpNames() 72 matched := qlRegexp.FindStringSubmatch(ql) 73 if len(matched) != len(groupNames) || (len(matched) != 0 && matched[0] != ql) { 74 return qv, errors.New("fail to parse the velaQL") 75 } 76 77 result := make(map[string]string, len(groupNames)) 78 for i, name := range groupNames { 79 if i != 0 && name != "" { 80 result[name] = strings.TrimSpace(matched[i]) 81 } 82 } 83 84 if len(result["view"]) == 0 { 85 return qv, errors.New("view name shouldn't be empty") 86 } 87 88 qv.View = result[KeyWordView] 89 if len(result[KeyWordExport]) != 0 { 90 qv.Export = result[KeyWordExport] 91 } 92 var err error 93 qv.Parameter, err = ParseParameter(result[KeyWordParameter]) 94 if err != nil { 95 return qv, err 96 } 97 return qv, nil 98 } 99 100 // ParseVelaQLFromPath will parse a velaQL file path to QueryView 101 func ParseVelaQLFromPath(velaQLViewPath string) (*QueryView, error) { 102 body, err := utils.ReadRemoteOrLocalPath(velaQLViewPath, false) 103 if err != nil { 104 return nil, errors.Errorf("read view file from %s: %v", velaQLViewPath, err) 105 } 106 107 val, err := value.NewValue(string(body), nil, "") 108 if err != nil { 109 return nil, errors.Errorf("new value for view: %v", err) 110 } 111 112 var expStr string 113 exp, err := val.LookupValue(KeyWordExport) 114 if err == nil { 115 expStr, err = exp.String() 116 if err != nil { 117 expStr = DefaultExportValue 118 } 119 } else { 120 expStr = DefaultExportValue 121 } 122 123 return &QueryView{ 124 View: string(body), 125 Parameter: nil, 126 Export: strings.Trim(strings.TrimSpace(expStr), `"`), 127 }, nil 128 } 129 130 // ParseParameter parse parameter to map[string]interface{} 131 func ParseParameter(parameter string) (map[string]interface{}, error) { 132 parameter = strings.TrimLeft(parameter, "{") 133 parameter = strings.TrimRight(parameter, "}") 134 parameter = strings.TrimSpace(parameter) 135 136 if len(parameter) == 0 { 137 return nil, errors.New("parameter shouldn't be empty") 138 } 139 140 groupNames := kvRegexp.SubexpNames() 141 matchKVs := kvRegexp.FindAllStringSubmatch(parameter, -1) 142 143 result := make(map[string]interface{}, len(matchKVs)) 144 for _, kv := range matchKVs { 145 kvMap := make(map[string]string, 2) 146 if len(kv) != len(groupNames) { 147 return nil, errors.New("failed to parse the parameter") 148 } 149 150 for i, name := range groupNames { 151 if i != 0 && name != "" { 152 kvMap[name] = strings.TrimSpace(kv[i]) 153 } 154 } 155 156 if len(kvMap["key"]) == 0 || len(kvMap["value"]) == 0 { 157 return nil, errors.New("key or value in parameter shouldn't be empty") 158 } 159 result[kvMap["key"]] = string2OtherType(kvMap["value"]) 160 } 161 162 return result, nil 163 } 164 165 // string2OtherType convert string to other type 166 func string2OtherType(s string) interface{} { 167 i, err := strconv.ParseInt(s, 10, 64) 168 if err == nil { 169 return i 170 } 171 172 b, err := strconv.ParseBool(s) 173 if err == nil { 174 return b 175 } 176 177 f, err := strconv.ParseFloat(s, 64) 178 if err == nil { 179 return f 180 } 181 return strings.Trim(s, "\"") 182 }