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  }