Skip to content

Commit

Permalink
Folders: Do not allow modifying the folder UID via the API (grafana#7…
Browse files Browse the repository at this point in the history
…4684)

* Folders: Do not allow changing the folder UID via the API

* Update Swagger/OpenAPI docs

* Update HTTP API docs
  • Loading branch information
papagian authored and chauchausoup committed Sep 15, 2023
1 parent 4df9e15 commit cc8da28
Show file tree
Hide file tree
Showing 7 changed files with 162 additions and 76 deletions.
1 change: 0 additions & 1 deletion docs/sources/developers/http_api/folder.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
13 changes: 0 additions & 13 deletions pkg/services/folder/folderimpl/folder.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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

Expand Down
8 changes: 1 addition & 7 deletions pkg/services/folder/folderimpl/sqlstore.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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")
Expand Down
24 changes: 0 additions & 24 deletions pkg/services/folder/folderimpl/sqlstore_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 0 additions & 4 deletions pkg/services/folder/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
93 changes: 79 additions & 14 deletions public/api-merged.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down Expand Up @@ -8684,7 +8696,7 @@
"saml",
"enterprise"
],
"summary": "It performs assertion Consumer Service (ACS).",
"summary": "It performs Assertion Consumer Service (ACS).",
"operationId": "postACS",
"parameters": [
{
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -12845,9 +12863,6 @@
"provisionedExternalId": {
"type": "string"
},
"publicDashboardAccessToken": {
"type": "string"
},
"publicDashboardEnabled": {
"type": "boolean"
},
Expand Down Expand Up @@ -14082,6 +14097,12 @@
"$ref": "#/definitions/SNSConfig"
}
},
"teams_configs": {
"type": "array",
"items": {
"$ref": "#/definitions/MSTeamsConfig"
}
},
"telegram_configs": {
"type": "array",
"items": {
Expand Down Expand Up @@ -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": {
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -16160,6 +16198,12 @@
"$ref": "#/definitions/SNSConfig"
}
},
"teams_configs": {
"type": "array",
"items": {
"$ref": "#/definitions/MSTeamsConfig"
}
},
"telegram_configs": {
"type": "array",
"items": {
Expand Down Expand Up @@ -16522,7 +16566,10 @@
"example": "Always firing"
},
"uid": {
"type": "string"
"type": "string",
"maxLength": 40,
"minLength": 1,
"pattern": "^[a-zA-Z0-9-_]+$"
},
"updated": {
"type": "string",
Expand Down Expand Up @@ -16869,6 +16916,12 @@
"$ref": "#/definitions/SNSConfig"
}
},
"teams_configs": {
"type": "array",
"items": {
"$ref": "#/definitions/MSTeamsConfig"
}
},
"telegram_configs": {
"type": "array",
"items": {
Expand Down Expand Up @@ -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"
Expand All @@ -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"
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -19381,6 +19442,9 @@
"isGrafanaAdmin": {
"type": "boolean"
},
"isGrafanaAdminExternallySynced": {
"type": "boolean"
},
"login": {
"type": "string"
},
Expand Down Expand Up @@ -19797,7 +19861,6 @@
}
},
"gettableAlert": {
"description": "GettableAlert gettable alert",
"type": "object",
"required": [
"labels",
Expand Down Expand Up @@ -19915,6 +19978,7 @@
}
},
"integration": {
"description": "Integration integration",
"type": "object",
"required": [
"name",
Expand Down Expand Up @@ -20058,6 +20122,7 @@
}
},
"postableSilence": {
"description": "PostableSilence postable silence",
"type": "object",
"required": [
"comment",
Expand Down
Loading

0 comments on commit cc8da28

Please sign in to comment.