diff --git a/http/api/dashboard.go b/http/api/dashboard.go index 43170b2..fff9257 100644 --- a/http/api/dashboard.go +++ b/http/api/dashboard.go @@ -109,19 +109,33 @@ func (api *DashboardAPI) DeleteDashboardByUID(c *gin.Context) { // GetDashboardByUID gets dashboard by given uid. func (api *DashboardAPI) GetDashboardByUID(c *gin.Context) { uid := c.Param(constant.UID) - dashboard, err := api.deps.DashboardSrv.GetDashboardByUID(c.Request.Context(), uid) + ctx := c.Request.Context() + dashboard, err := api.deps.DashboardSrv.GetDashboardByUID(ctx, uid) if err != nil { httppkg.Error(c, err) return } var dashboardMap map[string]any - if err := jsonUnmarshalFn(dashboard.Config, &dashboardMap); err != nil { + if err = jsonUnmarshalFn(dashboard.Config, &dashboardMap); err != nil { httppkg.Error(c, err) return } dashboardMap[constant.UID] = uid + provisioningDashboard, err := api.deps.DashboardSrv.GetProvisioningDashboard(ctx, uid) + if err != nil { + httppkg.Error(c, err) + return + } + meta := model.NewDashboardMeta() + if provisioningDashboard != nil { + meta.Provisioned = true + if !provisioningDashboard.AllowUIUpdates { + meta.CanEdit = false + } + } httppkg.OK(c, gin.H{ "dashboard": dashboardMap, + "meta": meta, }) } diff --git a/http/api/dashboard_test.go b/http/api/dashboard_test.go index 95541d2..2110523 100644 --- a/http/api/dashboard_test.go +++ b/http/api/dashboard_test.go @@ -316,6 +316,20 @@ func TestDashboardAPI_GetDashboardByUID(t *testing.T) { assert.Equal(t, http.StatusInternalServerError, resp.Code) }, }, + { + name: "get provisioning dashboard failure", + prepare: func() { + var dashboard datatypes.JSON + _ = encoding.JSONUnmarshal([]byte("{}"), &dashboard) + dashboardSrv.EXPECT().GetDashboardByUID(gomock.Any(), "1234").Return(&model.Dashboard{ + Config: dashboard, + }, nil) + dashboardSrv.EXPECT().GetProvisioningDashboard(gomock.Any(), "1234").Return(nil, fmt.Errorf("err")) + }, + assert: func(resp *httptest.ResponseRecorder) { + assert.Equal(t, http.StatusInternalServerError, resp.Code) + }, + }, { name: "get dashboard successfully", prepare: func() { @@ -324,6 +338,7 @@ func TestDashboardAPI_GetDashboardByUID(t *testing.T) { dashboardSrv.EXPECT().GetDashboardByUID(gomock.Any(), "1234").Return(&model.Dashboard{ Config: dashboard, }, nil) + dashboardSrv.EXPECT().GetProvisioningDashboard(gomock.Any(), "1234").Return(&model.DashboardProvisioning{AllowUIUpdates: false}, nil) }, assert: func(resp *httptest.ResponseRecorder) { assert.Equal(t, http.StatusOK, resp.Code) diff --git a/model/dashboard.go b/model/dashboard.go index 4b5f19a..918479e 100644 --- a/model/dashboard.go +++ b/model/dashboard.go @@ -95,6 +95,20 @@ type Dashboard struct { IsStarred bool `json:"isStarred,omitempty" gorm:"-"` } +// DashboardMeta represents dashboard metadata. +type DashboardMeta struct { + CanEdit bool `json:"canEdit"` + Provisioned bool `json:"provisioned"` +} + +// NewDashboardMeta creates a dashboard metadata. +func NewDashboardMeta() DashboardMeta { + return DashboardMeta{ + CanEdit: true, + Provisioned: false, + } +} + // DashboardProvisioning represents dashboard provisioning information. type DashboardProvisioning struct { BaseModel diff --git a/model/dashboard_test.go b/model/dashboard_test.go index 3142031..13df8a0 100644 --- a/model/dashboard_test.go +++ b/model/dashboard_test.go @@ -141,3 +141,9 @@ func TestDashboard_GetCharts(t *testing.T) { }) } } + +func TestDashboard_NewDashboardMeta(t *testing.T) { + meta := NewDashboardMeta() + assert.True(t, meta.CanEdit) + assert.False(t, meta.Provisioned) +} diff --git a/service/dashboard.go b/service/dashboard.go index 3f3e7b7..9cc46b4 100644 --- a/service/dashboard.go +++ b/service/dashboard.go @@ -19,8 +19,11 @@ package service import ( "context" + "errors" "strings" + "gorm.io/gorm" + "github.com/lindb/linsight/constant" "github.com/lindb/linsight/model" dbpkg "github.com/lindb/linsight/pkg/db" @@ -53,6 +56,8 @@ type DashboardService interface { SaveProvisioningDashboard(ctx context.Context, req *model.SaveProvisioningDashboardRequest) error // RemoveProvisioningDashboard removes provision dashboard if not exist. RemoveProvisioningDashboard(ctx context.Context, req *model.RemoveProvisioningDashboardRequest) error + // GetProvisioningDashboard returns provisioning dashboard by given dashboard uid. + GetProvisioningDashboard(ctx context.Context, dashboardUID string) (*model.DashboardProvisioning, error) } // dashboardService implements DashboardService interface. @@ -250,6 +255,19 @@ func (srv *dashboardService) RemoveProvisioningDashboard(ctx context.Context, re }) } +// GetProvisioningDashboard returns provisioning dashboard by given dashboard uid. +func (srv *dashboardService) GetProvisioningDashboard(ctx context.Context, dashboardUID string) (*model.DashboardProvisioning, error) { + rs := &model.DashboardProvisioning{} + signedUser := util.GetUser(ctx) + if err := srv.db.Get(rs, "dashboard_uid=? and org_id=?", dashboardUID, signedUser.Org.ID); err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, nil + } + return nil, err + } + return rs, nil +} + // toggleStar toggles the dashboard star. func (srv *dashboardService) toggleStar(ctx context.Context, uid string, star bool) error { dashboard, err := srv.getDashboardByUID(ctx, uid) diff --git a/service/dashboard_test.go b/service/dashboard_test.go index 24cd564..97045ac 100644 --- a/service/dashboard_test.go +++ b/service/dashboard_test.go @@ -25,6 +25,7 @@ import ( gomock "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" "gorm.io/datatypes" + "gorm.io/gorm" "github.com/lindb/linsight/model" "github.com/lindb/linsight/pkg/db" @@ -511,3 +512,50 @@ func TestDashboardService_SaveProvisioningDashboard(t *testing.T) { }) } } + +func TestDashboardService_GetProvisioningDashboard(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockDB := db.NewMockDB(ctrl) + srv := NewDashboardService(nil, mockDB) + + cases := []struct { + name string + prepare func() + wantErr bool + }{ + { + name: "get dashboard failure", + prepare: func() { + mockDB.EXPECT().Get(gomock.Any(), "dashboard_uid=? and org_id=?", "1234", int64(12)).Return(fmt.Errorf("err")) + }, + wantErr: true, + }, + { + name: "not found", + prepare: func() { + mockDB.EXPECT().Get(gomock.Any(), "dashboard_uid=? and org_id=?", "1234", int64(12)).Return(gorm.ErrRecordNotFound) + }, + wantErr: false, + }, + { + name: "found dashboard", + prepare: func() { + mockDB.EXPECT().Get(gomock.Any(), "dashboard_uid=? and org_id=?", "1234", int64(12)).Return(nil) + }, + wantErr: false, + }, + } + + for _, tt := range cases { + tt := tt + t.Run(tt.name, func(t *testing.T) { + tt.prepare() + _, err := srv.GetProvisioningDashboard(ctx, "1234") + if tt.wantErr != (err != nil) { + t.Fatal(tt.name) + } + }) + } +} diff --git a/web/src/components/dashboard/Panel.tsx b/web/src/components/dashboard/Panel.tsx index 9a72f6b..70c5d87 100644 --- a/web/src/components/dashboard/Panel.tsx +++ b/web/src/components/dashboard/Panel.tsx @@ -161,8 +161,8 @@ const PanelHeader = forwardRef( }}> Explore - {renderChartRepo()} - + {!isStatic && renderChartRepo()} + {isStatic && } {!isStatic && ( } diff --git a/web/src/features/dashboard/Dashboard.tsx b/web/src/features/dashboard/Dashboard.tsx index 65e507b..0e443fd 100644 --- a/web/src/features/dashboard/Dashboard.tsx +++ b/web/src/features/dashboard/Dashboard.tsx @@ -364,7 +364,7 @@ const Dashboard: React.FC = () => { }} /> )} - + {DashboardStore.canEdit() && }