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() && }
}
diff --git a/web/src/features/dashboard/View.tsx b/web/src/features/dashboard/View.tsx
index 7a0c97d..bb5bc1d 100644
--- a/web/src/features/dashboard/View.tsx
+++ b/web/src/features/dashboard/View.tsx
@@ -58,7 +58,7 @@ const View: React.FC = () => {
case RowPanelType:
return ;
default:
- return ;
+ return ;
}
};
diff --git a/web/src/stores/dashboard.store.ts b/web/src/stores/dashboard.store.ts
index 4e026ce..ba00f77 100644
--- a/web/src/stores/dashboard.store.ts
+++ b/web/src/stores/dashboard.store.ts
@@ -25,7 +25,7 @@ import {
VisualizationAddPanelType,
} from '@src/constants';
import { DashboardSrv } from '@src/services';
-import { Dashboard, GridPos, PanelSetting, Variable, Tracker, Chart } from '@src/types';
+import { Dashboard, GridPos, PanelSetting, Variable, Tracker, Chart, DashboardMeta } from '@src/types';
import { ApiKit, ObjectKit } from '@src/utils';
import {
set,
@@ -49,6 +49,7 @@ class DashboardStore {
private panelSeq = 0;
private dashboardTracker: Tracker = new Tracker({});
public dashboard: Dashboard = {};
+ public meta: DashboardMeta = {} as any;
constructor() {
makeAutoObservable(this);
@@ -276,6 +277,10 @@ class DashboardStore {
});
}
+ canEdit(): boolean {
+ return get(this.meta, 'canEdit', false);
+ }
+
async loadDashbaord(dashboardId: string | null) {
try {
// reset panel seq when load dashboard
@@ -286,6 +291,10 @@ class DashboardStore {
this.dashboard = {
title: ChartPendingAddStore.dashboadTitle || 'New Dashboard',
};
+ this.meta = {
+ canEdit: true,
+ provisioned: false,
+ };
if (isEmpty(charts)) {
const panelId = this.assignPanelId();
panels.push({
@@ -315,6 +324,7 @@ class DashboardStore {
try {
const dashboard = await DashboardSrv.getDashboard(`${dashboardId}`);
this.initDashboard(dashboard.dashboard);
+ this.meta = dashboard.meta;
} catch (err) {
console.warn('load dashobard error', err);
Notification.error(ApiKit.getErrorMsg(err));
diff --git a/web/src/types/dashboard.ts b/web/src/types/dashboard.ts
index 354b54a..636d487 100644
--- a/web/src/types/dashboard.ts
+++ b/web/src/types/dashboard.ts
@@ -19,6 +19,7 @@ import { PanelSetting, Query } from '@src/types';
export interface DashboardDetail {
dashboard: Dashboard;
+ meta: DashboardMeta;
}
export interface Variable {
@@ -45,6 +46,11 @@ export interface Dashboard {
templating?: Record;
}
+export interface DashboardMeta {
+ canEdit: boolean;
+ provisioned: boolean;
+}
+
export interface SearchDashboard {
title?: string;
ownership?: string;