| 
									
										
										
										
											2023-07-07 16:55:01 +08:00
										 |  |  | package api | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"context" | 
					
						
							|  |  |  | 	"encoding/json" | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"math/rand" | 
					
						
							|  |  |  | 	"net/http" | 
					
						
							|  |  |  | 	"net/http/httptest" | 
					
						
							|  |  |  | 	"testing" | 
					
						
							|  |  |  | 	"time" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-13 23:55:15 +08:00
										 |  |  | 	"github.com/stretchr/testify/assert" | 
					
						
							|  |  |  | 	"github.com/stretchr/testify/require" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-07 16:55:01 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/api/dtos" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/api/routing" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/bus" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/components/simplejson" | 
					
						
							| 
									
										
										
										
											2024-04-04 21:04:47 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/infra/db" | 
					
						
							| 
									
										
										
										
											2023-07-07 16:55:01 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/infra/localcache" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/infra/log" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/infra/tracing" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/services/accesscontrol" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl" | 
					
						
							|  |  |  | 	acdb "github.com/grafana/grafana/pkg/services/accesscontrol/database" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/services/accesscontrol/ossaccesscontrol" | 
					
						
							| 
									
										
										
										
											2024-04-19 22:38:14 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/accesscontrol/resourcepermissions" | 
					
						
							| 
									
										
										
										
											2024-07-02 20:45:25 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/authz/zanzana" | 
					
						
							| 
									
										
										
										
											2023-07-07 16:55:01 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/contexthandler/ctxkey" | 
					
						
							|  |  |  | 	contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/services/dashboards" | 
					
						
							| 
									
										
										
										
											2023-11-22 21:20:22 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/dashboards/dashboardaccess" | 
					
						
							| 
									
										
										
										
											2023-07-07 16:55:01 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/dashboards/database" | 
					
						
							|  |  |  | 	dashboardservice "github.com/grafana/grafana/pkg/services/dashboards/service" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/services/featuremgmt" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/services/folder/folderimpl" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/services/guardian" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/services/licensing/licensingtest" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/services/org/orgimpl" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/services/quota/quotatest" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/services/search" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/services/sqlstore" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/services/star" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/services/star/startest" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/services/supportbundles/bundleregistry" | 
					
						
							| 
									
										
										
										
											2024-02-26 18:27:22 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/supportbundles/supportbundlestest" | 
					
						
							| 
									
										
										
										
											2023-07-07 16:55:01 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/tag/tagimpl" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/services/team" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/services/team/teamimpl" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/services/user" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/services/user/userimpl" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/setting" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/web" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/web/webtest" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ( | 
					
						
							|  |  |  | 	LEVEL0_FOLDER_NUM    = 300 | 
					
						
							|  |  |  | 	LEVEL1_FOLDER_NUM    = 30 | 
					
						
							|  |  |  | 	LEVEL2_FOLDER_NUM    = 5 | 
					
						
							|  |  |  | 	LEVEL0_DASHBOARD_NUM = 300 | 
					
						
							|  |  |  | 	LEVEL1_DASHBOARD_NUM = 30 | 
					
						
							|  |  |  | 	LEVEL2_DASHBOARD_NUM = 5 | 
					
						
							|  |  |  | 	TEAM_NUM             = 50 | 
					
						
							|  |  |  | 	TEAM_MEMBER_NUM      = 5 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	MAXIMUM_INT_POSTGRES = 2147483647 | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type benchScenario struct { | 
					
						
							| 
									
										
										
										
											2024-04-04 21:04:47 +08:00
										 |  |  | 	db db.DB | 
					
						
							| 
									
										
										
										
											2023-07-07 16:55:01 +08:00
										 |  |  | 	// signedInUser is the user that is signed in to the server
 | 
					
						
							|  |  |  | 	cfg          *setting.Cfg | 
					
						
							|  |  |  | 	signedInUser *user.SignedInUser | 
					
						
							|  |  |  | 	teamSvc      team.Service | 
					
						
							|  |  |  | 	userSvc      user.Service | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func BenchmarkFolderListAndSearch(b *testing.B) { | 
					
						
							|  |  |  | 	start := time.Now() | 
					
						
							|  |  |  | 	b.Log("setup start") | 
					
						
							|  |  |  | 	sc := setupDB(b) | 
					
						
							|  |  |  | 	b.Log("setup time:", time.Since(start)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	all := LEVEL0_FOLDER_NUM*LEVEL0_DASHBOARD_NUM + LEVEL0_FOLDER_NUM*LEVEL1_FOLDER_NUM*LEVEL1_DASHBOARD_NUM + LEVEL0_FOLDER_NUM*LEVEL1_FOLDER_NUM*LEVEL2_FOLDER_NUM*LEVEL2_DASHBOARD_NUM | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// the maximum number of dashboards that can be returned by the search API
 | 
					
						
							|  |  |  | 	// otherwise the handler fails with 422 status code
 | 
					
						
							|  |  |  | 	const limit = 5000 | 
					
						
							|  |  |  | 	withLimit := func(res int) int { | 
					
						
							|  |  |  | 		if res > limit { | 
					
						
							|  |  |  | 			return limit | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return res | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	benchmarks := []struct { | 
					
						
							|  |  |  | 		desc        string | 
					
						
							|  |  |  | 		url         string | 
					
						
							|  |  |  | 		expectedLen int | 
					
						
							| 
									
										
										
										
											2024-01-10 02:38:06 +08:00
										 |  |  | 		features    featuremgmt.FeatureToggles | 
					
						
							| 
									
										
										
										
											2023-07-07 16:55:01 +08:00
										 |  |  | 	}{ | 
					
						
							|  |  |  | 		{ | 
					
						
							| 
									
										
										
										
											2023-11-06 21:16:23 +08:00
										 |  |  | 			desc:        "impl=default nested_folders=on get root folders", | 
					
						
							|  |  |  | 			url:         "/api/folders", | 
					
						
							| 
									
										
										
										
											2023-12-16 01:34:08 +08:00
										 |  |  | 			expectedLen: LEVEL0_FOLDER_NUM + 1, // for shared with me folder
 | 
					
						
							| 
									
										
										
										
											2023-11-06 21:16:23 +08:00
										 |  |  | 			features:    featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders), | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2023-07-07 16:55:01 +08:00
										 |  |  | 		{ | 
					
						
							| 
									
										
										
										
											2023-11-06 21:16:23 +08:00
										 |  |  | 			desc:        "impl=default nested_folders=on get subfolders", | 
					
						
							|  |  |  | 			url:         "/api/folders?parentUid=folder0", | 
					
						
							|  |  |  | 			expectedLen: LEVEL1_FOLDER_NUM, | 
					
						
							|  |  |  | 			features:    featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders), | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2023-07-07 16:55:01 +08:00
										 |  |  | 		{ | 
					
						
							| 
									
										
										
										
											2023-11-06 21:16:23 +08:00
										 |  |  | 			desc:        "impl=default nested_folders=on list all inherited dashboards", | 
					
						
							|  |  |  | 			url:         "/api/search?type=dash-db&limit=5000", | 
					
						
							|  |  |  | 			expectedLen: withLimit(all), | 
					
						
							|  |  |  | 			features:    featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders), | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			desc:        "impl=permissionsFilterRemoveSubquery nested_folders=on list all inherited dashboards", | 
					
						
							| 
									
										
										
										
											2023-07-07 16:55:01 +08:00
										 |  |  | 			url:         "/api/search?type=dash-db&limit=5000", | 
					
						
							|  |  |  | 			expectedLen: withLimit(all), | 
					
						
							| 
									
										
										
										
											2023-08-02 15:39:25 +08:00
										 |  |  | 			features:    featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders, featuremgmt.FlagPermissionsFilterRemoveSubquery), | 
					
						
							| 
									
										
										
										
											2023-07-07 16:55:01 +08:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							| 
									
										
										
										
											2023-11-06 21:16:23 +08:00
										 |  |  | 			desc:        "impl=default nested_folders=on search for pattern", | 
					
						
							|  |  |  | 			url:         "/api/search?type=dash-db&query=dashboard_0_0&limit=5000", | 
					
						
							|  |  |  | 			expectedLen: withLimit(1 + LEVEL1_DASHBOARD_NUM + LEVEL2_FOLDER_NUM*LEVEL2_DASHBOARD_NUM), | 
					
						
							|  |  |  | 			features:    featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders), | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			desc:        "impl=permissionsFilterRemoveSubquery nested_folders=on search for pattern", | 
					
						
							| 
									
										
										
										
											2023-07-07 16:55:01 +08:00
										 |  |  | 			url:         "/api/search?type=dash-db&query=dashboard_0_0&limit=5000", | 
					
						
							|  |  |  | 			expectedLen: withLimit(1 + LEVEL1_DASHBOARD_NUM + LEVEL2_FOLDER_NUM*LEVEL2_DASHBOARD_NUM), | 
					
						
							| 
									
										
										
										
											2023-08-02 15:39:25 +08:00
										 |  |  | 			features:    featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders, featuremgmt.FlagPermissionsFilterRemoveSubquery), | 
					
						
							| 
									
										
										
										
											2023-07-07 16:55:01 +08:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							| 
									
										
										
										
											2023-11-06 21:16:23 +08:00
										 |  |  | 			desc:        "impl=default nested_folders=on search for specific dashboard", | 
					
						
							|  |  |  | 			url:         "/api/search?type=dash-db&query=dashboard_0_0_0_0", | 
					
						
							|  |  |  | 			expectedLen: 1, | 
					
						
							|  |  |  | 			features:    featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders), | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			desc:        "impl=permissionsFilterRemoveSubquery nested_folders=on search for specific dashboard", | 
					
						
							| 
									
										
										
										
											2023-07-07 16:55:01 +08:00
										 |  |  | 			url:         "/api/search?type=dash-db&query=dashboard_0_0_0_0", | 
					
						
							|  |  |  | 			expectedLen: 1, | 
					
						
							| 
									
										
										
										
											2023-08-02 15:39:25 +08:00
										 |  |  | 			features:    featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders, featuremgmt.FlagPermissionsFilterRemoveSubquery), | 
					
						
							| 
									
										
										
										
											2023-07-07 16:55:01 +08:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							| 
									
										
										
										
											2023-11-06 21:16:23 +08:00
										 |  |  | 			desc:        "impl=default nested_folders=off get root folders", | 
					
						
							|  |  |  | 			url:         "/api/folders?limit=5000", | 
					
						
							|  |  |  | 			expectedLen: withLimit(LEVEL0_FOLDER_NUM), | 
					
						
							|  |  |  | 			features:    featuremgmt.WithFeatures(), | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2023-07-07 16:55:01 +08:00
										 |  |  | 		{ | 
					
						
							| 
									
										
										
										
											2023-11-06 21:16:23 +08:00
										 |  |  | 			desc:        "impl=default nested_folders=off list all dashboards", | 
					
						
							|  |  |  | 			url:         "/api/search?type=dash-db&limit=5000", | 
					
						
							|  |  |  | 			expectedLen: withLimit(LEVEL0_FOLDER_NUM * LEVEL0_DASHBOARD_NUM), | 
					
						
							|  |  |  | 			features:    featuremgmt.WithFeatures(), | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			desc:        "impl=permissionsFilterRemoveSubquery nested_folders=off list all dashboards", | 
					
						
							| 
									
										
										
										
											2023-07-07 16:55:01 +08:00
										 |  |  | 			url:         "/api/search?type=dash-db&limit=5000", | 
					
						
							|  |  |  | 			expectedLen: withLimit(LEVEL0_FOLDER_NUM * LEVEL0_DASHBOARD_NUM), | 
					
						
							| 
									
										
										
										
											2023-08-02 15:39:25 +08:00
										 |  |  | 			features:    featuremgmt.WithFeatures(featuremgmt.FlagPermissionsFilterRemoveSubquery), | 
					
						
							| 
									
										
										
										
											2023-07-07 16:55:01 +08:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							| 
									
										
										
										
											2023-11-06 21:16:23 +08:00
										 |  |  | 			desc:        "impl=default nested_folders=off search specific dashboard", | 
					
						
							|  |  |  | 			url:         "/api/search?type=dash-db&query=dashboard_0_0", | 
					
						
							|  |  |  | 			expectedLen: 1, | 
					
						
							|  |  |  | 			features:    featuremgmt.WithFeatures(), | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			desc:        "impl=permissionsFilterRemoveSubquery nested_folders=off search specific dashboard", | 
					
						
							| 
									
										
										
										
											2023-07-07 16:55:01 +08:00
										 |  |  | 			url:         "/api/search?type=dash-db&query=dashboard_0_0", | 
					
						
							|  |  |  | 			expectedLen: 1, | 
					
						
							| 
									
										
										
										
											2023-08-02 15:39:25 +08:00
										 |  |  | 			features:    featuremgmt.WithFeatures(featuremgmt.FlagPermissionsFilterRemoveSubquery), | 
					
						
							| 
									
										
										
										
											2023-07-07 16:55:01 +08:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for _, bm := range benchmarks { | 
					
						
							|  |  |  | 		b.Run(bm.desc, func(b *testing.B) { | 
					
						
							|  |  |  | 			m := setupServer(b, sc, bm.features) | 
					
						
							|  |  |  | 			req := httptest.NewRequest(http.MethodGet, bm.url, nil) | 
					
						
							|  |  |  | 			req = webtest.RequestWithSignedInUser(req, sc.signedInUser) | 
					
						
							|  |  |  | 			b.ResetTimer() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			for i := 0; i < b.N; i++ { | 
					
						
							|  |  |  | 				rec := httptest.NewRecorder() | 
					
						
							|  |  |  | 				m.ServeHTTP(rec, req) | 
					
						
							|  |  |  | 				require.Equal(b, 200, rec.Code) | 
					
						
							|  |  |  | 				var resp []dtos.FolderSearchHit | 
					
						
							|  |  |  | 				err := json.Unmarshal(rec.Body.Bytes(), &resp) | 
					
						
							|  |  |  | 				require.NoError(b, err) | 
					
						
							|  |  |  | 				assert.Len(b, resp, bm.expectedLen) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func setupDB(b testing.TB) benchScenario { | 
					
						
							|  |  |  | 	b.Helper() | 
					
						
							| 
									
										
										
										
											2024-04-24 16:38:40 +08:00
										 |  |  | 	db, cfg := sqlstore.InitTestDB(b) | 
					
						
							| 
									
										
										
										
											2023-07-07 16:55:01 +08:00
										 |  |  | 	IDs := map[int64]struct{}{} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	opts := sqlstore.NativeSettingsForDialect(db.GetDialect()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	quotaService := quotatest.New(false, nil) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-29 18:32:03 +08:00
										 |  |  | 	teamSvc, err := teamimpl.ProvideService(db, cfg, tracing.InitializeTracerForTest()) | 
					
						
							| 
									
										
										
										
											2024-02-12 21:48:29 +08:00
										 |  |  | 	require.NoError(b, err) | 
					
						
							| 
									
										
										
										
											2023-07-07 16:55:01 +08:00
										 |  |  | 	orgService, err := orgimpl.ProvideService(db, cfg, quotaService) | 
					
						
							|  |  |  | 	require.NoError(b, err) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	cache := localcache.ProvideService() | 
					
						
							| 
									
										
										
										
											2024-04-30 19:15:56 +08:00
										 |  |  | 	userSvc, err := userimpl.ProvideService( | 
					
						
							|  |  |  | 		db, orgService, cfg, teamSvc, cache, tracing.InitializeTracerForTest(), | 
					
						
							|  |  |  | 		"atest.FakeQuotaService{}, bundleregistry.ProvideService(), | 
					
						
							|  |  |  | 	) | 
					
						
							| 
									
										
										
										
											2023-07-07 16:55:01 +08:00
										 |  |  | 	require.NoError(b, err) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var orgID int64 = 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	userIDs := make([]int64, 0, TEAM_MEMBER_NUM) | 
					
						
							|  |  |  | 	for i := 0; i < TEAM_MEMBER_NUM; i++ { | 
					
						
							|  |  |  | 		u, err := userSvc.Create(context.Background(), &user.CreateUserCommand{ | 
					
						
							|  |  |  | 			OrgID: orgID, | 
					
						
							|  |  |  | 			Login: fmt.Sprintf("user%d", i), | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 		require.NoError(b, err) | 
					
						
							|  |  |  | 		require.NotZero(b, u.ID) | 
					
						
							|  |  |  | 		userIDs = append(userIDs, u.ID) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	signedInUser := user.SignedInUser{UserID: userIDs[0], OrgID: orgID, Permissions: map[int64]map[string][]string{ | 
					
						
							|  |  |  | 		orgID: {dashboards.ActionFoldersCreate: {}, dashboards.ActionFoldersWrite: {dashboards.ScopeFoldersAll}}, | 
					
						
							|  |  |  | 	}} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	now := time.Now() | 
					
						
							|  |  |  | 	roles := make([]accesscontrol.Role, 0, TEAM_NUM) | 
					
						
							|  |  |  | 	teams := make([]team.Team, 0, TEAM_NUM) | 
					
						
							|  |  |  | 	teamMembers := make([]team.TeamMember, 0, TEAM_MEMBER_NUM) | 
					
						
							|  |  |  | 	teamRoles := make([]accesscontrol.TeamRole, 0, TEAM_NUM) | 
					
						
							|  |  |  | 	for i := 1; i < TEAM_NUM+1; i++ { | 
					
						
							|  |  |  | 		teamID := int64(i) | 
					
						
							|  |  |  | 		teams = append(teams, team.Team{ | 
					
						
							|  |  |  | 			UID:     fmt.Sprintf("team%d", i), | 
					
						
							|  |  |  | 			ID:      teamID, | 
					
						
							|  |  |  | 			Name:    fmt.Sprintf("team%d", i), | 
					
						
							|  |  |  | 			OrgID:   orgID, | 
					
						
							|  |  |  | 			Created: now, | 
					
						
							|  |  |  | 			Updated: now, | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 		signedInUser.Teams = append(signedInUser.Teams, teamID) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		for _, userID := range userIDs { | 
					
						
							|  |  |  | 			teamMembers = append(teamMembers, team.TeamMember{ | 
					
						
							|  |  |  | 				UserID:     userID, | 
					
						
							|  |  |  | 				TeamID:     teamID, | 
					
						
							|  |  |  | 				OrgID:      orgID, | 
					
						
							| 
									
										
										
										
											2023-11-22 21:20:22 +08:00
										 |  |  | 				Permission: dashboardaccess.PERMISSION_VIEW, | 
					
						
							| 
									
										
										
										
											2023-07-07 16:55:01 +08:00
										 |  |  | 				Created:    now, | 
					
						
							|  |  |  | 				Updated:    now, | 
					
						
							|  |  |  | 			}) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		name := fmt.Sprintf("managed_team_role_%d", i) | 
					
						
							|  |  |  | 		roles = append(roles, accesscontrol.Role{ | 
					
						
							|  |  |  | 			ID:      int64(i), | 
					
						
							|  |  |  | 			UID:     name, | 
					
						
							|  |  |  | 			OrgID:   orgID, | 
					
						
							|  |  |  | 			Name:    name, | 
					
						
							|  |  |  | 			Updated: now, | 
					
						
							|  |  |  | 			Created: now, | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		teamRoles = append(teamRoles, accesscontrol.TeamRole{ | 
					
						
							|  |  |  | 			RoleID:  int64(i), | 
					
						
							|  |  |  | 			OrgID:   orgID, | 
					
						
							|  |  |  | 			TeamID:  teamID, | 
					
						
							|  |  |  | 			Created: now, | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	err = db.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error { | 
					
						
							|  |  |  | 		_, err := sess.BulkInsert("team", teams, opts) | 
					
						
							|  |  |  | 		require.NoError(b, err) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		_, err = sess.BulkInsert("team_member", teamMembers, opts) | 
					
						
							|  |  |  | 		require.NoError(b, err) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		_, err = sess.BulkInsert("role", roles, opts) | 
					
						
							|  |  |  | 		require.NoError(b, err) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		_, err = sess.BulkInsert("team_role", teamRoles, opts) | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 	require.NoError(b, err) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	foldersCap := LEVEL0_FOLDER_NUM + LEVEL0_FOLDER_NUM*LEVEL1_FOLDER_NUM + LEVEL0_FOLDER_NUM*LEVEL1_FOLDER_NUM*LEVEL2_FOLDER_NUM | 
					
						
							|  |  |  | 	folders := make([]*f, 0, foldersCap) | 
					
						
							|  |  |  | 	dashsCap := LEVEL0_FOLDER_NUM * LEVEL1_FOLDER_NUM * LEVEL2_FOLDER_NUM * LEVEL2_DASHBOARD_NUM | 
					
						
							|  |  |  | 	dashs := make([]*dashboards.Dashboard, 0, foldersCap+dashsCap) | 
					
						
							|  |  |  | 	dashTags := make([]*dashboardTag, 0, dashsCap) | 
					
						
							|  |  |  | 	permissions := make([]accesscontrol.Permission, 0, foldersCap*2) | 
					
						
							|  |  |  | 	for i := 0; i < LEVEL0_FOLDER_NUM; i++ { | 
					
						
							|  |  |  | 		f0, d := addFolder(orgID, generateID(IDs), fmt.Sprintf("folder%d", i), nil) | 
					
						
							|  |  |  | 		folders = append(folders, f0) | 
					
						
							|  |  |  | 		dashs = append(dashs, d) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		roleID := int64(i%TEAM_NUM + 1) | 
					
						
							|  |  |  | 		permissions = append(permissions, accesscontrol.Permission{ | 
					
						
							|  |  |  | 			RoleID:  roleID, | 
					
						
							|  |  |  | 			Action:  dashboards.ActionFoldersRead, | 
					
						
							|  |  |  | 			Scope:   dashboards.ScopeFoldersProvider.GetResourceScopeUID(f0.UID), | 
					
						
							|  |  |  | 			Updated: now, | 
					
						
							|  |  |  | 			Created: now, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 			accesscontrol.Permission{ | 
					
						
							|  |  |  | 				RoleID:  roleID, | 
					
						
							|  |  |  | 				Action:  dashboards.ActionDashboardsRead, | 
					
						
							|  |  |  | 				Scope:   dashboards.ScopeFoldersProvider.GetResourceScopeUID(f0.UID), | 
					
						
							|  |  |  | 				Updated: now, | 
					
						
							|  |  |  | 				Created: now, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		) | 
					
						
							| 
									
										
										
										
											2023-10-10 18:31:55 +08:00
										 |  |  | 		signedInUser.Permissions[orgID][dashboards.ActionFoldersRead] = append(signedInUser.Permissions[orgID][dashboards.ActionFoldersRead], dashboards.ScopeFoldersProvider.GetResourceScopeUID(f0.UID)) | 
					
						
							|  |  |  | 		signedInUser.Permissions[orgID][dashboards.ActionDashboardsRead] = append(signedInUser.Permissions[orgID][dashboards.ActionDashboardsRead], dashboards.ScopeFoldersProvider.GetResourceScopeUID(f0.UID)) | 
					
						
							| 
									
										
										
										
											2023-07-07 16:55:01 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		for j := 0; j < LEVEL0_DASHBOARD_NUM; j++ { | 
					
						
							|  |  |  | 			str := fmt.Sprintf("dashboard_%d_%d", i, j) | 
					
						
							|  |  |  | 			dashID := generateID(IDs) | 
					
						
							|  |  |  | 			dashs = append(dashs, &dashboards.Dashboard{ | 
					
						
							| 
									
										
										
										
											2023-12-20 22:12:05 +08:00
										 |  |  | 				ID:        dashID, | 
					
						
							|  |  |  | 				OrgID:     signedInUser.OrgID, | 
					
						
							|  |  |  | 				IsFolder:  false, | 
					
						
							|  |  |  | 				UID:       str, | 
					
						
							| 
									
										
										
										
											2024-02-16 16:40:39 +08:00
										 |  |  | 				FolderID:  f0.ID, | 
					
						
							| 
									
										
										
										
											2023-12-20 22:12:05 +08:00
										 |  |  | 				FolderUID: f0.UID, | 
					
						
							|  |  |  | 				Slug:      str, | 
					
						
							|  |  |  | 				Title:     str, | 
					
						
							|  |  |  | 				Data:      simplejson.New(), | 
					
						
							|  |  |  | 				Created:   now, | 
					
						
							|  |  |  | 				Updated:   now, | 
					
						
							| 
									
										
										
										
											2023-07-07 16:55:01 +08:00
										 |  |  | 			}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			dashTags = append(dashTags, &dashboardTag{ | 
					
						
							| 
									
										
										
										
											2023-12-20 22:12:05 +08:00
										 |  |  | 				DashboardID: dashID, | 
					
						
							| 
									
										
										
										
											2023-07-07 16:55:01 +08:00
										 |  |  | 				Term:        fmt.Sprintf("tag%d", j), | 
					
						
							|  |  |  | 			}) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		for j := 0; j < LEVEL1_FOLDER_NUM; j++ { | 
					
						
							|  |  |  | 			f1, d1 := addFolder(orgID, generateID(IDs), fmt.Sprintf("folder%d_%d", i, j), &f0.UID) | 
					
						
							|  |  |  | 			folders = append(folders, f1) | 
					
						
							|  |  |  | 			dashs = append(dashs, d1) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			for k := 0; k < LEVEL1_DASHBOARD_NUM; k++ { | 
					
						
							|  |  |  | 				str := fmt.Sprintf("dashboard_%d_%d_%d", i, j, k) | 
					
						
							|  |  |  | 				dashID := generateID(IDs) | 
					
						
							|  |  |  | 				dashs = append(dashs, &dashboards.Dashboard{ | 
					
						
							| 
									
										
										
										
											2023-12-20 22:12:05 +08:00
										 |  |  | 					ID:        dashID, | 
					
						
							|  |  |  | 					OrgID:     signedInUser.OrgID, | 
					
						
							|  |  |  | 					IsFolder:  false, | 
					
						
							|  |  |  | 					UID:       str, | 
					
						
							| 
									
										
										
										
											2024-02-16 16:40:39 +08:00
										 |  |  | 					FolderID:  f1.ID, | 
					
						
							| 
									
										
										
										
											2023-12-20 22:12:05 +08:00
										 |  |  | 					FolderUID: f1.UID, | 
					
						
							|  |  |  | 					Slug:      str, | 
					
						
							|  |  |  | 					Title:     str, | 
					
						
							|  |  |  | 					Data:      simplejson.New(), | 
					
						
							|  |  |  | 					Created:   now, | 
					
						
							|  |  |  | 					Updated:   now, | 
					
						
							| 
									
										
										
										
											2023-07-07 16:55:01 +08:00
										 |  |  | 				}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				dashTags = append(dashTags, &dashboardTag{ | 
					
						
							| 
									
										
										
										
											2023-12-20 22:12:05 +08:00
										 |  |  | 					DashboardID: dashID, | 
					
						
							| 
									
										
										
										
											2023-07-07 16:55:01 +08:00
										 |  |  | 					Term:        fmt.Sprintf("tag%d", k), | 
					
						
							|  |  |  | 				}) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			for k := 0; k < LEVEL2_FOLDER_NUM; k++ { | 
					
						
							|  |  |  | 				f2, d2 := addFolder(orgID, generateID(IDs), fmt.Sprintf("folder%d_%d_%d", i, j, k), &f1.UID) | 
					
						
							|  |  |  | 				folders = append(folders, f2) | 
					
						
							|  |  |  | 				dashs = append(dashs, d2) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				for l := 0; l < LEVEL2_DASHBOARD_NUM; l++ { | 
					
						
							|  |  |  | 					str := fmt.Sprintf("dashboard_%d_%d_%d_%d", i, j, k, l) | 
					
						
							|  |  |  | 					dashID := generateID(IDs) | 
					
						
							|  |  |  | 					dashs = append(dashs, &dashboards.Dashboard{ | 
					
						
							| 
									
										
										
										
											2023-12-20 22:12:05 +08:00
										 |  |  | 						ID:        dashID, | 
					
						
							|  |  |  | 						OrgID:     signedInUser.OrgID, | 
					
						
							|  |  |  | 						IsFolder:  false, | 
					
						
							|  |  |  | 						UID:       str, | 
					
						
							| 
									
										
										
										
											2024-02-16 16:40:39 +08:00
										 |  |  | 						FolderID:  f1.ID, | 
					
						
							| 
									
										
										
										
											2023-12-20 22:12:05 +08:00
										 |  |  | 						FolderUID: f2.UID, | 
					
						
							|  |  |  | 						Slug:      str, | 
					
						
							|  |  |  | 						Title:     str, | 
					
						
							|  |  |  | 						Data:      simplejson.New(), | 
					
						
							|  |  |  | 						Created:   now, | 
					
						
							|  |  |  | 						Updated:   now, | 
					
						
							| 
									
										
										
										
											2023-07-07 16:55:01 +08:00
										 |  |  | 					}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					dashTags = append(dashTags, &dashboardTag{ | 
					
						
							| 
									
										
										
										
											2023-12-20 22:12:05 +08:00
										 |  |  | 						DashboardID: dashID, | 
					
						
							| 
									
										
										
										
											2023-07-07 16:55:01 +08:00
										 |  |  | 						Term:        fmt.Sprintf("tag%d", l), | 
					
						
							|  |  |  | 					}) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	err = db.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error { | 
					
						
							|  |  |  | 		_, err := sess.BulkInsert("folder", folders, opts) | 
					
						
							|  |  |  | 		require.NoError(b, err) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		_, err = sess.BulkInsert("dashboard", dashs, opts) | 
					
						
							|  |  |  | 		require.NoError(b, err) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		_, err = sess.BulkInsert("permission", permissions, opts) | 
					
						
							|  |  |  | 		require.NoError(b, err) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		_, err = sess.BulkInsert("dashboard_tag", dashTags, opts) | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 	require.NoError(b, err) | 
					
						
							|  |  |  | 	return benchScenario{ | 
					
						
							|  |  |  | 		db:           db, | 
					
						
							|  |  |  | 		cfg:          cfg, | 
					
						
							|  |  |  | 		signedInUser: &signedInUser, | 
					
						
							|  |  |  | 		teamSvc:      teamSvc, | 
					
						
							|  |  |  | 		userSvc:      userSvc, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-10 02:38:06 +08:00
										 |  |  | func setupServer(b testing.TB, sc benchScenario, features featuremgmt.FeatureToggles) *web.Macaron { | 
					
						
							| 
									
										
										
										
											2023-07-07 16:55:01 +08:00
										 |  |  | 	b.Helper() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	m := web.New() | 
					
						
							|  |  |  | 	initCtx := &contextmodel.ReqContext{} | 
					
						
							|  |  |  | 	m.Use(func(c *web.Context) { | 
					
						
							|  |  |  | 		initCtx.Context = c | 
					
						
							|  |  |  | 		initCtx.Logger = log.New("api-test") | 
					
						
							|  |  |  | 		initCtx.SignedInUser = sc.signedInUser | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		c.Req = c.Req.WithContext(ctxkey.Set(c.Req.Context(), initCtx)) | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	license := licensingtest.NewFakeLicensing() | 
					
						
							|  |  |  | 	license.On("FeatureEnabled", "accesscontrol.enforcement").Return(true).Maybe() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	quotaSrv := quotatest.New(false, nil) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-04 21:04:47 +08:00
										 |  |  | 	dashStore, err := database.ProvideDashboardStore(sc.db, sc.cfg, features, tagimpl.ProvideService(sc.db), quotaSrv) | 
					
						
							| 
									
										
										
										
											2023-07-07 16:55:01 +08:00
										 |  |  | 	require.NoError(b, err) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	folderStore := folderimpl.ProvideDashboardFolderStore(sc.db) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-10 18:56:52 +08:00
										 |  |  | 	ac := acimpl.ProvideAccessControl(featuremgmt.WithFeatures()) | 
					
						
							| 
									
										
										
										
											2024-05-02 19:18:54 +08:00
										 |  |  | 	folderServiceWithFlagOn := folderimpl.ProvideService(ac, bus.ProvideBus(tracing.InitializeTracerForTest()), dashStore, folderStore, sc.db, features, supportbundlestest.NewFakeBundleService(), nil) | 
					
						
							| 
									
										
										
										
											2023-07-07 16:55:01 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-23 19:36:22 +08:00
										 |  |  | 	cfg := setting.NewCfg() | 
					
						
							| 
									
										
										
										
											2024-05-23 19:14:01 +08:00
										 |  |  | 	actionSets := resourcepermissions.NewActionSetService() | 
					
						
							| 
									
										
										
										
											2024-07-02 20:45:25 +08:00
										 |  |  | 	acSvc := acimpl.ProvideOSSService( | 
					
						
							|  |  |  | 		sc.cfg, acdb.ProvideService(sc.db), actionSets, localcache.ProvideService(), | 
					
						
							|  |  |  | 		features, tracing.InitializeTracerForTest(), zanzana.NewNoopClient(), sc.db, | 
					
						
							|  |  |  | 	) | 
					
						
							| 
									
										
										
										
											2024-05-21 22:09:26 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-07 16:55:01 +08:00
										 |  |  | 	folderPermissions, err := ossaccesscontrol.ProvideFolderPermissions( | 
					
						
							| 
									
										
										
										
											2024-05-09 17:18:03 +08:00
										 |  |  | 		cfg, features, routing.NewRouteRegister(), sc.db, ac, license, &dashboards.FakeDashboardStore{}, folderServiceWithFlagOn, acSvc, sc.teamSvc, sc.userSvc, actionSets) | 
					
						
							| 
									
										
										
										
											2023-07-07 16:55:01 +08:00
										 |  |  | 	require.NoError(b, err) | 
					
						
							|  |  |  | 	dashboardPermissions, err := ossaccesscontrol.ProvideDashboardPermissions( | 
					
						
							| 
									
										
										
										
											2024-05-09 17:18:03 +08:00
										 |  |  | 		cfg, features, routing.NewRouteRegister(), sc.db, ac, license, &dashboards.FakeDashboardStore{}, folderServiceWithFlagOn, acSvc, sc.teamSvc, sc.userSvc, actionSets) | 
					
						
							| 
									
										
										
										
											2023-07-07 16:55:01 +08:00
										 |  |  | 	require.NoError(b, err) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	dashboardSvc, err := dashboardservice.ProvideDashboardServiceImpl( | 
					
						
							| 
									
										
										
										
											2024-03-14 22:36:35 +08:00
										 |  |  | 		sc.cfg, dashStore, folderStore, | 
					
						
							| 
									
										
										
										
											2023-07-07 16:55:01 +08:00
										 |  |  | 		features, folderPermissions, dashboardPermissions, ac, | 
					
						
							| 
									
										
										
										
											2023-12-05 23:13:31 +08:00
										 |  |  | 		folderServiceWithFlagOn, nil, | 
					
						
							| 
									
										
										
										
											2023-07-07 16:55:01 +08:00
										 |  |  | 	) | 
					
						
							|  |  |  | 	require.NoError(b, err) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	starSvc := startest.NewStarServiceFake() | 
					
						
							|  |  |  | 	starSvc.ExpectedUserStars = &star.GetUserStarsResult{UserStars: make(map[int64]bool)} | 
					
						
							| 
									
										
										
										
											2023-10-10 18:31:55 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-07 16:55:01 +08:00
										 |  |  | 	hs := &HTTPServer{ | 
					
						
							| 
									
										
										
										
											2023-10-10 18:31:55 +08:00
										 |  |  | 		CacheService:     localcache.New(5*time.Minute, 10*time.Minute), | 
					
						
							|  |  |  | 		Cfg:              sc.cfg, | 
					
						
							|  |  |  | 		SQLStore:         sc.db, | 
					
						
							|  |  |  | 		Features:         features, | 
					
						
							|  |  |  | 		QuotaService:     quotaSrv, | 
					
						
							|  |  |  | 		SearchService:    search.ProvideService(sc.cfg, sc.db, starSvc, dashboardSvc), | 
					
						
							|  |  |  | 		folderService:    folderServiceWithFlagOn, | 
					
						
							|  |  |  | 		DashboardService: dashboardSvc, | 
					
						
							| 
									
										
										
										
											2023-07-07 16:55:01 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-10 18:56:52 +08:00
										 |  |  | 	hs.AccessControl = acimpl.ProvideAccessControl(featuremgmt.WithFeatures()) | 
					
						
							| 
									
										
										
										
											2023-10-10 18:31:55 +08:00
										 |  |  | 	guardian.InitAccessControlGuardian(hs.Cfg, hs.AccessControl, hs.DashboardService) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-07 16:55:01 +08:00
										 |  |  | 	m.Get("/api/folders", hs.GetFolders) | 
					
						
							|  |  |  | 	m.Get("/api/search", hs.Search) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return m | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type f struct { | 
					
						
							| 
									
										
										
										
											2024-02-16 16:40:39 +08:00
										 |  |  | 	ID          int64   `xorm:"pk autoincr 'id'"` | 
					
						
							| 
									
										
										
										
											2023-07-07 16:55:01 +08:00
										 |  |  | 	OrgID       int64   `xorm:"org_id"` | 
					
						
							|  |  |  | 	UID         string  `xorm:"uid"` | 
					
						
							|  |  |  | 	ParentUID   *string `xorm:"parent_uid"` | 
					
						
							|  |  |  | 	Title       string | 
					
						
							|  |  |  | 	Description string | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	Created time.Time | 
					
						
							|  |  |  | 	Updated time.Time | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (f *f) TableName() string { | 
					
						
							|  |  |  | 	return "folder" | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // SQL bean helper to save tags
 | 
					
						
							|  |  |  | type dashboardTag struct { | 
					
						
							| 
									
										
										
										
											2024-02-16 16:40:39 +08:00
										 |  |  | 	ID          int64 `xorm:"pk autoincr 'id'"` | 
					
						
							|  |  |  | 	DashboardID int64 `xorm:"dashboard_id"` | 
					
						
							| 
									
										
										
										
											2023-07-07 16:55:01 +08:00
										 |  |  | 	Term        string | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func addFolder(orgID int64, id int64, uid string, parentUID *string) (*f, *dashboards.Dashboard) { | 
					
						
							|  |  |  | 	now := time.Now() | 
					
						
							|  |  |  | 	title := uid | 
					
						
							|  |  |  | 	f := &f{ | 
					
						
							| 
									
										
										
										
											2024-02-16 16:40:39 +08:00
										 |  |  | 		OrgID:     orgID, | 
					
						
							|  |  |  | 		UID:       uid, | 
					
						
							|  |  |  | 		Title:     title, | 
					
						
							|  |  |  | 		ID:        id, | 
					
						
							| 
									
										
										
										
											2023-07-07 16:55:01 +08:00
										 |  |  | 		Created:   now, | 
					
						
							|  |  |  | 		Updated:   now, | 
					
						
							|  |  |  | 		ParentUID: parentUID, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	d := &dashboards.Dashboard{ | 
					
						
							|  |  |  | 		ID:       id, | 
					
						
							|  |  |  | 		OrgID:    orgID, | 
					
						
							|  |  |  | 		UID:      uid, | 
					
						
							|  |  |  | 		Version:  1, | 
					
						
							|  |  |  | 		Title:    title, | 
					
						
							| 
									
										
										
										
											2023-08-30 23:46:47 +08:00
										 |  |  | 		Data:     simplejson.NewFromAny(map[string]any{"schemaVersion": 17, "title": title, "uid": uid, "version": 1}), | 
					
						
							| 
									
										
										
										
											2023-07-07 16:55:01 +08:00
										 |  |  | 		IsFolder: true, | 
					
						
							|  |  |  | 		Created:  now, | 
					
						
							|  |  |  | 		Updated:  now, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return f, d | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func generateID(reserved map[int64]struct{}) int64 { | 
					
						
							|  |  |  | 	n := rand.Int63n(MAXIMUM_INT_POSTGRES) | 
					
						
							|  |  |  | 	if _, existing := reserved[n]; existing { | 
					
						
							|  |  |  | 		return generateID(reserved) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	reserved[n] = struct{}{} | 
					
						
							|  |  |  | 	return n | 
					
						
							|  |  |  | } |