From 6acc73c48f4b9cb7198537e106053b84f14c00ab Mon Sep 17 00:00:00 2001
From: Semyon Krestyaninov <krestyaninov@perx.ru>
Date: Thu, 3 Jul 2025 15:42:42 +0000
Subject: [PATCH] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BB?=
 =?UTF-8?q?=D0=B5=D0=BD=D0=B0=20=D0=BE=D1=88=D0=B8=D0=B1=D0=BA=D0=B0=20?=
 =?UTF-8?q?=D1=81=20=D0=BF=D0=BE=D1=82=D0=B5=D1=80=D0=B5=D0=B9=20=D0=BA?=
 =?UTF-8?q?=D0=BE=D0=BD=D1=82=D0=B5=D0=BA=D1=81=D1=82=D0=B0=20=D0=BF=D1=80?=
 =?UTF-8?q?=D0=B8=20=D0=B8=D1=81=D0=BF=D0=BE=D0=BB=D1=8C=D0=B7=D0=BE=D0=B2?=
 =?UTF-8?q?=D0=B0=D0=BD=D0=B8=D0=B8=20=D1=88=D0=B0=D0=B1=D0=BB=D0=BE=D0=BD?=
 =?UTF-8?q?=D0=BD=D1=8B=D1=85=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D0=B9?=
 =?UTF-8?q?=20=D0=B2=20template.Builder?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 template/builder.go      | 13 +++++++++----
 template/builder_test.go | 35 +++++++++++++++++++++++++++++++++++
 template/funcs.go        |  2 +-
 template/system.go       |  5 +++--
 4 files changed, 48 insertions(+), 7 deletions(-)

diff --git a/template/builder.go b/template/builder.go
index 5a2e08cb..e420bcb3 100644
--- a/template/builder.go
+++ b/template/builder.go
@@ -56,10 +56,7 @@ func NewBuilder(conf *BuilderConfig) *Builder {
 		ctx:  context.Background(),
 		data: make(map[string]any),
 	}
-	builder.funcMap = map[string]any{
-		"lookup": getLookup(builder),
-		"system": getSystem(builder),
-	}
+	setFuncMap(builder)
 
 	return builder
 }
@@ -83,6 +80,7 @@ func (b *Builder) Context() context.Context {
 func (b *Builder) WithContext(ctx context.Context) *Builder {
 	clone := *b
 	clone.ctx = ctx
+	setFuncMap(&clone)
 	return &clone
 }
 
@@ -145,3 +143,10 @@ func (b *Builder) ExecuteMap(patternMap map[string]any, data ...any) (map[string
 	}
 	return b.TextTemplate().ExecuteMap(patternMap, data...)
 }
+
+func setFuncMap(b *Builder) {
+	b.funcMap = map[string]any{
+		"lookup": getLookup(b),
+		"system": getSystem(b),
+	}
+}
diff --git a/template/builder_test.go b/template/builder_test.go
index 2cd640cd..be97605a 100644
--- a/template/builder_test.go
+++ b/template/builder_test.go
@@ -1,6 +1,7 @@
 package template
 
 import (
+	"context"
 	"testing"
 
 	"git.perx.ru/perxis/perxis-go/pkg/collections"
@@ -17,6 +18,8 @@ import (
 	"github.com/stretchr/testify/require"
 )
 
+type testCtxKey struct{}
+
 func TestBuilder(t *testing.T) {
 	t.Run("using default HTML template", func(t *testing.T) {
 		builder := NewBuilder(&BuilderConfig{
@@ -36,6 +39,7 @@ func TestBuilder_Funcs(t *testing.T) {
 		name          string
 		pattern       string
 		setupContent  func(t *testing.T) *content.Content
+		with          func(b *Builder) *Builder
 		spaceID       string
 		environmentID string
 		collectionID  string
@@ -177,6 +181,34 @@ func TestBuilder_Funcs(t *testing.T) {
 			want:          "coll_id",
 			assertError:   assert.NoError,
 		},
+		{
+			name:    "keep builder context",
+			pattern: "{{ lookup `coll_id.item_id.field` }}",
+			setupContent: func(t *testing.T) *content.Content {
+				itemsService := itemsmocks.NewItems(t)
+				itemsService.On("Get", mock.Anything, "space_id", "env_id", "coll_id", "item_id").
+					Return(&items.Item{
+						Data: map[string]any{
+							"field": "value",
+						},
+					}, nil).
+					Run(func(args mock.Arguments) {
+						ctx := args.Get(0).(context.Context)
+						assert.Equal(t, "bar", ctx.Value(testCtxKey{}))
+					}).
+					Once()
+				return &content.Content{
+					Items: itemsService,
+				}
+			},
+			with: func(b *Builder) *Builder {
+				return b.WithContext(context.WithValue(context.Background(), testCtxKey{}, "bar"))
+			},
+			spaceID:       "space_id",
+			environmentID: "env_id",
+			want:          "value",
+			assertError:   assert.NoError,
+		},
 	} {
 		t.Run(tc.name, func(t *testing.T) {
 			conf := &BuilderConfig{
@@ -188,6 +220,9 @@ func TestBuilder_Funcs(t *testing.T) {
 				conf.Content = tc.setupContent(t)
 			}
 			builder := NewBuilder(conf)
+			if tc.with != nil {
+				builder = tc.with(builder)
+			}
 			got, err := builder.TextTemplate().Execute(tc.pattern)
 			tc.assertError(t, err)
 			assert.Equal(t, tc.want, got)
diff --git a/template/funcs.go b/template/funcs.go
index 659b9c9d..41dcdc41 100644
--- a/template/funcs.go
+++ b/template/funcs.go
@@ -21,7 +21,7 @@ func getLookup(b *Builder) func(string) (any, error) {
 		field := parsedName[2]
 		item, err := b.Content().Items.Get(b.Context(), b.SpaceID(), b.EnvironmentID(), collectionID, itemID)
 		if err != nil {
-			return nil, errors.Wrapf(err, "failed to get \"%s\"")
+			return nil, errors.Wrapf(err, "get item %q", itemID)
 		}
 
 		if len(item.Data) > 0 {
diff --git a/template/system.go b/template/system.go
index 38ad1a69..dcd82eeb 100644
--- a/template/system.go
+++ b/template/system.go
@@ -29,7 +29,8 @@ func (s *System) Environment() (*environments.Environment, error) {
 		return s.builder.environment, nil
 	}
 
-	env, err := s.builder.Content().Environments.Get(s.builder.ctx, s.builder.SpaceID(), s.builder.EnvironmentID())
+	env, err := s.builder.Content().
+		Environments.Get(s.builder.Context(), s.builder.SpaceID(), s.builder.EnvironmentID())
 	s.builder.environment = env
 	return s.builder.environment, err
 }
@@ -43,7 +44,7 @@ func (s *System) Collection() (*collections.Collection, error) {
 		return s.builder.collection, nil
 	}
 
-	coll, err := s.builder.Content().Collections.Get(s.builder.ctx, s.builder.SpaceID(),
+	coll, err := s.builder.Content().Collections.Get(s.builder.Context(), s.builder.SpaceID(),
 		s.builder.EnvironmentID(), s.builder.CollectionID())
 	s.builder.collection = coll
 	return s.builder.collection, err
-- 
GitLab