| 
									
										
										
										
											2025-01-29 21:50:49 +08:00
										 |  |  | package folderimpl | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2025-02-20 02:06:26 +08:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2025-02-25 20:38:32 +08:00
										 |  |  | 	"net/http" | 
					
						
							| 
									
										
										
										
											2025-01-29 21:50:49 +08:00
										 |  |  | 	"testing" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-20 02:06:26 +08:00
										 |  |  | 	claims "github.com/grafana/authlib/types" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/apimachinery/identity" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/services/accesscontrol" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/services/apiserver/client" | 
					
						
							| 
									
										
										
										
											2025-03-05 06:31:41 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/dashboards" | 
					
						
							| 
									
										
										
										
											2025-01-29 21:50:49 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/folder" | 
					
						
							| 
									
										
										
										
											2025-03-28 04:01:07 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/user/usertest" | 
					
						
							| 
									
										
										
										
											2025-02-20 02:06:26 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/storage/unified/resource" | 
					
						
							|  |  |  | 	"github.com/stretchr/testify/mock" | 
					
						
							| 
									
										
										
										
											2025-01-29 21:50:49 +08:00
										 |  |  | 	"github.com/stretchr/testify/require" | 
					
						
							| 
									
										
										
										
											2025-02-25 20:38:32 +08:00
										 |  |  | 	apierrors "k8s.io/apimachinery/pkg/api/errors" | 
					
						
							|  |  |  | 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | 
					
						
							| 
									
										
										
										
											2025-02-20 02:06:26 +08:00
										 |  |  | 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" | 
					
						
							| 
									
										
										
										
											2025-03-05 06:31:41 +08:00
										 |  |  | 	"k8s.io/apimachinery/pkg/runtime/schema" | 
					
						
							| 
									
										
										
										
											2025-02-20 02:06:26 +08:00
										 |  |  | 	"k8s.io/apimachinery/pkg/selection" | 
					
						
							| 
									
										
										
										
											2025-01-29 21:50:49 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func TestComputeFullPath(t *testing.T) { | 
					
						
							|  |  |  | 	testCases := []struct { | 
					
						
							|  |  |  | 		name         string | 
					
						
							|  |  |  | 		parents      []*folder.Folder | 
					
						
							|  |  |  | 		wantPath     string | 
					
						
							|  |  |  | 		wantPathUIDs string | 
					
						
							|  |  |  | 	}{ | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name:         "empty slice should return empty paths", | 
					
						
							|  |  |  | 			parents:      []*folder.Folder{}, | 
					
						
							|  |  |  | 			wantPath:     "", | 
					
						
							|  |  |  | 			wantPathUIDs: "", | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name: "single element should return single path", | 
					
						
							|  |  |  | 			parents: []*folder.Folder{ | 
					
						
							|  |  |  | 				{ | 
					
						
							|  |  |  | 					Title: "Element", | 
					
						
							|  |  |  | 					UID:   "Element-uid", | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			wantPath:     "Element", | 
					
						
							|  |  |  | 			wantPathUIDs: "Element-uid", | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name: "multiple parents should return hierarchical path", | 
					
						
							|  |  |  | 			parents: []*folder.Folder{ | 
					
						
							|  |  |  | 				{ | 
					
						
							|  |  |  | 					Title: "Grandparent", | 
					
						
							|  |  |  | 					UID:   "grandparent-uid", | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 				{ | 
					
						
							|  |  |  | 					Title: "Parent", | 
					
						
							|  |  |  | 					UID:   "parent-uid", | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 				{ | 
					
						
							|  |  |  | 					Title: "Element", | 
					
						
							|  |  |  | 					UID:   "Element-uid", | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			wantPath:     "Grandparent/Parent/Element", | 
					
						
							|  |  |  | 			wantPathUIDs: "grandparent-uid/parent-uid/Element-uid", | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name: "should handle special characters in titles", | 
					
						
							|  |  |  | 			parents: []*folder.Folder{ | 
					
						
							|  |  |  | 				{ | 
					
						
							|  |  |  | 					Title: "Parent/With/Slashes", | 
					
						
							|  |  |  | 					UID:   "parent-uid", | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 				{ | 
					
						
							|  |  |  | 					Title: "Element With Spaces", | 
					
						
							|  |  |  | 					UID:   "Element-uid", | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			wantPath:     "Parent/With/Slashes/Element With Spaces", | 
					
						
							|  |  |  | 			wantPathUIDs: "parent-uid/Element-uid", | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, tc := range testCases { | 
					
						
							|  |  |  | 		t.Run(tc.name, func(t *testing.T) { | 
					
						
							|  |  |  | 			gotPath, gotPathUIDs := computeFullPath(tc.parents) | 
					
						
							|  |  |  | 			require.Equal(t, tc.wantPath, gotPath) | 
					
						
							|  |  |  | 			require.Equal(t, tc.wantPathUIDs, gotPathUIDs) | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2025-02-20 02:06:26 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-25 20:38:32 +08:00
										 |  |  | func TestGetParents(t *testing.T) { | 
					
						
							|  |  |  | 	mockCli := new(client.MockK8sHandler) | 
					
						
							|  |  |  | 	store := FolderUnifiedStoreImpl{ | 
					
						
							| 
									
										
										
										
											2025-03-28 04:01:07 +08:00
										 |  |  | 		k8sclient:   mockCli, | 
					
						
							|  |  |  | 		userService: usertest.NewUserServiceFake(), | 
					
						
							| 
									
										
										
										
											2025-02-25 20:38:32 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ctx := context.Background() | 
					
						
							|  |  |  | 	orgID := int64(1) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	t.Run("should return list of parent folders of a given folder uid", func(t *testing.T) { | 
					
						
							|  |  |  | 		mockCli.On("Get", mock.Anything, "parentone", orgID, mock.Anything, mock.Anything).Return(&unstructured.Unstructured{ | 
					
						
							|  |  |  | 			Object: map[string]interface{}{ | 
					
						
							|  |  |  | 				"metadata": map[string]interface{}{ | 
					
						
							|  |  |  | 					"name":        "parentone", | 
					
						
							|  |  |  | 					"annotations": map[string]interface{}{"grafana.app/folder": "parenttwo"}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, nil).Once() | 
					
						
							|  |  |  | 		mockCli.On("Get", mock.Anything, "parenttwo", orgID, mock.Anything, mock.Anything).Return(&unstructured.Unstructured{ | 
					
						
							|  |  |  | 			Object: map[string]interface{}{ | 
					
						
							|  |  |  | 				"metadata": map[string]interface{}{ | 
					
						
							|  |  |  | 					"name":        "parenttwo", | 
					
						
							|  |  |  | 					"annotations": map[string]interface{}{"grafana.app/folder": "parentthree"}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, nil).Once() | 
					
						
							|  |  |  | 		mockCli.On("Get", mock.Anything, "parentthree", orgID, mock.Anything, mock.Anything).Return(&unstructured.Unstructured{ | 
					
						
							|  |  |  | 			Object: map[string]interface{}{ | 
					
						
							|  |  |  | 				"metadata": map[string]interface{}{ | 
					
						
							|  |  |  | 					"name":        "parentthree", | 
					
						
							|  |  |  | 					"annotations": map[string]interface{}{"grafana.app/folder": "parentfour"}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, nil).Once() | 
					
						
							|  |  |  | 		mockCli.On("Get", mock.Anything, "parentfour", orgID, mock.Anything, mock.Anything).Return(&unstructured.Unstructured{ | 
					
						
							|  |  |  | 			Object: map[string]interface{}{ | 
					
						
							|  |  |  | 				"metadata": map[string]interface{}{ | 
					
						
							|  |  |  | 					"name": "parentfour", | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, nil).Once() | 
					
						
							|  |  |  | 		result, err := store.GetParents(ctx, folder.GetParentsQuery{ | 
					
						
							|  |  |  | 			UID:   "parentone", | 
					
						
							|  |  |  | 			OrgID: orgID, | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		require.NoError(t, err) | 
					
						
							|  |  |  | 		require.Len(t, result, 3) | 
					
						
							|  |  |  | 		require.Equal(t, "parentfour", result[0].UID) | 
					
						
							|  |  |  | 		require.Equal(t, "parentthree", result[1].UID) | 
					
						
							|  |  |  | 		require.Equal(t, "parenttwo", result[2].UID) | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	t.Run("should stop if user doesnt have access to the parent folder", func(t *testing.T) { | 
					
						
							|  |  |  | 		mockCli.On("Get", mock.Anything, "parentone", orgID, mock.Anything, mock.Anything).Return(&unstructured.Unstructured{ | 
					
						
							|  |  |  | 			Object: map[string]interface{}{ | 
					
						
							|  |  |  | 				"metadata": map[string]interface{}{ | 
					
						
							|  |  |  | 					"name":        "parentone", | 
					
						
							|  |  |  | 					"annotations": map[string]interface{}{"grafana.app/folder": "parenttwo"}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, nil).Once() | 
					
						
							|  |  |  | 		mockCli.On("Get", mock.Anything, "parenttwo", orgID, mock.Anything, mock.Anything).Return(&unstructured.Unstructured{ | 
					
						
							|  |  |  | 			Object: map[string]interface{}{ | 
					
						
							|  |  |  | 				"metadata": map[string]interface{}{ | 
					
						
							|  |  |  | 					"name":        "parenttwo", | 
					
						
							|  |  |  | 					"annotations": map[string]interface{}{"grafana.app/folder": "parentthree"}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, nil).Once() | 
					
						
							|  |  |  | 		mockCli.On("Get", mock.Anything, "parentthree", orgID, mock.Anything, mock.Anything).Return(nil, &apierrors.StatusError{ | 
					
						
							|  |  |  | 			ErrStatus: metav1.Status{Code: http.StatusForbidden}, | 
					
						
							|  |  |  | 		}).Once() | 
					
						
							|  |  |  | 		result, err := store.GetParents(ctx, folder.GetParentsQuery{ | 
					
						
							|  |  |  | 			UID:   "parentone", | 
					
						
							|  |  |  | 			OrgID: orgID, | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		require.NoError(t, err) | 
					
						
							|  |  |  | 		require.Len(t, result, 1) | 
					
						
							|  |  |  | 		require.Equal(t, "parenttwo", result[0].UID) | 
					
						
							|  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2025-03-05 06:31:41 +08:00
										 |  |  | 	t.Run("should stop if parent folder is not found", func(t *testing.T) { | 
					
						
							|  |  |  | 		mockCli.On("Get", mock.Anything, "parentone", orgID, mock.Anything, mock.Anything).Return(nil, apierrors.NewNotFound(schema.GroupResource{Group: "folders.folder.grafana.app", Resource: "folder"}, "parentone")).Once() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		_, err := store.GetParents(ctx, folder.GetParentsQuery{ | 
					
						
							|  |  |  | 			UID:   "parentone", | 
					
						
							|  |  |  | 			OrgID: orgID, | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		require.ErrorIs(t, err, dashboards.ErrFolderNotFound) | 
					
						
							|  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2025-02-25 20:38:32 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-20 02:06:26 +08:00
										 |  |  | func TestGetChildren(t *testing.T) { | 
					
						
							|  |  |  | 	mockCli := new(client.MockK8sHandler) | 
					
						
							|  |  |  | 	store := FolderUnifiedStoreImpl{ | 
					
						
							| 
									
										
										
										
											2025-03-28 04:01:07 +08:00
										 |  |  | 		k8sclient:   mockCli, | 
					
						
							|  |  |  | 		userService: usertest.NewUserServiceFake(), | 
					
						
							| 
									
										
										
										
											2025-02-20 02:06:26 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ctx := context.Background() | 
					
						
							|  |  |  | 	orgID := int64(2) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	t.Run("should be able to find children folders, and set defaults for pages", func(t *testing.T) { | 
					
						
							|  |  |  | 		mockCli.On("Search", mock.Anything, orgID, &resource.ResourceSearchRequest{ | 
					
						
							|  |  |  | 			Options: &resource.ListOptions{ | 
					
						
							|  |  |  | 				Fields: []*resource.Requirement{ | 
					
						
							|  |  |  | 					{ | 
					
						
							|  |  |  | 						Key:      resource.SEARCH_FIELD_FOLDER, | 
					
						
							|  |  |  | 						Operator: string(selection.In), | 
					
						
							|  |  |  | 						Values:   []string{"folder1"}, | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			Limit:  folderSearchLimit, // should default to folderSearchLimit
 | 
					
						
							|  |  |  | 			Offset: 0,                 // should be set as limit * (page - 1)
 | 
					
						
							|  |  |  | 			Page:   1,                 // should be set to 1 by default
 | 
					
						
							|  |  |  | 		}).Return(&resource.ResourceSearchResponse{ | 
					
						
							|  |  |  | 			Results: &resource.ResourceTable{ | 
					
						
							|  |  |  | 				Columns: []*resource.ResourceTableColumnDefinition{ | 
					
						
							|  |  |  | 					{Name: "folder", Type: resource.ResourceTableColumnDefinition_STRING}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 				Rows: []*resource.ResourceTableRow{ | 
					
						
							|  |  |  | 					{ | 
					
						
							|  |  |  | 						Key:   &resource.ResourceKey{Name: "folder2", Resource: "folder"}, | 
					
						
							|  |  |  | 						Cells: [][]byte{[]byte("folder1")}, | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 					{ | 
					
						
							|  |  |  | 						Key:   &resource.ResourceKey{Name: "folder3", Resource: "folder"}, | 
					
						
							|  |  |  | 						Cells: [][]byte{[]byte("folder1")}, | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			TotalHits: 1, | 
					
						
							|  |  |  | 		}, nil).Once() | 
					
						
							| 
									
										
										
										
											2025-03-05 06:31:41 +08:00
										 |  |  | 		mockCli.On("Get", mock.Anything, "folder1", orgID, mock.Anything, mock.Anything).Return(&unstructured.Unstructured{ | 
					
						
							|  |  |  | 			Object: map[string]interface{}{ | 
					
						
							|  |  |  | 				"metadata": map[string]interface{}{"name": "folder1"}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, nil).Once() | 
					
						
							| 
									
										
										
										
											2025-02-20 02:06:26 +08:00
										 |  |  | 		mockCli.On("Get", mock.Anything, "folder2", orgID, mock.Anything, mock.Anything).Return(&unstructured.Unstructured{ | 
					
						
							|  |  |  | 			Object: map[string]interface{}{ | 
					
						
							|  |  |  | 				"metadata": map[string]interface{}{"name": "folder2"}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, nil).Once() | 
					
						
							|  |  |  | 		mockCli.On("Get", mock.Anything, "folder3", orgID, mock.Anything, mock.Anything).Return(&unstructured.Unstructured{ | 
					
						
							|  |  |  | 			Object: map[string]interface{}{ | 
					
						
							|  |  |  | 				"metadata": map[string]interface{}{"name": "folder3"}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, nil).Once() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// don't set page or limit - should be automatically added
 | 
					
						
							|  |  |  | 		result, err := store.GetChildren(ctx, folder.GetChildrenQuery{ | 
					
						
							|  |  |  | 			UID:   "folder1", | 
					
						
							|  |  |  | 			OrgID: orgID, | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 		require.NoError(t, err) | 
					
						
							|  |  |  | 		require.Len(t, result, 2) | 
					
						
							|  |  |  | 		require.Equal(t, "folder2", result[0].UID) | 
					
						
							|  |  |  | 		require.Equal(t, "folder3", result[1].UID) | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-05 06:31:41 +08:00
										 |  |  | 	t.Run("should return an error if the folder is not found", func(t *testing.T) { | 
					
						
							|  |  |  | 		mockCli.On("Search", mock.Anything, orgID, &resource.ResourceSearchRequest{ | 
					
						
							|  |  |  | 			Options: &resource.ListOptions{ | 
					
						
							|  |  |  | 				Fields: []*resource.Requirement{ | 
					
						
							|  |  |  | 					{ | 
					
						
							|  |  |  | 						Key:      resource.SEARCH_FIELD_FOLDER, | 
					
						
							|  |  |  | 						Operator: string(selection.In), | 
					
						
							|  |  |  | 						Values:   []string{"folder1"}, | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			Limit:  folderSearchLimit, // should default to folderSearchLimit
 | 
					
						
							|  |  |  | 			Offset: 0,                 // should be set as limit * (page - 1)
 | 
					
						
							|  |  |  | 			Page:   1,                 // should be set to 1 by default
 | 
					
						
							|  |  |  | 		}).Return(&resource.ResourceSearchResponse{ | 
					
						
							|  |  |  | 			Results: &resource.ResourceTable{ | 
					
						
							|  |  |  | 				Columns: []*resource.ResourceTableColumnDefinition{ | 
					
						
							|  |  |  | 					{Name: "folder", Type: resource.ResourceTableColumnDefinition_STRING}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 				Rows: []*resource.ResourceTableRow{ | 
					
						
							|  |  |  | 					{ | 
					
						
							|  |  |  | 						Key:   &resource.ResourceKey{Name: "folder2", Resource: "folder"}, | 
					
						
							|  |  |  | 						Cells: [][]byte{[]byte("folder1")}, | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 					{ | 
					
						
							|  |  |  | 						Key:   &resource.ResourceKey{Name: "folder3", Resource: "folder"}, | 
					
						
							|  |  |  | 						Cells: [][]byte{[]byte("folder1")}, | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			TotalHits: 1, | 
					
						
							|  |  |  | 		}, nil).Once() | 
					
						
							|  |  |  | 		mockCli.On("Get", mock.Anything, "folder1", orgID, mock.Anything, mock.Anything).Return(nil, apierrors.NewNotFound(schema.GroupResource{Group: "folders.folder.grafana.app", Resource: "folder"}, "folder1")).Once() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		_, err := store.GetChildren(ctx, folder.GetChildrenQuery{ | 
					
						
							|  |  |  | 			UID:   "folder1", | 
					
						
							|  |  |  | 			OrgID: orgID, | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 		require.ErrorIs(t, err, dashboards.ErrFolderNotFound) | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-20 02:06:26 +08:00
										 |  |  | 	t.Run("pages should be able to be set, general folder should be turned to empty string, and folder uids should be passed in", func(t *testing.T) { | 
					
						
							|  |  |  | 		mockCli.On("Search", mock.Anything, orgID, &resource.ResourceSearchRequest{ | 
					
						
							|  |  |  | 			Options: &resource.ListOptions{ | 
					
						
							|  |  |  | 				Fields: []*resource.Requirement{ | 
					
						
							|  |  |  | 					{ | 
					
						
							|  |  |  | 						Key:      resource.SEARCH_FIELD_FOLDER, | 
					
						
							|  |  |  | 						Operator: string(selection.In), | 
					
						
							|  |  |  | 						Values:   []string{""}, // should be an empty string if general is passed in
 | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 					{ | 
					
						
							|  |  |  | 						Key:      resource.SEARCH_FIELD_NAME, | 
					
						
							|  |  |  | 						Operator: string(selection.In), | 
					
						
							|  |  |  | 						Values:   []string{"folder2"}, | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			Limit:  10, | 
					
						
							|  |  |  | 			Offset: 20, // should be set as limit * (page - 1)
 | 
					
						
							|  |  |  | 			Page:   3, | 
					
						
							|  |  |  | 		}).Return(&resource.ResourceSearchResponse{ | 
					
						
							|  |  |  | 			Results: &resource.ResourceTable{ | 
					
						
							|  |  |  | 				Columns: []*resource.ResourceTableColumnDefinition{ | 
					
						
							|  |  |  | 					{Name: "folder", Type: resource.ResourceTableColumnDefinition_STRING}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 				Rows: []*resource.ResourceTableRow{ | 
					
						
							|  |  |  | 					{ | 
					
						
							|  |  |  | 						Key:   &resource.ResourceKey{Name: "folder2", Resource: "folder"}, | 
					
						
							|  |  |  | 						Cells: [][]byte{[]byte("folder1")}, | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			TotalHits: 1, | 
					
						
							|  |  |  | 		}, nil).Once() | 
					
						
							|  |  |  | 		mockCli.On("Get", mock.Anything, "folder2", orgID, mock.Anything, mock.Anything).Return(&unstructured.Unstructured{ | 
					
						
							|  |  |  | 			Object: map[string]interface{}{ | 
					
						
							|  |  |  | 				"metadata": map[string]interface{}{"name": "folder2"}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, nil).Once() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		result, err := store.GetChildren(ctx, folder.GetChildrenQuery{ | 
					
						
							|  |  |  | 			UID:        "general", | 
					
						
							|  |  |  | 			OrgID:      orgID, | 
					
						
							|  |  |  | 			Limit:      10, | 
					
						
							|  |  |  | 			Page:       3, | 
					
						
							|  |  |  | 			FolderUIDs: []string{"folder2"}, | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 		require.NoError(t, err) | 
					
						
							|  |  |  | 		require.Len(t, result, 1) | 
					
						
							|  |  |  | 		require.Equal(t, "folder2", result[0].UID) | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	t.Run("k6 folder should only be returned to service accounts", func(t *testing.T) { | 
					
						
							|  |  |  | 		mockCli.On("Search", mock.Anything, orgID, mock.Anything).Return(&resource.ResourceSearchResponse{ | 
					
						
							|  |  |  | 			Results: &resource.ResourceTable{ | 
					
						
							|  |  |  | 				Columns: []*resource.ResourceTableColumnDefinition{ | 
					
						
							|  |  |  | 					{Name: "folder", Type: resource.ResourceTableColumnDefinition_STRING}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 				Rows: []*resource.ResourceTableRow{ | 
					
						
							|  |  |  | 					{ | 
					
						
							|  |  |  | 						Key:   &resource.ResourceKey{Name: accesscontrol.K6FolderUID, Resource: "folder"}, | 
					
						
							|  |  |  | 						Cells: [][]byte{[]byte("folder1")}, | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			TotalHits: 1, | 
					
						
							|  |  |  | 		}, nil) | 
					
						
							| 
									
										
										
										
											2025-03-05 06:31:41 +08:00
										 |  |  | 		mockCli.On("Get", mock.Anything, "folder", orgID, mock.Anything, mock.Anything).Return(&unstructured.Unstructured{ | 
					
						
							|  |  |  | 			Object: map[string]interface{}{ | 
					
						
							|  |  |  | 				"metadata": map[string]interface{}{"name": "folder"}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, nil) | 
					
						
							| 
									
										
										
										
											2025-02-20 02:06:26 +08:00
										 |  |  | 		mockCli.On("Get", mock.Anything, accesscontrol.K6FolderUID, orgID, mock.Anything, mock.Anything).Return(&unstructured.Unstructured{ | 
					
						
							|  |  |  | 			Object: map[string]interface{}{ | 
					
						
							|  |  |  | 				"metadata": map[string]interface{}{"name": accesscontrol.K6FolderUID}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, nil) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		result, err := store.GetChildren(ctx, folder.GetChildrenQuery{ | 
					
						
							|  |  |  | 			UID:   "folder", | 
					
						
							|  |  |  | 			OrgID: orgID, | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 		require.NoError(t, err) | 
					
						
							|  |  |  | 		require.Len(t, result, 0) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		result, err = store.GetChildren(ctx, folder.GetChildrenQuery{ | 
					
						
							|  |  |  | 			UID:          "folder", | 
					
						
							|  |  |  | 			OrgID:        orgID, | 
					
						
							|  |  |  | 			SignedInUser: &identity.StaticRequester{Type: claims.TypeServiceAccount}, | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 		require.NoError(t, err) | 
					
						
							|  |  |  | 		require.Len(t, result, 1) | 
					
						
							|  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2025-03-29 07:13:08 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	t.Run("should not do get requests for the children if RefOnly is true", func(t *testing.T) { | 
					
						
							|  |  |  | 		mockCli.On("Search", mock.Anything, orgID, &resource.ResourceSearchRequest{ | 
					
						
							|  |  |  | 			Options: &resource.ListOptions{ | 
					
						
							|  |  |  | 				Fields: []*resource.Requirement{ | 
					
						
							|  |  |  | 					{ | 
					
						
							|  |  |  | 						Key:      resource.SEARCH_FIELD_FOLDER, | 
					
						
							|  |  |  | 						Operator: string(selection.In), | 
					
						
							|  |  |  | 						Values:   []string{"folder1"}, | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			Limit:  folderSearchLimit, // should default to folderSearchLimit
 | 
					
						
							|  |  |  | 			Offset: 0,                 // should be set as limit * (page - 1)
 | 
					
						
							|  |  |  | 			Page:   1,                 // should be set to 1 by default
 | 
					
						
							|  |  |  | 		}).Return(&resource.ResourceSearchResponse{ | 
					
						
							|  |  |  | 			Results: &resource.ResourceTable{ | 
					
						
							|  |  |  | 				Columns: []*resource.ResourceTableColumnDefinition{ | 
					
						
							|  |  |  | 					{Name: "folder", Type: resource.ResourceTableColumnDefinition_STRING}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 				Rows: []*resource.ResourceTableRow{ | 
					
						
							|  |  |  | 					{ | 
					
						
							|  |  |  | 						Key:   &resource.ResourceKey{Name: "folder2", Resource: "folder"}, | 
					
						
							|  |  |  | 						Cells: [][]byte{[]byte("folder1")}, | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 					{ | 
					
						
							|  |  |  | 						Key:   &resource.ResourceKey{Name: "folder3", Resource: "folder"}, | 
					
						
							|  |  |  | 						Cells: [][]byte{[]byte("folder1")}, | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			TotalHits: 1, | 
					
						
							|  |  |  | 		}, nil).Once() | 
					
						
							|  |  |  | 		// only get the parent folder in this request
 | 
					
						
							|  |  |  | 		mockCli.On("Get", mock.Anything, "folder1", orgID, mock.Anything, mock.Anything).Return(&unstructured.Unstructured{ | 
					
						
							|  |  |  | 			Object: map[string]interface{}{ | 
					
						
							|  |  |  | 				"metadata": map[string]interface{}{"name": "folder1"}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, nil).Once() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// don't set page or limit - should be automatically added
 | 
					
						
							|  |  |  | 		result, err := store.GetChildren(ctx, folder.GetChildrenQuery{ | 
					
						
							| 
									
										
										
										
											2025-04-02 12:30:17 +08:00
										 |  |  | 			UID:   "folder1", | 
					
						
							|  |  |  | 			OrgID: orgID, | 
					
						
							| 
									
										
										
										
											2025-03-29 07:13:08 +08:00
										 |  |  | 		}) | 
					
						
							|  |  |  | 		require.NoError(t, err) | 
					
						
							|  |  |  | 		require.Len(t, result, 2) | 
					
						
							|  |  |  | 		require.Equal(t, "folder2", result[0].UID) | 
					
						
							|  |  |  | 		require.Equal(t, "folder3", result[1].UID) | 
					
						
							|  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2025-02-20 02:06:26 +08:00
										 |  |  | } |