github.com/getgauge/gauge@v1.6.9/parser/resolver.go (about) 1 /*---------------------------------------------------------------- 2 * Copyright (c) ThoughtWorks, Inc. 3 * Licensed under the Apache License, Version 2.0 4 * See LICENSE in the project root for license information. 5 *----------------------------------------------------------------*/ 6 7 package parser 8 9 import ( 10 "fmt" 11 "regexp" 12 "strings" 13 14 "github.com/getgauge/gauge-proto/go/gauge_messages" 15 "github.com/getgauge/gauge/gauge" 16 "github.com/getgauge/gauge/util" 17 ) 18 19 type invalidSpecialParamError struct { 20 message string 21 } 22 23 type resolverFn func(string) (*gauge.StepArg, error) 24 type specialTypeResolver struct { 25 predefinedResolvers map[string]resolverFn 26 } 27 28 func (invalidSpecialParamError invalidSpecialParamError) Error() string { 29 return invalidSpecialParamError.message 30 } 31 32 //Resolve takes a step, a lookup and updates the target after reconciling the dynamic paramters from the given lookup 33 func Resolve(step *gauge.Step, parent *gauge.Step, lookup *gauge.ArgLookup, target *gauge_messages.ProtoStep) error { 34 stepParameters, err := getResolvedParams(step, parent, lookup) 35 if err != nil { 36 return err 37 } 38 paramIndex := 0 39 for fragmentIndex, fragment := range target.Fragments { 40 if fragment.GetFragmentType() == gauge_messages.Fragment_Parameter { 41 target.Fragments[fragmentIndex].Parameter = stepParameters[paramIndex] 42 paramIndex++ 43 } 44 } 45 return nil 46 } 47 48 // getResolvedParams based on the arg type(static, dynamic, table, special_string, special_table) resolves the parameter of a step. 49 func getResolvedParams(step *gauge.Step, parent *gauge.Step, lookup *gauge.ArgLookup) ([]*gauge_messages.Parameter, error) { 50 parameters := make([]*gauge_messages.Parameter, 0) 51 for _, arg := range step.Args { 52 parameter := new(gauge_messages.Parameter) 53 parameter.Name = arg.Name 54 if arg.ArgType == gauge.Static { 55 parameter.ParameterType = gauge_messages.Parameter_Static 56 parameter.Value = arg.Value 57 } else if arg.ArgType == gauge.Dynamic { 58 var resolvedArg *gauge.StepArg 59 var err error 60 if parent != nil { 61 resolvedArg, err = parent.GetArg(arg.Value) 62 } else { 63 resolvedArg, err = lookup.GetArg(arg.Value) 64 } 65 if err != nil { 66 return nil, err 67 } 68 //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 69 parameter.Name = resolvedArg.Name 70 if resolvedArg.Table.IsInitialized() { 71 parameter.ParameterType = gauge_messages.Parameter_Special_Table 72 table, err := createProtoStepTable(&resolvedArg.Table, lookup) 73 if err != nil { 74 return nil, err 75 } 76 parameter.Table = table 77 } else { 78 parameter.ParameterType = gauge_messages.Parameter_Dynamic 79 parameter.Value = resolvedArg.Value 80 } 81 } else if arg.ArgType == gauge.SpecialString { 82 parameter.ParameterType = gauge_messages.Parameter_Special_String 83 parameter.Value = arg.Value 84 } else if arg.ArgType == gauge.SpecialTable { 85 parameter.ParameterType = gauge_messages.Parameter_Special_Table 86 table, err := createProtoStepTable(&arg.Table, lookup) 87 if err != nil { 88 return nil, err 89 } 90 parameter.Table = table 91 } else { 92 parameter.ParameterType = gauge_messages.Parameter_Table 93 table, err := createProtoStepTable(&arg.Table, lookup) 94 if err != nil { 95 return nil, err 96 } 97 parameter.Table = table 98 } 99 parameters = append(parameters, parameter) 100 } 101 102 return parameters, nil 103 } 104 105 func createProtoStepTable(table *gauge.Table, lookup *gauge.ArgLookup) (*gauge_messages.ProtoTable, error) { 106 protoTable := new(gauge_messages.ProtoTable) 107 protoTable.Headers = &gauge_messages.ProtoTableRow{Cells: table.Headers} 108 tableRows := make([]*gauge_messages.ProtoTableRow, 0) 109 if len(table.Columns) == 0 { 110 protoTable.Rows = tableRows 111 return protoTable, nil 112 } 113 for i := 0; i < len(table.Columns[0]); i++ { 114 row := make([]string, 0) 115 for _, header := range table.Headers { 116 tableCells, _ := table.Get(header) 117 value := tableCells[i].Value 118 if tableCells[i].CellType == gauge.Dynamic { 119 //if concept has a table with dynamic cell, fetch from datatable 120 arg, err := lookup.GetArg(tableCells[i].Value) 121 if err != nil { 122 return nil, err 123 } 124 value = arg.Value 125 } else if tableCells[i].CellType == gauge.SpecialString { 126 resolvedArg, _ := newSpecialTypeResolver().resolve(value) 127 value = resolvedArg.Value 128 } 129 row = append(row, value) 130 } 131 tableRows = append(tableRows, &gauge_messages.ProtoTableRow{Cells: row}) 132 } 133 protoTable.Rows = tableRows 134 return protoTable, nil 135 } 136 137 func newSpecialTypeResolver() *specialTypeResolver { 138 resolver := new(specialTypeResolver) 139 resolver.predefinedResolvers = initializePredefinedResolvers() 140 return resolver 141 } 142 143 func initializePredefinedResolvers() map[string]resolverFn { 144 return map[string]resolverFn{ 145 "file": func(filePath string) (*gauge.StepArg, error) { 146 fileContent, err := util.GetFileContents(filePath) 147 if err != nil { 148 return nil, err 149 } 150 return &gauge.StepArg{Value: fileContent, ArgType: gauge.SpecialString}, nil 151 }, 152 "table": func(filePath string) (*gauge.StepArg, error) { 153 csv, err := util.GetFileContents(filePath) 154 if err != nil { 155 return nil, err 156 } 157 csvTable, err := convertCsvToTable(csv) 158 if err != nil { 159 return nil, err 160 } 161 return &gauge.StepArg{Table: *csvTable, ArgType: gauge.SpecialTable}, nil 162 }, 163 } 164 } 165 166 func (resolver *specialTypeResolver) resolve(arg string) (*gauge.StepArg, error) { 167 if util.IsWindows() { 168 arg = GetUnescapedString(arg) 169 } 170 regEx := regexp.MustCompile("(.*?):(.*)") 171 match := regEx.FindAllStringSubmatch(arg, -1) 172 specialType := strings.TrimSpace(match[0][1]) 173 value := strings.TrimSpace(match[0][2]) 174 stepArg, err := resolver.getStepArg(specialType, value, arg) 175 if err == nil { 176 stepArg.Name = arg 177 } 178 return stepArg, err 179 } 180 181 func (resolver *specialTypeResolver) getStepArg(specialType string, value string, arg string) (*gauge.StepArg, error) { 182 resolveFunc, found := resolver.predefinedResolvers[specialType] 183 if found { 184 return resolveFunc(value) 185 } 186 return nil, invalidSpecialParamError{message: fmt.Sprintf("Resolver not found for special param <%s>", arg)} 187 } 188 189 // PopulateConceptDynamicParams creates a copy of the lookup and populates table values 190 func PopulateConceptDynamicParams(concept *gauge.Step, dataTableLookup *gauge.ArgLookup) error { 191 //If it is a top level concept 192 lookup, err := concept.Lookup.GetCopy() 193 if err != nil { 194 return err 195 } 196 for key := range lookup.ParamIndexMap { 197 conceptLookupArg, err := lookup.GetArg(key) 198 if err != nil { 199 return err 200 } 201 if conceptLookupArg.ArgType == gauge.Dynamic { 202 resolvedArg, err := dataTableLookup.GetArg(conceptLookupArg.Value) 203 if err != nil { 204 return err 205 } 206 if err = lookup.AddArgValue(key, resolvedArg); err != nil { 207 return err 208 } 209 } 210 if conceptLookupArg.ArgType == gauge.TableArg { 211 var updateColumns [][]gauge.TableCell 212 for _, cells := range conceptLookupArg.Table.Columns { 213 var updateCells []gauge.TableCell 214 for _, cell := range cells { 215 if cell.CellType == gauge.Dynamic { 216 v, err := dataTableLookup.GetArg(cell.Value) 217 if err != nil { 218 return err 219 } 220 updateCells = append(updateCells, gauge.TableCell{CellType: gauge.Static, Value: v.Value}) 221 } else { 222 updateCells = append(updateCells, cell) 223 } 224 } 225 updateColumns = append(updateColumns, updateCells) 226 } 227 conceptLookupArg.Table.Columns = updateColumns 228 } 229 } 230 concept.Lookup = *lookup 231 //Updating values inside the concept step as well 232 newArgs := make([]*gauge.StepArg, 0) 233 for _, arg := range concept.Args { 234 if arg.ArgType == gauge.Dynamic { 235 if concept.Parent != nil { 236 cArg, err := concept.Parent.GetArg(arg.Value) 237 if err != nil { 238 return err 239 } 240 newArgs = append(newArgs, cArg) 241 } else { 242 dArg, err := dataTableLookup.GetArg(arg.Value) 243 if err != nil { 244 return err 245 } 246 newArgs = append(newArgs, dArg) 247 } 248 } else { 249 newArgs = append(newArgs, arg) 250 } 251 } 252 concept.Args = newArgs 253 concept.PopulateFragments() 254 return nil 255 } 256 257 // GetResolvedDataTablerows resolves any dynamic parameters in a table cell 258 func GetResolvedDataTablerows(table *gauge.Table) { 259 for i, cells := range table.Columns { 260 for j, cell := range cells { 261 if cell.CellType == gauge.SpecialString { 262 resolvedArg, _ := newSpecialTypeResolver().resolve(cell.Value) 263 table.Columns[i][j].Value = resolvedArg.Value 264 } 265 } 266 } 267 }