github.com/ezbuy/gauge@v0.9.4-0.20171013092048-7ac5bd3931cd/parser/resolver.go (about) 1 // Copyright 2015 ThoughtWorks, Inc. 2 3 // This file is part of Gauge. 4 5 // Gauge is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 10 // Gauge is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU General Public License for more details. 14 15 // You should have received a copy of the GNU General Public License 16 // along with Gauge. If not, see <http://www.gnu.org/licenses/>. 17 18 package parser 19 20 import ( 21 "fmt" 22 "regexp" 23 "strings" 24 25 "github.com/getgauge/common" 26 "github.com/getgauge/gauge/gauge" 27 "github.com/getgauge/gauge/gauge_messages" 28 "github.com/getgauge/gauge/util" 29 ) 30 31 type invalidSpecialParamError struct { 32 message string 33 } 34 35 type resolverFn func(string) (*gauge.StepArg, error) 36 type specialTypeResolver struct { 37 predefinedResolvers map[string]resolverFn 38 } 39 40 type ParamResolver struct { 41 } 42 43 func (invalidSpecialParamError invalidSpecialParamError) Error() string { 44 return invalidSpecialParamError.message 45 } 46 47 func (paramResolver *ParamResolver) GetResolvedParams(step *gauge.Step, parent *gauge.Step, lookup *gauge.ArgLookup) []*gauge_messages.Parameter { 48 parameters := make([]*gauge_messages.Parameter, 0) 49 for _, arg := range step.Args { 50 parameter := new(gauge_messages.Parameter) 51 parameter.Name = arg.Name 52 if arg.ArgType == gauge.Static { 53 parameter.ParameterType = gauge_messages.Parameter_Static 54 parameter.Value = arg.Value 55 } else if arg.ArgType == gauge.Dynamic { 56 var resolvedArg *gauge.StepArg 57 if parent != nil { 58 resolvedArg = parent.GetArg(arg.Value) 59 } else { 60 resolvedArg = lookup.GetArg(arg.Value) 61 } 62 //In case a special table used in a concept, you will get a dynamic table value which has to be resolved from the concept lookup 63 parameter.Name = resolvedArg.Name 64 if resolvedArg.Table.IsInitialized() { 65 parameter.ParameterType = gauge_messages.Parameter_Special_Table 66 parameter.Table = paramResolver.createProtoStepTable(&resolvedArg.Table, lookup) 67 } else { 68 parameter.ParameterType = gauge_messages.Parameter_Dynamic 69 parameter.Value = resolvedArg.Value 70 } 71 } else if arg.ArgType == gauge.SpecialString { 72 parameter.ParameterType = gauge_messages.Parameter_Special_String 73 parameter.Value = arg.Value 74 } else if arg.ArgType == gauge.SpecialTable { 75 parameter.ParameterType = gauge_messages.Parameter_Special_Table 76 parameter.Table = paramResolver.createProtoStepTable(&arg.Table, lookup) 77 } else { 78 parameter.ParameterType = gauge_messages.Parameter_Table 79 parameter.Table = paramResolver.createProtoStepTable(&arg.Table, lookup) 80 81 } 82 parameters = append(parameters, parameter) 83 } 84 85 return parameters 86 87 } 88 89 func (resolver *ParamResolver) createProtoStepTable(table *gauge.Table, lookup *gauge.ArgLookup) *gauge_messages.ProtoTable { 90 protoTable := new(gauge_messages.ProtoTable) 91 protoTable.Headers = &gauge_messages.ProtoTableRow{Cells: table.Headers} 92 tableRows := make([]*gauge_messages.ProtoTableRow, 0) 93 if len(table.Columns) == 0 { 94 protoTable.Rows = tableRows 95 return protoTable 96 } 97 for i := 0; i < len(table.Columns[0]); i++ { 98 row := make([]string, 0) 99 for _, header := range table.Headers { 100 tableCell := table.Get(header)[i] 101 value := tableCell.Value 102 if tableCell.CellType == gauge.Dynamic { 103 //if concept has a table with dynamic cell, fetch from datatable 104 value = lookup.GetArg(tableCell.Value).Value 105 } 106 row = append(row, value) 107 } 108 tableRows = append(tableRows, &gauge_messages.ProtoTableRow{Cells: row}) 109 } 110 protoTable.Rows = tableRows 111 return protoTable 112 } 113 114 func newSpecialTypeResolver() *specialTypeResolver { 115 resolver := new(specialTypeResolver) 116 resolver.predefinedResolvers = initializePredefinedResolvers() 117 return resolver 118 } 119 120 func initializePredefinedResolvers() map[string]resolverFn { 121 return map[string]resolverFn{ 122 "file": func(filePath string) (*gauge.StepArg, error) { 123 fileContent, err := common.ReadFileContents(util.GetPathToFile(filePath)) 124 if err != nil { 125 return nil, err 126 } 127 return &gauge.StepArg{Value: fileContent, ArgType: gauge.SpecialString}, nil 128 }, 129 "table": func(filePath string) (*gauge.StepArg, error) { 130 csv, err := common.ReadFileContents(util.GetPathToFile(filePath)) 131 if err != nil { 132 return nil, err 133 } 134 csvTable, err := convertCsvToTable(csv) 135 if err != nil { 136 return nil, err 137 } 138 return &gauge.StepArg{Table: *csvTable, ArgType: gauge.SpecialTable}, nil 139 }, 140 } 141 } 142 143 func (resolver *specialTypeResolver) resolve(arg string) (*gauge.StepArg, error) { 144 if util.IsWindows() { 145 arg = GetUnescapedString(arg) 146 } 147 regEx := regexp.MustCompile("(.*?):(.*)") 148 match := regEx.FindAllStringSubmatch(arg, -1) 149 specialType := strings.TrimSpace(match[0][1]) 150 value := strings.TrimSpace(match[0][2]) 151 stepArg, err := resolver.getStepArg(specialType, value, arg) 152 if err == nil { 153 stepArg.Name = arg 154 } 155 return stepArg, err 156 } 157 158 func (resolver *specialTypeResolver) getStepArg(specialType string, value string, arg string) (*gauge.StepArg, error) { 159 resolveFunc, found := resolver.predefinedResolvers[specialType] 160 if found { 161 return resolveFunc(value) 162 } 163 return nil, invalidSpecialParamError{message: fmt.Sprintf("Resolver not found for special param <%s>", arg)} 164 } 165 166 // Creating a copy of the lookup and populating table values 167 func PopulateConceptDynamicParams(concept *gauge.Step, dataTableLookup *gauge.ArgLookup) { 168 //If it is a top level concept 169 lookup := concept.Lookup.GetCopy() 170 for key := range lookup.ParamIndexMap { 171 conceptLookupArg := lookup.GetArg(key) 172 if conceptLookupArg.ArgType == gauge.Dynamic { 173 resolvedArg := dataTableLookup.GetArg(conceptLookupArg.Value) 174 lookup.AddArgValue(key, resolvedArg) 175 } 176 } 177 concept.Lookup = *lookup 178 179 //Updating values inside the concept step as well 180 newArgs := make([]*gauge.StepArg, 0) 181 for _, arg := range concept.Args { 182 if arg.ArgType == gauge.Dynamic { 183 if concept.Parent != nil { 184 newArgs = append(newArgs, concept.Parent.GetArg(arg.Value)) 185 } else { 186 newArgs = append(newArgs, dataTableLookup.GetArg(arg.Value)) 187 } 188 } else { 189 newArgs = append(newArgs, arg) 190 } 191 } 192 concept.Args = newArgs 193 concept.PopulateFragments() 194 }