Skip to content

Commit

Permalink
Avoid copying the journal data into context data for template rendering
Browse files Browse the repository at this point in the history
  • Loading branch information
tommysitu committed Aug 28, 2024
1 parent b120b47 commit 6c44065
Show file tree
Hide file tree
Showing 5 changed files with 41 additions and 78 deletions.
5 changes: 3 additions & 2 deletions core/hoverfly.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,16 @@ func NewHoverfly() *Hoverfly {

authBackend := backends.NewCacheBasedAuthBackend(cache.NewInMemoryCache(), cache.NewInMemoryCache())

newJournal := journal.NewJournal()
hoverfly := &Hoverfly{
Simulation: models.NewSimulation(),
Authentication: authBackend,
Counter: metrics.NewModeCounter([]string{modes.Simulate, modes.Synthesize, modes.Modify, modes.Capture, modes.Spy, modes.Diff}),
StoreLogsHook: NewStoreLogsHook(),
Journal: journal.NewJournal(),
Journal: newJournal,
Cfg: InitSettings(),
state: state.NewState(),
templator: templating.NewTemplator(),
templator: templating.NewEnrichedTemplator(newJournal),
responsesDiff: make(map[v2.SimpleRequestDefinitionView][]v2.DiffReport),
PostServeActionDetails: action.NewPostServeActionDetails(),
}
Expand Down
6 changes: 3 additions & 3 deletions core/hoverfly_funcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ func (hf *Hoverfly) applyTransitionsStateTemplating(requestDetails *models.Reque
state := make(map[string]string)

for k, v := range stateTemplates {
state[k], err = hf.templator.RenderTemplate(v, requestDetails, response, hf.Simulation.Literals, hf.Simulation.Vars, hf.state.State, hf.Journal)
state[k], err = hf.templator.RenderTemplate(v, requestDetails, response, hf.Simulation.Literals, hf.Simulation.Vars, hf.state.State)
if err != nil {
return nil, err
}
Expand All @@ -245,7 +245,7 @@ func (hf *Hoverfly) applyBodyTemplating(requestDetails *models.RequestDetails, r
}
}

return hf.templator.RenderTemplate(template, requestDetails, response, hf.Simulation.Literals, hf.Simulation.Vars, hf.state.State, hf.Journal)
return hf.templator.RenderTemplate(template, requestDetails, response, hf.Simulation.Literals, hf.Simulation.Vars, hf.state.State)
}

func (hf *Hoverfly) applyHeadersTemplating(requestDetails *models.RequestDetails, response *models.ResponseDetails, cachedResponse *models.CachedResponse) (map[string][]string, error) {
Expand Down Expand Up @@ -280,7 +280,7 @@ func (hf *Hoverfly) applyHeadersTemplating(requestDetails *models.RequestDetails
for k, v := range headersTemplates {
header = make([]string, len(v))
for i, h := range v {
header[i], err = hf.templator.RenderTemplate(h, requestDetails, response, hf.Simulation.Literals, hf.Simulation.Vars, hf.state.State, hf.Journal)
header[i], err = hf.templator.RenderTemplate(h, requestDetails, response, hf.Simulation.Literals, hf.Simulation.Vars, hf.state.State)

if err != nil {
return nil, err
Expand Down
26 changes: 13 additions & 13 deletions core/templating/template_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ type templateHelpers struct {
now func() time.Time
fakerSource *gofakeit.Faker
TemplateDataSource *TemplateDataSource
journal *journal.Journal
}

func (t templateHelpers) nowHelper(offset string, format string) string {
Expand Down Expand Up @@ -466,8 +467,7 @@ func (t templateHelpers) csvSqlCommand(commandString string) []RowMap {
}

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 {
if journalEntry, err := getIndexEntry(t.journal, indexName, keyValue); err == nil {
if body := getBodyDataToParse(dataSource, journalEntry); body != "" {
data := util.FetchFromRequestBody(queryType, lookupQuery, body)
if _, ok := data.(error); ok {
Expand All @@ -481,9 +481,9 @@ func (t templateHelpers) parseJournalBasedOnIndex(indexName, keyValue, dataSourc
return getEvaluationString("journal", options)
}

func (t templateHelpers) hasJournalKey(indexName, keyValue string, options *raymond.Options) bool {
journalDetails := options.Value("Journal").(Journal)
journalEntry, _ := getIndexEntry(journalDetails, indexName, keyValue)
func (t templateHelpers) hasJournalKey(indexName, keyValue string) bool {

journalEntry, _ := getIndexEntry(t.journal, indexName, keyValue)

return journalEntry != nil
}
Expand Down Expand Up @@ -615,25 +615,25 @@ func formatNumber(number float64, format string) string {
return fmt.Sprintf("%."+strconv.Itoa(decimalPlaces)+"f", rounded)
}

func getIndexEntry(journalIndexDetails Journal, indexName, indexValue string) (*JournalEntry, error) {
func getIndexEntry(journal *journal.Journal, indexName, indexValue string) (*journal.JournalEntry, error) {

for _, index := range journalIndexDetails.indexes {
if index.name == indexName {
if journalEntry, exists := index.entries[indexValue]; exists {
return &journalEntry, nil
for _, index := range journal.Indexes {
if index.Name == indexName {
if journalEntry, exists := index.Entries[indexValue]; exists {
return journalEntry, nil
}
}
}
return nil, fmt.Errorf("no entry found for index %s", indexName)
}

func getBodyDataToParse(source string, journalEntry *JournalEntry) string {
func getBodyDataToParse(source string, journalEntry *journal.JournalEntry) string {

if strings.EqualFold(source, "request") {
return journalEntry.requestBody
return journalEntry.Request.Body
}
if strings.EqualFold(source, "response") {
return journalEntry.responseBody
return journalEntry.Response.Body
}
return ""
}
Expand Down
52 changes: 8 additions & 44 deletions core/templating/templating.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ type TemplatingData struct {
CurrentDateTime func(string, string, string) string
Literals map[string]interface{}
Vars map[string]interface{}
Journal Journal
Kvs map[string]interface{}
InternalVars map[string]interface{} // data store used internally by templating helpers
}
Expand All @@ -42,19 +41,6 @@ type Request struct {
Host string
}

type JournalEntry struct {
requestBody string
responseBody string
}

type Journal struct {
indexes []JournalIndex
}

type JournalIndex struct {
name string
entries map[string]JournalEntry
}

type Templator struct {
SupportedMethodMap map[string]interface{}
Expand All @@ -65,13 +51,18 @@ var helpersRegistered = false
var helperMethodMap = make(map[string]interface{})

func NewTemplator() *Templator {
return NewEnrichedTemplator(journal.NewJournal())
}

func NewEnrichedTemplator(journal *journal.Journal) *Templator {

templateDataSource := NewTemplateDataSource()

t := templateHelpers{
now: time.Now,
fakerSource: gofakeit.New(0),
TemplateDataSource: templateDataSource,
journal: journal,
}

helperMethodMap["now"] = t.nowHelper
Expand Down Expand Up @@ -148,12 +139,12 @@ func (*Templator) ParseTemplate(responseBody string) (*raymond.Template, error)
return raymond.Parse(responseBody)
}

func (t *Templator) RenderTemplate(tpl *raymond.Template, requestDetails *models.RequestDetails, response *models.ResponseDetails, literals *models.Literals, vars *models.Variables, state map[string]string, journal *journal.Journal) (string, error) {
func (t *Templator) RenderTemplate(tpl *raymond.Template, requestDetails *models.RequestDetails, response *models.ResponseDetails, literals *models.Literals, vars *models.Variables, state map[string]string) (string, error) {
if tpl == nil {
return "", fmt.Errorf("template cannot be nil")
}

ctx := t.NewTemplatingData(requestDetails, response, literals, vars, state, journal)
ctx := t.NewTemplatingData(requestDetails, literals, vars, state)
result, err := tpl.Exec(ctx)
if err == nil {
statusCode, ok := ctx.InternalVars["statusCode"]
Expand All @@ -168,7 +159,7 @@ func (templator *Templator) GetSupportedMethodMap() map[string]interface{} {
return templator.SupportedMethodMap
}

func (t *Templator) NewTemplatingData(requestDetails *models.RequestDetails, response *models.ResponseDetails, literals *models.Literals, vars *models.Variables, state map[string]string, journal *journal.Journal) *TemplatingData {
func (t *Templator) NewTemplatingData(requestDetails *models.RequestDetails, literals *models.Literals, vars *models.Variables, state map[string]string) *TemplatingData {

literalMap := make(map[string]interface{})
if literals != nil {
Expand All @@ -178,40 +169,13 @@ func (t *Templator) NewTemplatingData(requestDetails *models.RequestDetails, res
}

variableMap := t.getVariables(vars, requestDetails)
templateJournal := Journal{}
if journal != nil {

indexes := make([]JournalIndex, 0, len(journal.Indexes))
for _, index := range journal.Indexes {

journalIndexEntries := make(map[string]JournalEntry)
for indexKey, entry := range index.Entries {

journalEntry := JournalEntry{
requestBody: entry.Request.Body,
responseBody: entry.Response.Body,
}
journalIndexEntries[indexKey] = journalEntry
}
journalIndex := JournalIndex{
name: index.Name,
entries: journalIndexEntries,
}
indexes = append(indexes, journalIndex)
}
templateJournal = Journal{
indexes: indexes,
}

}

kvs := make(map[string]interface{})
return &TemplatingData{
Request: getRequest(requestDetails),
Literals: literalMap,
Vars: variableMap,
State: state,
Journal: templateJournal,
CurrentDateTime: func(a1, a2, a3 string) string {
return a1 + " " + a2 + " " + a3
},
Expand Down
30 changes: 14 additions & 16 deletions core/templating/templating_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ package templating_test
import (
"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 @@ -297,7 +295,7 @@ func Test_ShouldCreateTemplatingDataPathsFromRequest(t *testing.T) {
Scheme: "http",
Destination: "test.com",
Path: "/foo/bar",
}, nil, &models.Literals{}, &models.Variables{}, make(map[string]string), &journal.Journal{})
}, &models.Literals{}, &models.Variables{}, make(map[string]string))

Expect(actual.Request.Path).To(ConsistOf("foo", "bar"))
}
Expand All @@ -308,7 +306,7 @@ func Test_ShouldCreateTemplatingDataPathsFromRequestWithNoPaths(t *testing.T) {
actual := templating.NewTemplator().NewTemplatingData(&models.RequestDetails{
Scheme: "http",
Destination: "test.com",
}, nil, &models.Literals{}, &models.Variables{}, make(map[string]string), &journal.Journal{})
}, &models.Literals{}, &models.Variables{}, make(map[string]string))

Expect(actual.Request.Path).To(BeEmpty())
}
Expand All @@ -323,7 +321,7 @@ func Test_ShouldCreateTemplatingDataQueryParamsFromRequest(t *testing.T) {
"cheese": {"1", "3"},
"ham": {"2"},
},
}, nil, &models.Literals{}, &models.Variables{}, make(map[string]string), &journal.Journal{})
}, &models.Literals{}, &models.Variables{}, make(map[string]string))

Expect(actual.Request.QueryParam).To(HaveKeyWithValue("cheese", []string{"1", "3"}))
Expect(actual.Request.QueryParam).To(HaveKeyWithValue("ham", []string{"2"}))
Expand All @@ -336,7 +334,7 @@ func Test_ShouldCreateTemplatingDataQueryParamsFromRequestWithNoQueryParams(t *t
actual := templating.NewTemplator().NewTemplatingData(&models.RequestDetails{
Scheme: "http",
Destination: "test.com",
}, nil, &models.Literals{}, &models.Variables{}, make(map[string]string), &journal.Journal{})
}, &models.Literals{}, &models.Variables{}, make(map[string]string))

Expect(actual.Request.QueryParam).To(BeEmpty())
}
Expand All @@ -347,7 +345,7 @@ func Test_ShouldCreateTemplatingDataHttpScheme(t *testing.T) {
actual := templating.NewTemplator().NewTemplatingData(&models.RequestDetails{
Scheme: "http",
Destination: "test.com",
}, nil, &models.Literals{}, &models.Variables{}, make(map[string]string), &journal.Journal{})
}, &models.Literals{}, &models.Variables{}, make(map[string]string))

Expect(actual.Request.Scheme).To(Equal("http"))
}
Expand All @@ -362,7 +360,7 @@ func Test_ShouldCreateTemplatingDataHeaderFromRequest(t *testing.T) {
"cheese": {"1", "3"},
"ham": {"2"},
},
}, nil, &models.Literals{}, &models.Variables{}, make(map[string]string), &journal.Journal{})
}, &models.Literals{}, &models.Variables{}, make(map[string]string))

Expect(actual.Request.Header).To(HaveKeyWithValue("cheese", []string{"1", "3"}))
Expect(actual.Request.Header).To(HaveKeyWithValue("ham", []string{"2"}))
Expand All @@ -375,7 +373,7 @@ func Test_ShouldCreateTemplatingDataHeaderFromRequestWithNoHeader(t *testing.T)
actual := templating.NewTemplator().NewTemplatingData(&models.RequestDetails{
Scheme: "http",
Destination: "test.com",
}, nil, &models.Literals{}, &models.Variables{}, make(map[string]string), &journal.Journal{})
}, &models.Literals{}, &models.Variables{}, make(map[string]string))

Expect(actual.Request.Header).To(BeEmpty())
}
Expand Down Expand Up @@ -702,7 +700,7 @@ func Test_VarSetToNilInCaseOfInvalidArgsPassed(t *testing.T) {
actual := templator.NewTemplatingData(&models.RequestDetails{
Scheme: "http",
Destination: "test.com",
}, nil, &models.Literals{}, vars, make(map[string]string), &journal.Journal{})
}, &models.Literals{}, vars, make(map[string]string))

Expect(actual.Vars["varOne"]).To(BeNil())

Expand All @@ -722,7 +720,7 @@ func Test_VarSetToProperValueInCaseOfRequestDetailsPassedAsArgument(t *testing.T

actual := templator.NewTemplatingData(&models.RequestDetails{
Path: "/part1/foo,bar",
}, nil, &models.Literals{}, vars, make(map[string]string), &journal.Journal{})
}, &models.Literals{}, vars, make(map[string]string))

Expect(actual.Vars["splitRequestPath"]).ToNot(BeNil())
Expect(len(actual.Vars["splitRequestPath"].([]string))).To(Equal(2))
Expand Down Expand Up @@ -823,13 +821,13 @@ func Test_ApplyTemplate_Arithmetic_Ops_With_Each_Block(t *testing.T) {

template, _ := templator.ParseTemplate(responseBody)
state := make(map[string]string)
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)

Expect(err).To(BeNil())
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)
Expect(err).To(BeNil())
Expect(result).To(Equal(` 3.5 9 total: 12.50`))
}
Expand Down Expand Up @@ -867,7 +865,7 @@ func Test_ApplyTemplate_setStatusCode(t *testing.T) {
Expect(err).To(BeNil())

response := &models.ResponseDetails{}
result, err := templator.RenderTemplate(template, &models.RequestDetails{}, response, &models.Literals{}, &models.Variables{}, make(map[string]string), &journal.Journal{})
result, err := templator.RenderTemplate(template, &models.RequestDetails{}, response, &models.Literals{}, &models.Variables{}, make(map[string]string))

Expect(err).To(BeNil())
Expect(result).To(Equal(""))
Expand All @@ -883,7 +881,7 @@ func Test_ApplyTemplate_setStatusCode_should_ignore_invalid_code(t *testing.T) {
Expect(err).To(BeNil())

response := &models.ResponseDetails{}
result, err := templator.RenderTemplate(template, &models.RequestDetails{}, response, &models.Literals{}, &models.Variables{}, make(map[string]string), &journal.Journal{})
result, err := templator.RenderTemplate(template, &models.RequestDetails{}, response, &models.Literals{}, &models.Variables{}, make(map[string]string))

Expect(err).To(BeNil())
Expect(result).To(Equal(""))
Expand Down Expand Up @@ -920,7 +918,7 @@ func ApplyTemplate(requestDetails *models.RequestDetails, state map[string]strin
func renderTemplate(requestDetails *models.RequestDetails, state map[string]string, responseBody string, templator *templating.Templator) (string, error) {
template, err := templator.ParseTemplate(responseBody)
Expect(err).To(BeNil())
return templator.RenderTemplate(template, requestDetails, nil, &models.Literals{}, &models.Variables{}, state, &journal.Journal{})
return templator.RenderTemplate(template, requestDetails, nil, &models.Literals{}, &models.Variables{}, state)
}

func initiateTemplator() *templating.Templator {
Expand Down

0 comments on commit 6c44065

Please sign in to comment.