Skip to content

Commit

Permalink
Methods to retrieve CSV data as array or map
Browse files Browse the repository at this point in the history
  • Loading branch information
stuioco authored and tommysitu committed Aug 15, 2024
1 parent d6b5df4 commit 6a5dd9c
Show file tree
Hide file tree
Showing 3 changed files with 151 additions and 4 deletions.
77 changes: 76 additions & 1 deletion core/templating/template_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ func (t templateHelpers) faker(fakerType string) []reflect.Value {
return []reflect.Value{}
}

func (t templateHelpers) parseCsv(dataSourceName, searchFieldName, searchFieldValue, returnFieldName string, options *raymond.Options) string {
func (t templateHelpers) fetchSingleFieldCsv(dataSourceName, searchFieldName, searchFieldValue, returnFieldName string, options *raymond.Options) string {

templateDataSources := t.TemplateDataSource.DataSources
source, exists := templateDataSources[dataSourceName]
Expand Down Expand Up @@ -248,6 +248,81 @@ func (t templateHelpers) parseCsv(dataSourceName, searchFieldName, searchFieldVa

}

func (t templateHelpers) fetchMatchingRowsCsv(dataSourceName string, searchFieldName string, searchFieldValue string) []map[string]string {
templateDataSources := t.TemplateDataSource.DataSources
source, exists := templateDataSources[dataSourceName]
if !exists {
log.Debug("could not find datasource " + dataSourceName)
return []map[string]string{}
}
if len(source.Data) < 1 {
log.Debug("no data available in datasource " + dataSourceName)
return []map[string]string{}
}
headers := source.Data[0]
fieldIndex := -1
for i, header := range headers {
if header == searchFieldName {
fieldIndex = i
break
}
}
if fieldIndex == -1 {
log.Debug("could not find search field name " + searchFieldName)
return []map[string]string{}
}
var result []map[string]string
for _, row := range source.Data[1:] {
if fieldIndex < len(row) && row[fieldIndex] == searchFieldValue {
rowMap := make(map[string]string)
for i, cell := range row {
if i < len(headers) {
rowMap[headers[i]] = cell
}
}
result = append(result, rowMap)
}
}

return result
}

func (t templateHelpers) csvAsArray(dataSourceName string) [][]string {
templateDataSources := t.TemplateDataSource.DataSources
source, exists := templateDataSources[dataSourceName]
if exists {
return source.Data
} else {
log.Debug("could not find datasource " + dataSourceName)
return [][]string{}
}
}

func (t templateHelpers) csvAsMap(dataSourceName string) []map[string]string {
templateDataSources := t.TemplateDataSource.DataSources
source, exists := templateDataSources[dataSourceName]
if !exists {
log.Debug("could not find datasource " + dataSourceName)
return []map[string]string{}
}
if len(source.Data) < 1 {
log.Debug("no data available in datasource " + dataSourceName)
return []map[string]string{}
}
headers := source.Data[0]
var result []map[string]string
for _, row := range source.Data[1:] {
rowMap := make(map[string]string)
for i, cell := range row {
if i < len(headers) {
rowMap[headers[i]] = cell
}
}
result = append(result, rowMap)
}
return result
}

func (t templateHelpers) parseJournalBasedOnIndex(indexName, keyValue, dataSource, queryType, lookupQuery string, options *raymond.Options) interface{} {
journalDetails := options.Value("Journal").(Journal)
if journalEntry, err := getIndexEntry(journalDetails, indexName, keyValue); err == nil {
Expand Down
5 changes: 4 additions & 1 deletion core/templating/templating.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,10 @@ func NewTemplator() *Templator {
helperMethodMap["matchesRegex"] = t.matchesRegex
helperMethodMap["faker"] = t.faker
helperMethodMap["requestBody"] = t.requestBody
helperMethodMap["csv"] = t.parseCsv
helperMethodMap["csv"] = t.fetchSingleFieldCsv
helperMethodMap["csvMatchingRows"] = t.fetchMatchingRowsCsv
helperMethodMap["csvAsArray"] = t.csvAsArray
helperMethodMap["csvAsMap"] = t.csvAsMap
helperMethodMap["journal"] = t.parseJournalBasedOnIndex
helperMethodMap["hasJournalKey"] = t.hasJournalKey
helperMethodMap["setStatusCode"] = t.setStatusCode
Expand Down
73 changes: 71 additions & 2 deletions core/templating/templating_test.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package templating_test

import (
"github.com/SpectoLabs/hoverfly/core/journal"
"testing"

"github.com/SpectoLabs/hoverfly/core/journal"

"github.com/SpectoLabs/hoverfly/core/models"
"github.com/SpectoLabs/hoverfly/core/templating"
. "github.com/onsi/gomega"
Expand Down Expand Up @@ -71,6 +72,74 @@ func Test_ApplyTemplate_ParseCsv_WithEachBlockAndMissingDataSource(t *testing.T)
Expect(template).To(Equal(` 0 : Product Name with productId 1 is {{ csv products productId 1 productName }} 1 : Product Name with productId 2 is {{ csv products productId 2 productName }} `))
}

// --------------------------------------
func Test_ApplyTemplate_MatchingRowsCsvAndReturnMatchedString(t *testing.T) {
RegisterTestingT(t)

template, err := ApplyTemplate(&models.RequestDetails{}, make(map[string]string), `{{#each (csvMatchingRows 'test-csv1' 'id' '2')}}{{this.name}}{{/each}}`)

Expect(err).To(BeNil())
Expect(template).To(Equal(`Test2`))
}

func Test_ApplyTemplate_MatchingRowsCsvMissingDataSource(t *testing.T) {
RegisterTestingT(t)

template, err := ApplyTemplate(&models.RequestDetails{}, make(map[string]string), `{{#each (csvMatchingRows 'test-csv99' 'id' '2')}}{{this.name}}{{/each}}`)

Expect(err).To(BeNil())
Expect(template).To(Equal(``))
}

func Test_ApplyTemplate_MatchingRowsCsvInvalidKey(t *testing.T) {
RegisterTestingT(t)

template, err := ApplyTemplate(&models.RequestDetails{}, make(map[string]string), `{{#each (csvMatchingRows 'test-csv1' 'id' '99')}}{{this.name}}{{/each}}`)

Expect(err).To(BeNil())
Expect(template).To(Equal(``))
}

// -------------------------------
func Test_ApplyTemplate_CsvAsArrayAndReturnMatchedString(t *testing.T) {
RegisterTestingT(t)

template, err := ApplyTemplate(&models.RequestDetails{}, make(map[string]string), `{{#each (csvAsArray 'test-csv1')}}{{#each this}}{{this}}{{/each}}{{/each}}`)

Expect(err).To(BeNil())
Expect(template).To(Equal(`idnamemarks1Test1552Test256*DummyABSENT`))
}

func Test_ApplyTemplate_CsvAsArrayMissingDataSource(t *testing.T) {
RegisterTestingT(t)

template, err := ApplyTemplate(&models.RequestDetails{}, make(map[string]string), `{{#each (csvAsArray 'test-csv99')}}{{#each this}}{{this}}{{/each}}{{/each}}`)

Expect(err).To(BeNil())
Expect(template).To(Equal(``))
}

// -------------------------------
func Test_ApplyTemplate_CsvAsMapAndReturnMatchedString(t *testing.T) {
RegisterTestingT(t)

template, err := ApplyTemplate(&models.RequestDetails{}, make(map[string]string), `{{#each (csvAsMap 'test-csv1')}}{{this.name}}{{/each}}`)

Expect(err).To(BeNil())
Expect(template).To(Equal(`Test1Test2Dummy`))
}

func Test_ApplyTemplate_CsvAsMapMissingDataSource(t *testing.T) {
RegisterTestingT(t)

template, err := ApplyTemplate(&models.RequestDetails{}, make(map[string]string), `{{#each (csvAsMap 'test-csv99')}}{{this.name}}{{/each}}`)

Expect(err).To(BeNil())
Expect(template).To(Equal(``))
}

// -------------------------------

func Test_ApplyTemplate_EachBlockWithSplitTemplatingFunction(t *testing.T) {
RegisterTestingT(t)

Expand Down Expand Up @@ -642,7 +711,7 @@ func Test_ApplyTemplate_Arithmetic_Ops_With_Each_Block(t *testing.T) {
Expect(result).To(Equal(` 3.5 9 total: 12.50`))

// Running the second time should produce the same result because each execution has its own context data.
result, err = templator.RenderTemplate(template, requestDetails, nil, &models.Literals{}, &models.Variables{}, state, &journal.Journal{})
result, err = templator.RenderTemplate(template, requestDetails, nil, &models.Literals{}, &models.Variables{}, state, &journal.Journal{})
Expect(err).To(BeNil())
Expect(result).To(Equal(` 3.5 9 total: 12.50`))
}
Expand Down

0 comments on commit 6a5dd9c

Please sign in to comment.