mirror of
https://github.com/grafana/grafana.git
synced 2025-08-01 18:13:09 +08:00
Chore: Remove dashboard version from models (#50287)
* Remove dashbpard version from models * Fix lint * Fix api & sqlstore tests * Remove integration tags * Fix lint again * Add integration test to correct namespace * Lont fix 2 * Change Id to ID in dashVersionMeta
This commit is contained in:
@ -634,9 +634,9 @@ func (hs *HTTPServer) GetDashboardVersion(c *models.ReqContext) response.Respons
|
|||||||
creator = hs.getUserLogin(c.Req.Context(), res.CreatedBy)
|
creator = hs.getUserLogin(c.Req.Context(), res.CreatedBy)
|
||||||
}
|
}
|
||||||
|
|
||||||
dashVersionMeta := &models.DashboardVersionMeta{
|
dashVersionMeta := &dashver.DashboardVersionMeta{
|
||||||
Id: res.ID,
|
ID: res.ID,
|
||||||
DashboardId: res.DashboardID,
|
DashboardID: res.DashboardID,
|
||||||
DashboardUID: dashUID,
|
DashboardUID: dashUID,
|
||||||
Data: res.Data,
|
Data: res.Data,
|
||||||
ParentVersion: res.ParentVersion,
|
ParentVersion: res.ParentVersion,
|
||||||
@ -691,7 +691,7 @@ func (hs *HTTPServer) CalculateDashboardDiff(c *models.ReqContext) response.Resp
|
|||||||
|
|
||||||
baseVersionRes, err := hs.dashboardVersionService.Get(c.Req.Context(), &baseVersionQuery)
|
baseVersionRes, err := hs.dashboardVersionService.Get(c.Req.Context(), &baseVersionQuery)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, models.ErrDashboardVersionNotFound) {
|
if errors.Is(err, dashver.ErrDashboardVersionNotFound) {
|
||||||
return response.Error(404, "Dashboard version not found", err)
|
return response.Error(404, "Dashboard version not found", err)
|
||||||
}
|
}
|
||||||
return response.Error(500, "Unable to compute diff", err)
|
return response.Error(500, "Unable to compute diff", err)
|
||||||
@ -705,7 +705,7 @@ func (hs *HTTPServer) CalculateDashboardDiff(c *models.ReqContext) response.Resp
|
|||||||
|
|
||||||
newVersionRes, err := hs.dashboardVersionService.Get(c.Req.Context(), &newVersionQuery)
|
newVersionRes, err := hs.dashboardVersionService.Get(c.Req.Context(), &newVersionQuery)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, models.ErrDashboardVersionNotFound) {
|
if errors.Is(err, dashver.ErrDashboardVersionNotFound) {
|
||||||
return response.Error(404, "Dashboard version not found", err)
|
return response.Error(404, "Dashboard version not found", err)
|
||||||
}
|
}
|
||||||
return response.Error(500, "Unable to compute diff", err)
|
return response.Error(500, "Unable to compute diff", err)
|
||||||
@ -717,7 +717,7 @@ func (hs *HTTPServer) CalculateDashboardDiff(c *models.ReqContext) response.Resp
|
|||||||
result, err := dashdiffs.CalculateDiff(c.Req.Context(), &options, baseData, newData)
|
result, err := dashdiffs.CalculateDiff(c.Req.Context(), &options, baseData, newData)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, models.ErrDashboardVersionNotFound) {
|
if errors.Is(err, dashver.ErrDashboardVersionNotFound) {
|
||||||
return response.Error(404, "Dashboard version not found", err)
|
return response.Error(404, "Dashboard version not found", err)
|
||||||
}
|
}
|
||||||
return response.Error(500, "Unable to compute diff", err)
|
return response.Error(500, "Unable to compute diff", err)
|
||||||
|
@ -722,20 +722,6 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Given two dashboards being compared", func(t *testing.T) {
|
t.Run("Given two dashboards being compared", func(t *testing.T) {
|
||||||
dashboardvs := []*models.DashboardVersion{
|
|
||||||
{
|
|
||||||
DashboardId: 1,
|
|
||||||
Version: 1,
|
|
||||||
Data: simplejson.NewFromAny(map[string]interface{}{
|
|
||||||
"title": "Dash1",
|
|
||||||
})},
|
|
||||||
{
|
|
||||||
DashboardId: 2,
|
|
||||||
Version: 2,
|
|
||||||
Data: simplejson.NewFromAny(map[string]interface{}{
|
|
||||||
"title": "Dash2",
|
|
||||||
})},
|
|
||||||
}
|
|
||||||
fakeDashboardVersionService := dashvertest.NewDashboardVersionServiceFake()
|
fakeDashboardVersionService := dashvertest.NewDashboardVersionServiceFake()
|
||||||
fakeDashboardVersionService.ExpectedDashboardVersions = []*dashver.DashboardVersion{
|
fakeDashboardVersionService.ExpectedDashboardVersions = []*dashver.DashboardVersion{
|
||||||
{
|
{
|
||||||
@ -753,7 +739,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
|||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
sqlmock := mockstore.SQLStoreMock{ExpectedDashboardVersions: dashboardvs}
|
sqlmock := mockstore.SQLStoreMock{}
|
||||||
setUp := func() {
|
setUp := func() {
|
||||||
dashSvc := dashboards.NewFakeDashboardService(t)
|
dashSvc := dashboards.NewFakeDashboardService(t)
|
||||||
dashSvc.On("GetDashboardAclInfoList", mock.Anything, mock.AnythingOfType("*models.GetDashboardAclInfoListQuery")).Return(nil)
|
dashSvc.On("GetDashboardAclInfoList", mock.Anything, mock.AnythingOfType("*models.GetDashboardAclInfoListQuery")).Return(nil)
|
||||||
@ -861,12 +847,6 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
|||||||
Version: 1,
|
Version: 1,
|
||||||
}
|
}
|
||||||
mockSQLStore := mockstore.NewSQLStoreMock()
|
mockSQLStore := mockstore.NewSQLStoreMock()
|
||||||
mockSQLStore.ExpectedDashboardVersions = []*models.DashboardVersion{
|
|
||||||
{
|
|
||||||
DashboardId: 2,
|
|
||||||
Version: 1,
|
|
||||||
Data: fakeDash.Data,
|
|
||||||
}}
|
|
||||||
restoreDashboardVersionScenario(t, "When calling POST on", "/api/dashboards/id/1/restore",
|
restoreDashboardVersionScenario(t, "When calling POST on", "/api/dashboards/id/1/restore",
|
||||||
"/api/dashboards/id/:dashboardId/restore", dashboardService, fakeDashboardVersionService, cmd, func(sc *scenarioContext) {
|
"/api/dashboards/id/:dashboardId/restore", dashboardService, fakeDashboardVersionService, cmd, func(sc *scenarioContext) {
|
||||||
callRestoreDashboardVersion(sc)
|
callRestoreDashboardVersion(sc)
|
||||||
|
@ -2,7 +2,7 @@ package definitions
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/grafana/grafana/pkg/api/dtos"
|
"github.com/grafana/grafana/pkg/api/dtos"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
dashver "github.com/grafana/grafana/pkg/services/dashboardversion"
|
||||||
)
|
)
|
||||||
|
|
||||||
// swagger:route GET /dashboards/id/{DashboardID}/versions dashboard_versions getDashboardVersions
|
// swagger:route GET /dashboards/id/{DashboardID}/versions dashboard_versions getDashboardVersions
|
||||||
@ -122,11 +122,11 @@ type GetDashboardVersionsParams struct {
|
|||||||
// swagger:response dashboardVersionsResponse
|
// swagger:response dashboardVersionsResponse
|
||||||
type DashboardVersionsResponse struct {
|
type DashboardVersionsResponse struct {
|
||||||
// in: body
|
// in: body
|
||||||
Body []*models.DashboardVersionDTO `json:"body"`
|
Body []*dashver.DashboardVersionDTO `json:"body"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// swagger:response dashboardVersionResponse
|
// swagger:response dashboardVersionResponse
|
||||||
type DashboardVersionResponse struct {
|
type DashboardVersionResponse struct {
|
||||||
// in: body
|
// in: body
|
||||||
Body *models.DashboardVersionMeta `json:"body"`
|
Body *dashver.DashboardVersionMeta `json:"body"`
|
||||||
}
|
}
|
||||||
|
@ -1,89 +0,0 @@
|
|||||||
package models
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrDashboardVersionNotFound = errors.New("dashboard version not found")
|
|
||||||
ErrNoVersionsForDashboardId = errors.New("no dashboard versions found for the given DashboardId")
|
|
||||||
)
|
|
||||||
|
|
||||||
// A DashboardVersion represents the comparable data in a dashboard, allowing
|
|
||||||
// diffs of the dashboard to be performed.
|
|
||||||
type DashboardVersion struct {
|
|
||||||
Id int64 `json:"id"`
|
|
||||||
DashboardId int64 `json:"dashboardId"`
|
|
||||||
ParentVersion int `json:"parentVersion"`
|
|
||||||
RestoredFrom int `json:"restoredFrom"`
|
|
||||||
Version int `json:"version"`
|
|
||||||
|
|
||||||
Created time.Time `json:"created"`
|
|
||||||
CreatedBy int64 `json:"createdBy"`
|
|
||||||
|
|
||||||
Message string `json:"message"`
|
|
||||||
Data *simplejson.Json `json:"data"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// DashboardVersionMeta extends the dashboard version model with the names
|
|
||||||
// associated with the UserIds, overriding the field with the same name from
|
|
||||||
// the DashboardVersion model.
|
|
||||||
type DashboardVersionMeta struct {
|
|
||||||
Id int64 `json:"id"`
|
|
||||||
DashboardId int64 `json:"dashboardId"`
|
|
||||||
DashboardUID string `json:"uid"`
|
|
||||||
ParentVersion int `json:"parentVersion"`
|
|
||||||
RestoredFrom int `json:"restoredFrom"`
|
|
||||||
Version int `json:"version"`
|
|
||||||
Created time.Time `json:"created"`
|
|
||||||
Message string `json:"message"`
|
|
||||||
Data *simplejson.Json `json:"data"`
|
|
||||||
CreatedBy string `json:"createdBy"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// DashboardVersionDTO represents a dashboard version, without the dashboard
|
|
||||||
// map.
|
|
||||||
type DashboardVersionDTO struct {
|
|
||||||
Id int64 `json:"id"`
|
|
||||||
DashboardId int64 `json:"dashboardId"`
|
|
||||||
DashboardUID string `json:"dashboardUid"`
|
|
||||||
ParentVersion int `json:"parentVersion"`
|
|
||||||
RestoredFrom int `json:"restoredFrom"`
|
|
||||||
Version int `json:"version"`
|
|
||||||
Created time.Time `json:"created"`
|
|
||||||
CreatedBy string `json:"createdBy"`
|
|
||||||
Message string `json:"message"`
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Queries
|
|
||||||
//
|
|
||||||
|
|
||||||
type GetDashboardVersionQuery struct {
|
|
||||||
DashboardId int64
|
|
||||||
OrgId int64
|
|
||||||
Version int
|
|
||||||
|
|
||||||
Result *DashboardVersion
|
|
||||||
}
|
|
||||||
|
|
||||||
type GetDashboardVersionsQuery struct {
|
|
||||||
DashboardId int64
|
|
||||||
DashboardUID string
|
|
||||||
OrgId int64
|
|
||||||
Limit int
|
|
||||||
Start int
|
|
||||||
|
|
||||||
Result []*DashboardVersionDTO
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Commands
|
|
||||||
//
|
|
||||||
|
|
||||||
type DeleteExpiredVersionsCommand struct {
|
|
||||||
DeletedRows int64
|
|
||||||
}
|
|
@ -13,6 +13,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
|
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
|
dashver "github.com/grafana/grafana/pkg/services/dashboardversion"
|
||||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||||
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
||||||
"github.com/grafana/grafana/pkg/services/sqlstore/permissions"
|
"github.com/grafana/grafana/pkg/services/sqlstore/permissions"
|
||||||
@ -472,8 +473,8 @@ func saveDashboard(sess *sqlstore.DBSession, cmd *models.SaveDashboardCommand) e
|
|||||||
return models.ErrDashboardNotFound
|
return models.ErrDashboardNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
dashVersion := &models.DashboardVersion{
|
dashVersion := &dashver.DashboardVersion{
|
||||||
DashboardId: dash.Id,
|
DashboardID: dash.Id,
|
||||||
ParentVersion: parentVersion,
|
ParentVersion: parentVersion,
|
||||||
RestoredFrom: cmd.RestoredFrom,
|
RestoredFrom: cmd.RestoredFrom,
|
||||||
Version: dash.Version,
|
Version: dash.Version,
|
||||||
|
@ -54,7 +54,7 @@ func TestIntegrationGetDashboardVersion(t *testing.T) {
|
|||||||
|
|
||||||
_, err := dashVerStore.Get(context.Background(), &query)
|
_, err := dashVerStore.Get(context.Background(), &query)
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
assert.Equal(t, models.ErrDashboardVersionNotFound, err)
|
assert.Equal(t, dashver.ErrDashboardVersionNotFound, err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,8 +165,8 @@ func insertTestDashboard(t *testing.T, sqlStore *sqlstore.SQLStore, title string
|
|||||||
dash.Data.Set("uid", dash.Uid)
|
dash.Data.Set("uid", dash.Uid)
|
||||||
|
|
||||||
err = sqlStore.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error {
|
err = sqlStore.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error {
|
||||||
dashVersion := &models.DashboardVersion{
|
dashVersion := &dashver.DashboardVersion{
|
||||||
DashboardId: dash.Id,
|
DashboardID: dash.Id,
|
||||||
ParentVersion: dash.Version,
|
ParentVersion: dash.Version,
|
||||||
RestoredFrom: cmd.RestoredFrom,
|
RestoredFrom: cmd.RestoredFrom,
|
||||||
Version: dash.Version,
|
Version: dash.Version,
|
||||||
@ -227,8 +227,8 @@ func updateTestDashboard(t *testing.T, sqlStore *sqlstore.SQLStore, dashboard *m
|
|||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
|
||||||
err = sqlStore.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error {
|
err = sqlStore.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error {
|
||||||
dashVersion := &models.DashboardVersion{
|
dashVersion := &dashver.DashboardVersion{
|
||||||
DashboardId: dash.Id,
|
DashboardID: dash.Id,
|
||||||
ParentVersion: parentVersion,
|
ParentVersion: parentVersion,
|
||||||
RestoredFrom: cmd.RestoredFrom,
|
RestoredFrom: cmd.RestoredFrom,
|
||||||
Version: dash.Version,
|
Version: dash.Version,
|
||||||
|
@ -14,7 +14,7 @@ var (
|
|||||||
|
|
||||||
type DashboardVersion struct {
|
type DashboardVersion struct {
|
||||||
ID int64 `json:"id"`
|
ID int64 `json:"id"`
|
||||||
DashboardID int64 `json:"dashboardId"`
|
DashboardID int64 `json:"dashboardId" xorm:"dashboard_id"`
|
||||||
ParentVersion int `json:"parentVersion"`
|
ParentVersion int `json:"parentVersion"`
|
||||||
RestoredFrom int `json:"restoredFrom"`
|
RestoredFrom int `json:"restoredFrom"`
|
||||||
Version int `json:"version"`
|
Version int `json:"version"`
|
||||||
@ -55,3 +55,19 @@ type DashboardVersionDTO struct {
|
|||||||
CreatedBy string `json:"createdBy"`
|
CreatedBy string `json:"createdBy"`
|
||||||
Message string `json:"message"`
|
Message string `json:"message"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DashboardVersionMeta extends the dashboard version model with the names
|
||||||
|
// associated with the UserIds, overriding the field with the same name from
|
||||||
|
// the DashboardVersion model.
|
||||||
|
type DashboardVersionMeta struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
DashboardID int64 `json:"dashboardId"`
|
||||||
|
DashboardUID string `json:"uid"`
|
||||||
|
ParentVersion int `json:"parentVersion"`
|
||||||
|
RestoredFrom int `json:"restoredFrom"`
|
||||||
|
Version int `json:"version"`
|
||||||
|
Created time.Time `json:"created"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
Data *simplejson.Json `json:"data"`
|
||||||
|
CreatedBy string `json:"createdBy"`
|
||||||
|
}
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
|
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
|
dashver "github.com/grafana/grafana/pkg/services/dashboardversion"
|
||||||
"github.com/grafana/grafana/pkg/util"
|
"github.com/grafana/grafana/pkg/util"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
@ -295,8 +296,8 @@ func updateTestDashboard(t *testing.T, sqlStore *SQLStore, dashboard *models.Das
|
|||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
|
||||||
err = sqlStore.WithDbSession(context.Background(), func(sess *DBSession) error {
|
err = sqlStore.WithDbSession(context.Background(), func(sess *DBSession) error {
|
||||||
dashVersion := &models.DashboardVersion{
|
dashVersion := &dashver.DashboardVersion{
|
||||||
DashboardId: dash.Id,
|
DashboardID: dash.Id,
|
||||||
ParentVersion: parentVersion,
|
ParentVersion: parentVersion,
|
||||||
RestoredFrom: cmd.RestoredFrom,
|
RestoredFrom: cmd.RestoredFrom,
|
||||||
Version: dash.Version,
|
Version: dash.Version,
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"xorm.io/xorm"
|
"xorm.io/xorm"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
|
dashver "github.com/grafana/grafana/pkg/services/dashboardversion"
|
||||||
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
||||||
"github.com/grafana/grafana/pkg/util"
|
"github.com/grafana/grafana/pkg/util"
|
||||||
|
|
||||||
@ -118,8 +119,8 @@ func (m *folderHelper) createFolder(orgID int64, title string) (*dashboard, erro
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
dashVersion := &models.DashboardVersion{
|
dashVersion := &dashver.DashboardVersion{
|
||||||
DashboardId: dash.Id,
|
DashboardID: dash.Id,
|
||||||
ParentVersion: parentVersion,
|
ParentVersion: parentVersion,
|
||||||
RestoredFrom: cmd.RestoredFrom,
|
RestoredFrom: cmd.RestoredFrom,
|
||||||
Version: dash.Version,
|
Version: dash.Version,
|
||||||
|
@ -21,8 +21,6 @@ type SQLStoreMock struct {
|
|||||||
ExpectedAlert *models.Alert
|
ExpectedAlert *models.Alert
|
||||||
ExpectedPluginSetting *models.PluginSetting
|
ExpectedPluginSetting *models.PluginSetting
|
||||||
ExpectedDashboards []*models.Dashboard
|
ExpectedDashboards []*models.Dashboard
|
||||||
ExpectedDashboardVersion *models.DashboardVersion
|
|
||||||
ExpectedDashboardVersions []*models.DashboardVersion
|
|
||||||
ExpectedDashboardAclInfoList []*models.DashboardAclInfoDTO
|
ExpectedDashboardAclInfoList []*models.DashboardAclInfoDTO
|
||||||
ExpectedUserOrgList []*models.UserOrgDTO
|
ExpectedUserOrgList []*models.UserOrgDTO
|
||||||
ExpectedOrgListResponse OrgListResponse
|
ExpectedOrgListResponse OrgListResponse
|
||||||
|
@ -11,6 +11,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
|
dashver "github.com/grafana/grafana/pkg/services/dashboardversion"
|
||||||
"github.com/grafana/grafana/pkg/util"
|
"github.com/grafana/grafana/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -441,8 +442,8 @@ func insertTestDashboard(t *testing.T, sqlStore *SQLStore, title string, orgId i
|
|||||||
dash.Data.Set("uid", dash.Uid)
|
dash.Data.Set("uid", dash.Uid)
|
||||||
|
|
||||||
err = sqlStore.WithDbSession(context.Background(), func(sess *DBSession) error {
|
err = sqlStore.WithDbSession(context.Background(), func(sess *DBSession) error {
|
||||||
dashVersion := &models.DashboardVersion{
|
dashVersion := &dashver.DashboardVersion{
|
||||||
DashboardId: dash.Id,
|
DashboardID: dash.Id,
|
||||||
ParentVersion: dash.Version,
|
ParentVersion: dash.Version,
|
||||||
RestoredFrom: cmd.RestoredFrom,
|
RestoredFrom: cmd.RestoredFrom,
|
||||||
Version: dash.Version,
|
Version: dash.Version,
|
||||||
|
Reference in New Issue
Block a user