diff --git a/builder/store/database/agent_instance.go b/builder/store/database/agent_instance.go index 625fe5527..074d8a5b5 100644 --- a/builder/store/database/agent_instance.go +++ b/builder/store/database/agent_instance.go @@ -143,7 +143,7 @@ func (s *agentTemplateStoreImpl) FindByTypeAndName(ctx context.Context, agentTyp return templates, nil } -func (s *agentTemplateStoreImpl) applyAgentTemplateFilters(query *bun.SelectQuery, filter types.AgentTemplateFilter) *bun.SelectQuery { +func (s *agentTemplateStoreImpl) applyAgentTemplateFilters(query *bun.SelectQuery, userUUID string, filter types.AgentTemplateFilter) *bun.SelectQuery { filter.Search = strings.TrimSpace(filter.Search) if filter.Search != "" { searchPattern := "%" + filter.Search + "%" @@ -154,6 +154,14 @@ func (s *agentTemplateStoreImpl) applyAgentTemplateFilters(query *bun.SelectQuer query = query.Where("at.type = ?", filter.Type) } + if filter.Editable != nil { + if *filter.Editable { + query = query.Where("at.user_uuid = ?", userUUID) + } else { + query = query.Where("at.user_uuid != ?", userUUID) + } + } + return query } @@ -177,7 +185,7 @@ func (s *agentTemplateStoreImpl) ListByUserUUID(ctx context.Context, userUUID st Where("at.deleted_at IS NULL"). Where("at.user_uuid = ? OR at.public = ?", userUUID, true) - query = s.applyAgentTemplateFilters(query, filter) + query = s.applyAgentTemplateFilters(query, userUUID, filter) total, err := query.Count(ctx) if err != nil { diff --git a/builder/store/database/agent_template_test.go b/builder/store/database/agent_template_test.go index c99b7fd09..eca015b63 100644 --- a/builder/store/database/agent_template_test.go +++ b/builder/store/database/agent_template_test.go @@ -282,3 +282,97 @@ func TestAgentTemplateStore_ListByUserUUID_WithFilters(t *testing.T) { require.Len(t, templates, 1) // Should return 1 template on second page require.Equal(t, 3, total) // Total should still be 3 } + +func TestAgentTemplateStore_ListByUserUUID_WithEditableFilter(t *testing.T) { + db := tests.InitTestDB() + defer db.Close() + ctx := context.TODO() + + store := database.NewAgentTemplateStoreWithDB(db) + + userUUID1 := uuid.New().String() + userUUID2 := uuid.New().String() + + // Create user1's private template (owned by user1) + user1Private := &database.AgentTemplate{ + Type: "langflow", + UserUUID: userUUID1, + Name: "User1 Private Template", + Content: "private", + Public: false, + } + _, err := store.Create(ctx, user1Private) + require.NoError(t, err) + + // Create user1's public template (owned by user1) + user1Public := &database.AgentTemplate{ + Type: "code", + UserUUID: userUUID1, + Name: "User1 Public Template", + Content: "public", + Public: true, + } + _, err = store.Create(ctx, user1Public) + require.NoError(t, err) + + // Create user2's public template (not owned by user1) + user2Public := &database.AgentTemplate{ + Type: "agno", + UserUUID: userUUID2, + Name: "User2 Public Template", + Content: "public", + Public: true, + } + _, err = store.Create(ctx, user2Public) + require.NoError(t, err) + + // Create user2's private template (not owned by user1, excluded by base query) + user2Private := &database.AgentTemplate{ + Type: "langflow", + UserUUID: userUUID2, + Name: "User2 Private Template", + Content: "private", + Public: false, + } + _, err = store.Create(ctx, user2Private) + require.NoError(t, err) + + // Editable=true should return only templates owned by user1 + editableTrue := true + templates, total, err := store.ListByUserUUID(ctx, userUUID1, types.AgentTemplateFilter{Editable: &editableTrue}, 10, 1) + require.NoError(t, err) + require.Len(t, templates, 2) + require.Equal(t, 2, total) + for _, tmpl := range templates { + require.Equal(t, userUUID1, tmpl.UserUUID, "All templates should belong to user1 when Editable=true") + } + + // Editable=false should return only public templates not owned by user1 + editableFalse := false + templates, total, err = store.ListByUserUUID(ctx, userUUID1, types.AgentTemplateFilter{Editable: &editableFalse}, 10, 1) + require.NoError(t, err) + require.Len(t, templates, 1) + require.Equal(t, 1, total) + require.Equal(t, user2Public.ID, templates[0].ID, "Only user2's public template should be returned when Editable=false") + + // Combined filter (Editable=true + Type) + templates, total, err = store.ListByUserUUID(ctx, userUUID1, types.AgentTemplateFilter{Editable: &editableTrue, Type: "langflow"}, 10, 1) + require.NoError(t, err) + require.Len(t, templates, 1) + require.Equal(t, 1, total) + require.Equal(t, user1Private.ID, templates[0].ID, "Only user1's private langflow template should be returned") + + // Combined filter (Editable=false + Type) + templates, total, err = store.ListByUserUUID(ctx, userUUID1, types.AgentTemplateFilter{Editable: &editableFalse, Type: "agno"}, 10, 1) + require.NoError(t, err) + require.Len(t, templates, 1) + require.Equal(t, 1, total) + require.Equal(t, user2Public.ID, templates[0].ID, "Only user2's public agno template should be returned") + + // Combined filter (Editable=true + Search) + templates, total, err = store.ListByUserUUID(ctx, userUUID1, types.AgentTemplateFilter{Editable: &editableTrue, Search: "Public"}, 10, 1) + require.NoError(t, err) + require.Len(t, templates, 1) + require.Equal(t, 1, total) + require.Equal(t, user1Public.ID, templates[0].ID, "Only user1's public template should match the search") +} diff --git a/common/types/agent.go b/common/types/agent.go index 108e12472..0896d587a 100644 --- a/common/types/agent.go +++ b/common/types/agent.go @@ -47,6 +47,7 @@ type AgentTemplate struct { Description *string `json:"description" binding:"omitempty,max=500"` // Agent template description Content *string `json:"content,omitempty"` // Used to store the complete content of the template Public *bool `json:"public,omitempty"` // Whether the template is public + Editable bool `json:"editable"` // Whether the template is editable (only the owner can delete) Metadata *map[string]any `json:"metadata,omitempty"` // Template metadata IsPinned bool `json:"is_pinned"` // Whether the template is pinned by the user CreatedAt time.Time `json:"created_at"` // When the template was created @@ -54,8 +55,9 @@ type AgentTemplate struct { } type AgentTemplateFilter struct { - Search string - Type string + Search string + Type string + Editable *bool } // AgentInstance represents an instance created from an agent template