mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Folders: Do not allow modifying the folder UID via the API (#74684)
* Folders: Do not allow changing the folder UID via the API * Update Swagger/OpenAPI docs * Update HTTP API docs
This commit is contained in:
parent
2fac3bd41e
commit
376f9a75db
@ -235,7 +235,6 @@ Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
|
||||
JSON Body schema:
|
||||
|
||||
- **uid** – Provide another [unique identifier](/http_api/folder/#identifier-id-vs-unique-identifier-uid) than stored to change the unique identifier. Starting with 10.0, this is **deprecated**. It will be removed in a future release. Please avoid using it because it can result in folder losing its permissions.
|
||||
- **title** – The title of the folder.
|
||||
- **version** – Provide the current version to be able to update the folder. Not needed if `overwrite=true`.
|
||||
- **overwrite** – Set to true if you want to overwrite existing folder with newer version.
|
||||
|
@ -373,18 +373,9 @@ func (s *Service) Update(ctx context.Context, cmd *folder.UpdateFolderCommand) (
|
||||
return dashFolder, nil
|
||||
}
|
||||
|
||||
if cmd.NewUID != nil && *cmd.NewUID != "" {
|
||||
if !util.IsValidShortUID(*cmd.NewUID) {
|
||||
return nil, dashboards.ErrDashboardInvalidUid
|
||||
} else if util.IsShortUIDTooLong(*cmd.NewUID) {
|
||||
return nil, dashboards.ErrDashboardUidTooLong
|
||||
}
|
||||
}
|
||||
|
||||
foldr, err := s.store.Update(ctx, folder.UpdateFolderCommand{
|
||||
UID: cmd.UID,
|
||||
OrgID: cmd.OrgID,
|
||||
NewUID: cmd.NewUID,
|
||||
NewTitle: cmd.NewTitle,
|
||||
NewDescription: cmd.NewDescription,
|
||||
SignedInUser: user,
|
||||
@ -471,10 +462,6 @@ func prepareForUpdate(dashFolder *dashboards.Dashboard, orgId int64, userId int6
|
||||
dashFolder.Title = strings.TrimSpace(title)
|
||||
dashFolder.Data.Set("title", dashFolder.Title)
|
||||
|
||||
if cmd.NewUID != nil && *cmd.NewUID != "" {
|
||||
dashFolder.SetUID(*cmd.NewUID)
|
||||
}
|
||||
|
||||
dashFolder.SetVersion(cmd.Version)
|
||||
dashFolder.IsFolder = true
|
||||
|
||||
|
@ -92,7 +92,7 @@ func (ss *sqlStore) Update(ctx context.Context, cmd folder.UpdateFolderCommand)
|
||||
|
||||
var foldr *folder.Folder
|
||||
|
||||
if cmd.NewDescription == nil && cmd.NewTitle == nil && cmd.NewUID == nil && cmd.NewParentUID == nil {
|
||||
if cmd.NewDescription == nil && cmd.NewTitle == nil && cmd.NewParentUID == nil {
|
||||
return nil, folder.ErrBadRequest.Errorf("nothing to update")
|
||||
}
|
||||
err := ss.db.WithDbSession(ctx, func(sess *db.Session) error {
|
||||
@ -110,12 +110,6 @@ func (ss *sqlStore) Update(ctx context.Context, cmd folder.UpdateFolderCommand)
|
||||
args = append(args, *cmd.NewTitle)
|
||||
}
|
||||
|
||||
if cmd.NewUID != nil {
|
||||
columnsToUpdate = append(columnsToUpdate, "uid = ?")
|
||||
uid = *cmd.NewUID
|
||||
args = append(args, *cmd.NewUID)
|
||||
}
|
||||
|
||||
if cmd.NewParentUID != nil {
|
||||
if *cmd.NewParentUID == "" {
|
||||
columnsToUpdate = append(columnsToUpdate, "parent_uid = NULL")
|
||||
|
@ -286,30 +286,6 @@ func TestIntegrationUpdate(t *testing.T) {
|
||||
f = updated
|
||||
})
|
||||
|
||||
t.Run("updating folder UID should succeed", func(t *testing.T) {
|
||||
newUID := "new"
|
||||
existingTitle := f.Title
|
||||
existingDesc := f.Description
|
||||
updated, err := folderStore.Update(context.Background(), folder.UpdateFolderCommand{
|
||||
UID: f.UID,
|
||||
OrgID: f.OrgID,
|
||||
NewUID: &newUID,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, newUID, updated.UID)
|
||||
|
||||
updated, err = folderStore.Get(context.Background(), folder.GetFolderQuery{
|
||||
UID: &updated.UID,
|
||||
OrgID: orgID,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, newUID, updated.UID)
|
||||
assert.Equal(t, existingTitle, updated.Title)
|
||||
assert.Equal(t, existingDesc, updated.Description)
|
||||
assert.NotEmpty(t, updated.URL)
|
||||
})
|
||||
|
||||
t.Run("updating folder parent UID", func(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
|
@ -90,10 +90,6 @@ type CreateFolderCommand struct {
|
||||
type UpdateFolderCommand struct {
|
||||
UID string `json:"-"`
|
||||
OrgID int64 `json:"-"`
|
||||
// NewUID it's an optional parameter used for overriding the existing folder UID
|
||||
// Starting with 10.0, this is deprecated. It will be removed in a future release.
|
||||
// Please avoid using it because it can result in folder loosing its permissions.
|
||||
NewUID *string `json:"uid"` // keep same json tag with the legacy command for not breaking the existing APIs
|
||||
// NewTitle it's an optional parameter used for overriding the existing folder title
|
||||
NewTitle *string `json:"title"` // keep same json tag with the legacy command for not breaking the existing APIs
|
||||
// NewDescription it's an optional parameter used for overriding the existing folder description
|
||||
|
@ -2541,6 +2541,18 @@
|
||||
"description": "Format of the downloaded file, either yaml or json. Accept header can also be used, but the query parameter will take precedence.",
|
||||
"name": "format",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "UID of folder from which export rules",
|
||||
"name": "folderUid",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Name of group of rules to export. Must be specified only together with folder UID",
|
||||
"name": "group",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
@ -8684,7 +8696,7 @@
|
||||
"saml",
|
||||
"enterprise"
|
||||
],
|
||||
"summary": "It performs assertion Consumer Service (ACS).",
|
||||
"summary": "It performs Assertion Consumer Service (ACS).",
|
||||
"operationId": "postACS",
|
||||
"parameters": [
|
||||
{
|
||||
@ -12178,6 +12190,12 @@
|
||||
"type": "string",
|
||||
"example": "My Label"
|
||||
},
|
||||
"orgId": {
|
||||
"description": "OrgID of the data source the correlation originates from",
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"example": 1
|
||||
},
|
||||
"sourceUID": {
|
||||
"description": "UID of the data source the correlation originates from",
|
||||
"type": "string",
|
||||
@ -12845,9 +12863,6 @@
|
||||
"provisionedExternalId": {
|
||||
"type": "string"
|
||||
},
|
||||
"publicDashboardAccessToken": {
|
||||
"type": "string"
|
||||
},
|
||||
"publicDashboardEnabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
@ -14082,6 +14097,12 @@
|
||||
"$ref": "#/definitions/SNSConfig"
|
||||
}
|
||||
},
|
||||
"teams_configs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/MSTeamsConfig"
|
||||
}
|
||||
},
|
||||
"telegram_configs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
@ -15162,6 +15183,26 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"MSTeamsConfig": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"http_config": {
|
||||
"$ref": "#/definitions/HTTPClientConfig"
|
||||
},
|
||||
"send_resolved": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"text": {
|
||||
"type": "string"
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
},
|
||||
"webhook_url": {
|
||||
"$ref": "#/definitions/SecretURL"
|
||||
}
|
||||
}
|
||||
},
|
||||
"MassDeleteAnnotationsCmd": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@ -15239,9 +15280,6 @@
|
||||
"type": "string",
|
||||
"example": "now-1h"
|
||||
},
|
||||
"publicDashboardAccessToken": {
|
||||
"type": "string"
|
||||
},
|
||||
"queries": {
|
||||
"description": "queries.refId – Specifies an identifier of the query. Is optional and default to “A”.\nqueries.datasourceId – Specifies the data source to be queried. Each query in the request must have an unique datasourceId.\nqueries.maxDataPoints - Species maximum amount of data points that dashboard panel can render. Is optional and default to 100.\nqueries.intervalMs - Specifies the time interval in milliseconds of time series. Is optional and defaults to 1000.",
|
||||
"type": "array",
|
||||
@ -16160,6 +16198,12 @@
|
||||
"$ref": "#/definitions/SNSConfig"
|
||||
}
|
||||
},
|
||||
"teams_configs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/MSTeamsConfig"
|
||||
}
|
||||
},
|
||||
"telegram_configs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
@ -16522,7 +16566,10 @@
|
||||
"example": "Always firing"
|
||||
},
|
||||
"uid": {
|
||||
"type": "string"
|
||||
"type": "string",
|
||||
"maxLength": 40,
|
||||
"minLength": 1,
|
||||
"pattern": "^[a-zA-Z0-9-_]+$"
|
||||
},
|
||||
"updated": {
|
||||
"type": "string",
|
||||
@ -16869,6 +16916,12 @@
|
||||
"$ref": "#/definitions/SNSConfig"
|
||||
}
|
||||
},
|
||||
"teams_configs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/MSTeamsConfig"
|
||||
}
|
||||
},
|
||||
"telegram_configs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
@ -18126,10 +18179,18 @@
|
||||
"type": "object",
|
||||
"title": "TLSConfig configures the options for TLS connections.",
|
||||
"properties": {
|
||||
"ca": {
|
||||
"description": "Text of the CA cert to use for the targets.",
|
||||
"type": "string"
|
||||
},
|
||||
"ca_file": {
|
||||
"description": "The CA cert to use for the targets.",
|
||||
"type": "string"
|
||||
},
|
||||
"cert": {
|
||||
"description": "Text of the client cert file for the targets.",
|
||||
"type": "string"
|
||||
},
|
||||
"cert_file": {
|
||||
"description": "The client cert file for the targets.",
|
||||
"type": "string"
|
||||
@ -18138,6 +18199,9 @@
|
||||
"description": "Disable target certificate validation.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"key": {
|
||||
"$ref": "#/definitions/Secret"
|
||||
},
|
||||
"key_file": {
|
||||
"description": "The client key file for the targets.",
|
||||
"type": "string"
|
||||
@ -18824,8 +18888,9 @@
|
||||
"type": "string"
|
||||
},
|
||||
"URL": {
|
||||
"description": "The general form represented is:\n\n[scheme:][//[userinfo@]host][/]path[?query][#fragment]\n\nURLs that do not start with a slash after the scheme are interpreted as:\n\nscheme:opaque[?query][#fragment]\n\nNote that the Path field is stored in decoded form: /%47%6f%2f becomes /Go/.\nA consequence is that it is impossible to tell which slashes in the Path were\nslashes in the raw URL and which were %2f. This distinction is rarely important,\nbut when it is, the code should use the EscapedPath method, which preserves\nthe original encoding of Path.\n\nThe RawPath field is an optional field which is only set when the default\nencoding of Path is different from the escaped path. See the EscapedPath method\nfor more details.\n\nURL's String method uses the EscapedPath method to obtain the path.",
|
||||
"type": "object",
|
||||
"title": "URL is a custom URL type that allows validation at configuration load time.",
|
||||
"title": "A URL represents a parsed URL (technically, a URI reference).",
|
||||
"properties": {
|
||||
"ForceQuery": {
|
||||
"type": "boolean"
|
||||
@ -19075,10 +19140,6 @@
|
||||
"description": "NewTitle it's an optional parameter used for overriding the existing folder title",
|
||||
"type": "string"
|
||||
},
|
||||
"uid": {
|
||||
"description": "NewUID it's an optional parameter used for overriding the existing folder UID\nStarting with 10.0, this is deprecated. It will be removed in a future release.\nPlease avoid using it because it can result in folder loosing its permissions.",
|
||||
"type": "string"
|
||||
},
|
||||
"version": {
|
||||
"description": "Version only used by the legacy folder implementation",
|
||||
"type": "integer",
|
||||
@ -19381,6 +19442,9 @@
|
||||
"isGrafanaAdmin": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"isGrafanaAdminExternallySynced": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"login": {
|
||||
"type": "string"
|
||||
},
|
||||
@ -19797,7 +19861,6 @@
|
||||
}
|
||||
},
|
||||
"gettableAlert": {
|
||||
"description": "GettableAlert gettable alert",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"labels",
|
||||
@ -19915,6 +19978,7 @@
|
||||
}
|
||||
},
|
||||
"integration": {
|
||||
"description": "Integration integration",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"name",
|
||||
@ -20058,6 +20122,7 @@
|
||||
}
|
||||
},
|
||||
"postableSilence": {
|
||||
"description": "PostableSilence postable silence",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"comment",
|
||||
|
@ -3191,6 +3191,12 @@
|
||||
"example": "My Label",
|
||||
"type": "string"
|
||||
},
|
||||
"orgId": {
|
||||
"description": "OrgID of the data source the correlation originates from",
|
||||
"example": 1,
|
||||
"format": "int64",
|
||||
"type": "integer"
|
||||
},
|
||||
"sourceUID": {
|
||||
"description": "UID of the data source the correlation originates from",
|
||||
"example": "d0oxYRg4z",
|
||||
@ -3858,9 +3864,6 @@
|
||||
"provisionedExternalId": {
|
||||
"type": "string"
|
||||
},
|
||||
"publicDashboardAccessToken": {
|
||||
"type": "string"
|
||||
},
|
||||
"publicDashboardEnabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
@ -5095,6 +5098,12 @@
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"teams_configs": {
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/MSTeamsConfig"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"telegram_configs": {
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/TelegramConfig"
|
||||
@ -6176,6 +6185,26 @@
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"MSTeamsConfig": {
|
||||
"properties": {
|
||||
"http_config": {
|
||||
"$ref": "#/components/schemas/HTTPClientConfig"
|
||||
},
|
||||
"send_resolved": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"text": {
|
||||
"type": "string"
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
},
|
||||
"webhook_url": {
|
||||
"$ref": "#/components/schemas/SecretURL"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"MassDeleteAnnotationsCmd": {
|
||||
"properties": {
|
||||
"annotationId": {
|
||||
@ -6247,9 +6276,6 @@
|
||||
"example": "now-1h",
|
||||
"type": "string"
|
||||
},
|
||||
"publicDashboardAccessToken": {
|
||||
"type": "string"
|
||||
},
|
||||
"queries": {
|
||||
"description": "queries.refId – Specifies an identifier of the query. Is optional and default to “A”.\nqueries.datasourceId – Specifies the data source to be queried. Each query in the request must have an unique datasourceId.\nqueries.maxDataPoints - Species maximum amount of data points that dashboard panel can render. Is optional and default to 100.\nqueries.intervalMs - Specifies the time interval in milliseconds of time series. Is optional and defaults to 1000.",
|
||||
"example": [
|
||||
@ -7172,6 +7198,12 @@
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"teams_configs": {
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/MSTeamsConfig"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"telegram_configs": {
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/TelegramConfig"
|
||||
@ -7523,6 +7555,9 @@
|
||||
"type": "string"
|
||||
},
|
||||
"uid": {
|
||||
"maxLength": 40,
|
||||
"minLength": 1,
|
||||
"pattern": "^[a-zA-Z0-9-_]+$",
|
||||
"type": "string"
|
||||
},
|
||||
"updated": {
|
||||
@ -7880,6 +7915,12 @@
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"teams_configs": {
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/MSTeamsConfig"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"telegram_configs": {
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/TelegramConfig"
|
||||
@ -9136,10 +9177,18 @@
|
||||
},
|
||||
"TLSConfig": {
|
||||
"properties": {
|
||||
"ca": {
|
||||
"description": "Text of the CA cert to use for the targets.",
|
||||
"type": "string"
|
||||
},
|
||||
"ca_file": {
|
||||
"description": "The CA cert to use for the targets.",
|
||||
"type": "string"
|
||||
},
|
||||
"cert": {
|
||||
"description": "Text of the client cert file for the targets.",
|
||||
"type": "string"
|
||||
},
|
||||
"cert_file": {
|
||||
"description": "The client cert file for the targets.",
|
||||
"type": "string"
|
||||
@ -9148,6 +9197,9 @@
|
||||
"description": "Disable target certificate validation.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"key": {
|
||||
"$ref": "#/components/schemas/Secret"
|
||||
},
|
||||
"key_file": {
|
||||
"description": "The client key file for the targets.",
|
||||
"type": "string"
|
||||
@ -9836,6 +9888,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"URL": {
|
||||
"description": "The general form represented is:\n\n[scheme:][//[userinfo@]host][/]path[?query][#fragment]\n\nURLs that do not start with a slash after the scheme are interpreted as:\n\nscheme:opaque[?query][#fragment]\n\nNote that the Path field is stored in decoded form: /%47%6f%2f becomes /Go/.\nA consequence is that it is impossible to tell which slashes in the Path were\nslashes in the raw URL and which were %2f. This distinction is rarely important,\nbut when it is, the code should use the EscapedPath method, which preserves\nthe original encoding of Path.\n\nThe RawPath field is an optional field which is only set when the default\nencoding of Path is different from the escaped path. See the EscapedPath method\nfor more details.\n\nURL's String method uses the EscapedPath method to obtain the path.",
|
||||
"properties": {
|
||||
"ForceQuery": {
|
||||
"type": "boolean"
|
||||
@ -9871,7 +9924,7 @@
|
||||
"$ref": "#/components/schemas/Userinfo"
|
||||
}
|
||||
},
|
||||
"title": "URL is a custom URL type that allows validation at configuration load time.",
|
||||
"title": "A URL represents a parsed URL (technically, a URI reference).",
|
||||
"type": "object"
|
||||
},
|
||||
"UpdateAlertNotificationCommand": {
|
||||
@ -10086,10 +10139,6 @@
|
||||
"description": "NewTitle it's an optional parameter used for overriding the existing folder title",
|
||||
"type": "string"
|
||||
},
|
||||
"uid": {
|
||||
"description": "NewUID it's an optional parameter used for overriding the existing folder UID\nStarting with 10.0, this is deprecated. It will be removed in a future release.\nPlease avoid using it because it can result in folder loosing its permissions.",
|
||||
"type": "string"
|
||||
},
|
||||
"version": {
|
||||
"description": "Version only used by the legacy folder implementation",
|
||||
"format": "int64",
|
||||
@ -10392,6 +10441,9 @@
|
||||
"isGrafanaAdmin": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"isGrafanaAdminExternallySynced": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"login": {
|
||||
"type": "string"
|
||||
},
|
||||
@ -10809,7 +10861,6 @@
|
||||
"type": "object"
|
||||
},
|
||||
"gettableAlert": {
|
||||
"description": "GettableAlert gettable alert",
|
||||
"properties": {
|
||||
"annotations": {
|
||||
"$ref": "#/components/schemas/labelSet"
|
||||
@ -10927,6 +10978,7 @@
|
||||
"type": "array"
|
||||
},
|
||||
"integration": {
|
||||
"description": "Integration integration",
|
||||
"properties": {
|
||||
"lastNotifyAttempt": {
|
||||
"description": "A timestamp indicating the last attempt to deliver a notification regardless of the outcome.\nFormat: date-time",
|
||||
@ -11070,6 +11122,7 @@
|
||||
"type": "array"
|
||||
},
|
||||
"postableSilence": {
|
||||
"description": "PostableSilence postable silence",
|
||||
"properties": {
|
||||
"comment": {
|
||||
"description": "comment",
|
||||
@ -13958,6 +14011,22 @@
|
||||
"default": "yaml",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "UID of folder from which export rules",
|
||||
"in": "query",
|
||||
"name": "folderUid",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "Name of group of rules to export. Must be specified only together with folder UID",
|
||||
"in": "query",
|
||||
"name": "group",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
@ -20689,7 +20758,7 @@
|
||||
"$ref": "#/components/responses/internalServerError"
|
||||
}
|
||||
},
|
||||
"summary": "It performs assertion Consumer Service (ACS).",
|
||||
"summary": "It performs Assertion Consumer Service (ACS).",
|
||||
"tags": [
|
||||
"saml",
|
||||
"enterprise"
|
||||
|
Loading…
Reference in New Issue
Block a user