Nested folders: Fix move (#65526)

* Nested Folders: Fix move response

Fix JSON tags to be consistent with the GET endpoint

* Add API test for moving to root folder

* Allow move to root folder

* Fix failure message

* Update OpenAPI specification
This commit is contained in:
Sofia Papagiannaki 2023-03-30 11:46:11 +03:00 committed by GitHub
parent fc16fb0407
commit c65cd5833a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 506 additions and 50 deletions

View File

@ -201,25 +201,37 @@ func (hs *HTTPServer) setDefaultFolderPermissions(ctx context.Context, orgID int
return nil
}
// swagger:route POST /folders/{folder_uid}/move folders moveFolder
//
// Move folder.
//
// Responses:
// 200: folderResponse
// 401: unauthorisedError
// 403: forbiddenError
// 404: notFoundError
// 500: internalServerError
func (hs *HTTPServer) MoveFolder(c *contextmodel.ReqContext) response.Response {
if hs.Features.IsEnabled(featuremgmt.FlagNestedFolders) {
cmd := folder.MoveFolderCommand{}
if err := web.Bind(c.Req, &cmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
var theFolder *folder.Folder
var err error
if cmd.NewParentUID != "" {
cmd.OrgID = c.OrgID
cmd.UID = web.Params(c.Req)[":uid"]
cmd.SignedInUser = c.SignedInUser
theFolder, err = hs.folderService.Move(c.Req.Context(), &cmd)
if err != nil {
return response.Error(http.StatusInternalServerError, "update folder uid failed", err)
}
cmd.OrgID = c.OrgID
cmd.UID = web.Params(c.Req)[":uid"]
cmd.SignedInUser = c.SignedInUser
theFolder, err := hs.folderService.Move(c.Req.Context(), &cmd)
if err != nil {
return response.Error(http.StatusInternalServerError, "move folder failed", err)
}
return response.JSON(http.StatusOK, theFolder)
g, err := guardian.NewByUID(c.Req.Context(), cmd.UID, c.OrgID, c.SignedInUser)
if err != nil {
return response.Err(err)
}
return response.JSON(http.StatusOK, hs.newToFolderDto(c, g, theFolder))
}
result := map[string]string{}
result["message"] = "To use this service, you need to activate nested folder feature."
@ -408,6 +420,16 @@ type CreateFolderParams struct {
Body folder.CreateFolderCommand `json:"body"`
}
// swagger:parameters moveFolder
type MoveFolderParams struct {
// in:path
// required:true
FolderUID string `json:"folder_uid"`
// in:body
// required:true
Body folder.MoveFolderCommand `json:"body"`
}
// swagger:parameters deleteFolder
type DeleteFolderParams struct {
// in:path

View File

@ -359,7 +359,9 @@ func TestHTTPServer_FolderMetadata(t *testing.T) {
}
func TestFolderMoveAPIEndpoint(t *testing.T) {
folderService := &foldertest.FakeService{}
folderService := &foldertest.FakeService{
ExpectedFolder: &folder.Folder{},
}
setUpRBACGuardian(t)
type testCase struct {
@ -378,6 +380,14 @@ func TestFolderMoveAPIEndpoint(t *testing.T) {
{Action: dashboards.ActionFoldersWrite, Scope: dashboards.ScopeFoldersProvider.GetResourceScopeUID("newParentUid")},
},
},
{
description: "can move folder to the root folder with specific permissions",
newParentUid: "",
expectedCode: http.StatusOK,
permissions: []accesscontrol.Permission{
{Action: dashboards.ActionFoldersWrite, Scope: dashboards.ScopeFoldersProvider.GetResourceScopeUID("uid")},
},
},
{
description: "forbidden to move folder to another folder without the write access on the folder being moved",
newParentUid: "newParentUid",

View File

@ -182,6 +182,9 @@ func (ss *sqlStore) Get(ctx context.Context, q folder.GetFolderQuery) (*folder.F
}
func (ss *sqlStore) GetParents(ctx context.Context, q folder.GetParentsQuery) ([]*folder.Folder, error) {
if q.UID == "" {
return []*folder.Folder{}, nil
}
var folders []*folder.Folder
recQuery := `

View File

@ -390,9 +390,10 @@ func TestIntegrationGetParents(t *testing.T) {
require.NoError(t, err)
})
t.Run("get parents of unknown folder should return an error", func(t *testing.T) {
_, err := folderStore.GetParents(context.Background(), folder.GetParentsQuery{})
require.ErrorIs(t, err, folder.ErrFolderNotFound)
t.Run("get parents of root folder should should be empty", func(t *testing.T) {
parents, err := folderStore.GetParents(context.Background(), folder.GetParentsQuery{})
require.NoError(t, err)
require.Empty(t, parents)
})
t.Run("get parents of 1-st level folder should be empty", func(t *testing.T) {

View File

@ -2534,6 +2534,13 @@
"description": "Whether to initiate a download of the file or not.",
"name": "download",
"in": "query"
},
{
"type": "string",
"default": "yaml",
"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"
}
],
"responses": {
@ -2670,6 +2677,13 @@
"description": "Whether to initiate a download of the file or not.",
"name": "download",
"in": "query"
},
{
"type": "string",
"default": "yaml",
"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"
}
],
"responses": {
@ -2919,6 +2933,13 @@
"description": "Whether to initiate a download of the file or not.",
"name": "download",
"in": "query"
},
{
"type": "string",
"default": "yaml",
"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"
}
],
"responses": {
@ -5534,7 +5555,6 @@
}
},
"put": {
"description": "If nested folders are enabled then it optionally expects a new parent folder UID that moves the folder and\nincludes it into the response.",
"tags": [
"folders"
],
@ -5625,6 +5645,48 @@
}
}
},
"/folders/{folder_uid}/move": {
"post": {
"tags": [
"folders"
],
"summary": "Move folder.",
"operationId": "moveFolder",
"parameters": [
{
"type": "string",
"name": "folder_uid",
"in": "path",
"required": true
},
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/MoveFolderCommand"
}
}
],
"responses": {
"200": {
"$ref": "#/responses/folderResponse"
},
"401": {
"$ref": "#/responses/unauthorisedError"
},
"403": {
"$ref": "#/responses/forbiddenError"
},
"404": {
"$ref": "#/responses/notFoundError"
},
"500": {
"$ref": "#/responses/internalServerError"
}
}
}
},
"/folders/{folder_uid}/permissions": {
"get": {
"tags": [
@ -11519,6 +11581,10 @@
"internal",
"external"
]
},
"numExternalAlertmanagers": {
"type": "integer",
"format": "int64"
}
}
},
@ -11561,6 +11627,10 @@
"type": "integer",
"format": "int64"
},
"lastUsedAt": {
"type": "string",
"format": "date-time"
},
"name": {
"type": "string"
},
@ -11857,6 +11927,9 @@
"$ref": "#/definitions/EmbeddedContactPoint"
}
},
"CookieType": {
"type": "string"
},
"Correlation": {
"description": "Correlation is the model for correlations definitions",
"type": "object",
@ -11912,6 +11985,9 @@
"expr": "job=app"
}
},
"transformations": {
"$ref": "#/definitions/Transformations"
},
"type": {
"$ref": "#/definitions/CorrelationConfigType"
}
@ -11936,6 +12012,23 @@
"expr": "job=app"
}
},
"transformations": {
"description": "Source data transformations",
"type": "array",
"items": {
"$ref": "#/definitions/Transformation"
},
"example": [
{
"type": "logfmt"
},
{
"expression": "(Superman|Batman)",
"type": "regex",
"variable": "name"
}
]
},
"type": {
"$ref": "#/definitions/CorrelationConfigType"
}
@ -13117,6 +13210,40 @@
}
}
},
"EnumFieldConfig": {
"description": "Enum field config\nVector values are used as lookup keys into the enum fields",
"type": "object",
"properties": {
"color": {
"description": "Color is the color value for a given index (empty is undefined)",
"type": "array",
"items": {
"type": "string"
}
},
"description": {
"description": "Description of the enum state",
"type": "array",
"items": {
"type": "string"
}
},
"icon": {
"description": "Icon supports setting an icon for a given index value",
"type": "array",
"items": {
"type": "string"
}
},
"text": {
"description": "Value is the string display value for a given index",
"type": "array",
"items": {
"type": "string"
}
}
}
},
"ErrorResponseBody": {
"type": "object",
"required": [
@ -13323,6 +13450,9 @@
"thresholds": {
"$ref": "#/definitions/ThresholdsConfig"
},
"type": {
"$ref": "#/definitions/FieldTypeConfig"
},
"unit": {
"description": "Numeric Options",
"type": "string"
@ -13333,6 +13463,15 @@
}
}
},
"FieldTypeConfig": {
"description": "FieldTypeConfig has type specific configs, only one should be active at a time",
"type": "object",
"properties": {
"enum": {
"$ref": "#/definitions/EnumFieldConfig"
}
}
},
"FindTagsResult": {
"type": "object",
"title": "FindTagsResult is the result of a tags search.",
@ -13430,7 +13569,7 @@
"title": "Frame is a columnar data structure where each column is a Field.",
"properties": {
"Fields": {
"description": "Fields are the columns of a frame.\nAll Fields must be of the same the length when marshalling the Frame for transmission.",
"description": "Fields are the columns of a frame.\nAll Fields must be of the same the length when marshalling the Frame for transmission.\nThere should be no `nil` entries in the Fields slice (making them pointers was a mistake).",
"type": "array",
"items": {
"$ref": "#/definitions/Field"
@ -13502,6 +13641,9 @@
},
"type": {
"$ref": "#/definitions/FrameType"
},
"typeVersion": {
"$ref": "#/definitions/FrameTypeVersion"
}
}
},
@ -13509,8 +13651,16 @@
"description": "A FrameType string, when present in a frame's metadata, asserts that the\nframe's structure conforms to the FrameType's specification.\nThis property is currently optional, so FrameType may be FrameTypeUnknown even if the properties of\nthe Frame correspond to a defined FrameType.",
"type": "string"
},
"FrameTypeVersion": {
"type": "array",
"title": "FrameType is a 2 number version (Major / Minor).",
"items": {
"type": "integer",
"format": "uint64"
}
},
"Frames": {
"description": "It is the main data container within a backend.DataResponse.",
"description": "It is the main data container within a backend.DataResponse.\nThere should be no `nil` entries in the Frames slice (making them pointers was a mistake).",
"type": "array",
"title": "Frames is a slice of Frame pointers.",
"items": {
@ -14408,7 +14558,7 @@
"type": "string",
"format": "date-time"
},
"DashboardId": {
"DashboardID": {
"type": "integer",
"format": "int64"
},
@ -14429,7 +14579,7 @@
"type": "integer",
"format": "int64"
},
"Id": {
"ID": {
"type": "integer",
"format": "int64"
},
@ -14443,11 +14593,11 @@
"type": "string",
"format": "date-time"
},
"OrgId": {
"OrgID": {
"type": "integer",
"format": "int64"
},
"PanelId": {
"PanelID": {
"type": "integer",
"format": "int64"
},
@ -14779,6 +14929,15 @@
}
}
},
"MoveFolderCommand": {
"description": "MoveFolderCommand captures the information required by the folder service\nto move a folder.",
"type": "object",
"properties": {
"parentUid": {
"type": "string"
}
}
},
"MultiStatus": {
"type": "object"
},
@ -15098,6 +15257,9 @@
"isDisabled": {
"type": "boolean"
},
"isExternallySynced": {
"type": "boolean"
},
"lastSeenAt": {
"type": "string",
"format": "date-time"
@ -15292,6 +15454,12 @@
"PatchPrefsCmd": {
"type": "object",
"properties": {
"cookies": {
"type": "array",
"items": {
"$ref": "#/definitions/CookieType"
}
},
"homeDashboardId": {
"description": "The numerical :id of a favorited dashboard",
"type": "integer",
@ -15828,7 +15996,7 @@
"$ref": "#/definitions/QueryHistoryPreference"
},
"theme": {
"description": "light, dark, empty is default",
"description": "Theme light, dark, empty is default",
"type": "string"
},
"timezone": {
@ -15836,7 +16004,7 @@
"type": "string"
},
"weekStart": {
"description": "day of the week (sunday, monday, etc)",
"description": "WeekStart day of the week (sunday, monday, etc)",
"type": "string"
}
}
@ -16243,6 +16411,9 @@
"thresholds": {
"$ref": "#/definitions/ThresholdsConfig"
},
"type": {
"$ref": "#/definitions/FieldTypeConfig"
},
"unit": {
"description": "Numeric Options",
"type": "string"
@ -16982,7 +17153,7 @@
"basicRole": {
"type": "string"
},
"orgID": {
"orgId": {
"type": "integer",
"format": "int64"
},
@ -16992,11 +17163,11 @@
"scope": {
"type": "string"
},
"teamID": {
"teamId": {
"type": "integer",
"format": "int64"
},
"userID": {
"userId": {
"type": "integer",
"format": "int64"
},
@ -18036,6 +18207,33 @@
"type": "integer",
"format": "int64"
},
"Transformation": {
"type": "object",
"properties": {
"expression": {
"type": "string"
},
"field": {
"type": "string"
},
"mapValue": {
"type": "string"
},
"type": {
"type": "string",
"enum": [
"regex",
"logfmt"
]
}
}
},
"Transformations": {
"type": "array",
"items": {
"$ref": "#/definitions/Transformation"
}
},
"TrimDashboardCommand": {
"type": "object",
"properties": {
@ -18062,8 +18260,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 RawPath, an optional field which only gets\nset if the default encoding is different from Path.\n\nURL's String method uses the EscapedPath method to obtain the path. See the\nEscapedPath method for more details.",
"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"
@ -18391,6 +18590,12 @@
"UpdatePrefsCmd": {
"type": "object",
"properties": {
"cookies": {
"type": "array",
"items": {
"$ref": "#/definitions/CookieType"
}
},
"homeDashboardId": {
"description": "The numerical :id of a favorited dashboard",
"type": "integer",
@ -18604,6 +18809,9 @@
"isExternal": {
"type": "boolean"
},
"isExternallySynced": {
"type": "boolean"
},
"isGrafanaAdmin": {
"type": "boolean"
},
@ -18892,7 +19100,6 @@
}
},
"alertGroup": {
"description": "AlertGroup alert group",
"type": "object",
"required": [
"alerts",
@ -18916,6 +19123,7 @@
}
},
"alertGroups": {
"description": "AlertGroups alert groups",
"type": "array",
"items": {
"$ref": "#/definitions/alertGroup"
@ -19020,7 +19228,6 @@
}
},
"gettableAlert": {
"description": "GettableAlert gettable alert",
"type": "object",
"required": [
"labels",
@ -19131,14 +19338,12 @@
}
},
"gettableSilences": {
"description": "GettableSilences gettable silences",
"type": "array",
"items": {
"$ref": "#/definitions/gettableSilence"
}
},
"integration": {
"description": "Integration integration",
"type": "object",
"required": [
"name",
@ -19319,7 +19524,6 @@
}
},
"receiver": {
"description": "Receiver receiver",
"type": "object",
"required": [
"active",

View File

@ -2705,6 +2705,10 @@
"external"
],
"type": "string"
},
"numExternalAlertmanagers": {
"format": "int64",
"type": "integer"
}
},
"type": "object"
@ -2747,6 +2751,10 @@
"format": "int64",
"type": "integer"
},
"lastUsedAt": {
"format": "date-time",
"type": "string"
},
"name": {
"type": "string"
},
@ -3044,6 +3052,9 @@
},
"type": "array"
},
"CookieType": {
"type": "string"
},
"Correlation": {
"description": "Correlation is the model for correlations definitions",
"properties": {
@ -3093,6 +3104,9 @@
},
"type": "object"
},
"transformations": {
"$ref": "#/components/schemas/Transformations"
},
"type": {
"$ref": "#/components/schemas/CorrelationConfigType"
}
@ -3122,6 +3136,23 @@
},
"type": "object"
},
"transformations": {
"description": "Source data transformations",
"example": [
{
"type": "logfmt"
},
{
"expression": "(Superman|Batman)",
"type": "regex",
"variable": "name"
}
],
"items": {
"$ref": "#/components/schemas/Transformation"
},
"type": "array"
},
"type": {
"$ref": "#/components/schemas/CorrelationConfigType"
}
@ -4304,6 +4335,40 @@
],
"type": "object"
},
"EnumFieldConfig": {
"description": "Enum field config\nVector values are used as lookup keys into the enum fields",
"properties": {
"color": {
"description": "Color is the color value for a given index (empty is undefined)",
"items": {
"type": "string"
},
"type": "array"
},
"description": {
"description": "Description of the enum state",
"items": {
"type": "string"
},
"type": "array"
},
"icon": {
"description": "Icon supports setting an icon for a given index value",
"items": {
"type": "string"
},
"type": "array"
},
"text": {
"description": "Value is the string display value for a given index",
"items": {
"type": "string"
},
"type": "array"
}
},
"type": "object"
},
"ErrorResponseBody": {
"properties": {
"error": {
@ -4508,6 +4573,9 @@
"thresholds": {
"$ref": "#/components/schemas/ThresholdsConfig"
},
"type": {
"$ref": "#/components/schemas/FieldTypeConfig"
},
"unit": {
"description": "Numeric Options",
"type": "string"
@ -4520,6 +4588,15 @@
"title": "FieldConfig represents the display properties for a Field.",
"type": "object"
},
"FieldTypeConfig": {
"description": "FieldTypeConfig has type specific configs, only one should be active at a time",
"properties": {
"enum": {
"$ref": "#/components/schemas/EnumFieldConfig"
}
},
"type": "object"
},
"FindTagsResult": {
"properties": {
"tags": {
@ -4615,7 +4692,7 @@
"description": "Each Field is well typed by its FieldType and supports optional Labels.\n\nA Frame is a general data container for Grafana. A Frame can be table data\nor time series data depending on its content and field types.",
"properties": {
"Fields": {
"description": "Fields are the columns of a frame.\nAll Fields must be of the same the length when marshalling the Frame for transmission.",
"description": "Fields are the columns of a frame.\nAll Fields must be of the same the length when marshalling the Frame for transmission.\nThere should be no `nil` entries in the Fields slice (making them pointers was a mistake).",
"items": {
"$ref": "#/components/schemas/Field"
},
@ -4687,6 +4764,9 @@
},
"type": {
"$ref": "#/components/schemas/FrameType"
},
"typeVersion": {
"$ref": "#/components/schemas/FrameTypeVersion"
}
},
"title": "FrameMeta matches:",
@ -4696,8 +4776,16 @@
"description": "A FrameType string, when present in a frame's metadata, asserts that the\nframe's structure conforms to the FrameType's specification.\nThis property is currently optional, so FrameType may be FrameTypeUnknown even if the properties of\nthe Frame correspond to a defined FrameType.",
"type": "string"
},
"FrameTypeVersion": {
"items": {
"format": "uint64",
"type": "integer"
},
"title": "FrameType is a 2 number version (Major / Minor).",
"type": "array"
},
"Frames": {
"description": "It is the main data container within a backend.DataResponse.",
"description": "It is the main data container within a backend.DataResponse.\nThere should be no `nil` entries in the Frames slice (making them pointers was a mistake).",
"items": {
"$ref": "#/components/schemas/Frame"
},
@ -5594,7 +5682,7 @@
"format": "date-time",
"type": "string"
},
"DashboardId": {
"DashboardID": {
"format": "int64",
"type": "integer"
},
@ -5615,7 +5703,7 @@
"format": "int64",
"type": "integer"
},
"Id": {
"ID": {
"format": "int64",
"type": "integer"
},
@ -5629,11 +5717,11 @@
"format": "date-time",
"type": "string"
},
"OrgId": {
"OrgID": {
"format": "int64",
"type": "integer"
},
"PanelId": {
"PanelID": {
"format": "int64",
"type": "integer"
},
@ -5966,6 +6054,15 @@
},
"type": "object"
},
"MoveFolderCommand": {
"description": "MoveFolderCommand captures the information required by the folder service\nto move a folder.",
"properties": {
"parentUid": {
"type": "string"
}
},
"type": "object"
},
"MultiStatus": {
"type": "object"
},
@ -6283,6 +6380,9 @@
"isDisabled": {
"type": "boolean"
},
"isExternallySynced": {
"type": "boolean"
},
"lastSeenAt": {
"format": "date-time",
"type": "string"
@ -6477,6 +6577,12 @@
},
"PatchPrefsCmd": {
"properties": {
"cookies": {
"items": {
"$ref": "#/components/schemas/CookieType"
},
"type": "array"
},
"homeDashboardId": {
"default": 0,
"description": "The numerical :id of a favorited dashboard",
@ -7012,7 +7118,7 @@
"$ref": "#/components/schemas/QueryHistoryPreference"
},
"theme": {
"description": "light, dark, empty is default",
"description": "Theme light, dark, empty is default",
"type": "string"
},
"timezone": {
@ -7020,7 +7126,7 @@
"type": "string"
},
"weekStart": {
"description": "day of the week (sunday, monday, etc)",
"description": "WeekStart day of the week (sunday, monday, etc)",
"type": "string"
}
},
@ -7427,6 +7533,9 @@
"thresholds": {
"$ref": "#/components/schemas/ThresholdsConfig"
},
"type": {
"$ref": "#/components/schemas/FieldTypeConfig"
},
"unit": {
"description": "Numeric Options",
"type": "string"
@ -8167,7 +8276,7 @@
"basicRole": {
"type": "string"
},
"orgID": {
"orgId": {
"format": "int64",
"type": "integer"
},
@ -8177,11 +8286,11 @@
"scope": {
"type": "string"
},
"teamID": {
"teamId": {
"format": "int64",
"type": "integer"
},
"userID": {
"userId": {
"format": "int64",
"type": "integer"
},
@ -9221,6 +9330,33 @@
"format": "int64",
"type": "integer"
},
"Transformation": {
"properties": {
"expression": {
"type": "string"
},
"field": {
"type": "string"
},
"mapValue": {
"type": "string"
},
"type": {
"enum": [
"regex",
"logfmt"
],
"type": "string"
}
},
"type": "object"
},
"Transformations": {
"items": {
"$ref": "#/components/schemas/Transformation"
},
"type": "array"
},
"TrimDashboardCommand": {
"properties": {
"dashboard": {
@ -9247,6 +9383,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 RawPath, an optional field which only gets\nset if the default encoding is different from Path.\n\nURL's String method uses the EscapedPath method to obtain the path. See the\nEscapedPath method for more details.",
"properties": {
"ForceQuery": {
"type": "boolean"
@ -9282,7 +9419,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": {
@ -9575,6 +9712,12 @@
},
"UpdatePrefsCmd": {
"properties": {
"cookies": {
"items": {
"$ref": "#/components/schemas/CookieType"
},
"type": "array"
},
"homeDashboardId": {
"default": 0,
"description": "The numerical :id of a favorited dashboard",
@ -9788,6 +9931,9 @@
"isExternal": {
"type": "boolean"
},
"isExternallySynced": {
"type": "boolean"
},
"isGrafanaAdmin": {
"type": "boolean"
},
@ -10077,7 +10223,6 @@
"type": "object"
},
"alertGroup": {
"description": "AlertGroup alert group",
"properties": {
"alerts": {
"description": "alerts",
@ -10101,6 +10246,7 @@
"type": "object"
},
"alertGroups": {
"description": "AlertGroups alert groups",
"items": {
"$ref": "#/components/schemas/alertGroup"
},
@ -10205,7 +10351,6 @@
"type": "object"
},
"gettableAlert": {
"description": "GettableAlert gettable alert",
"properties": {
"annotations": {
"$ref": "#/components/schemas/labelSet"
@ -10261,6 +10406,7 @@
"type": "object"
},
"gettableAlerts": {
"description": "GettableAlerts gettable alerts",
"items": {
"$ref": "#/components/schemas/gettableAlert"
},
@ -10315,14 +10461,12 @@
"type": "object"
},
"gettableSilences": {
"description": "GettableSilences gettable silences",
"items": {
"$ref": "#/components/schemas/gettableSilence"
},
"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",
@ -10503,7 +10647,6 @@
"type": "object"
},
"receiver": {
"description": "Receiver receiver",
"properties": {
"active": {
"description": "active",
@ -13345,6 +13488,15 @@
"default": false,
"type": "boolean"
}
},
{
"description": "Format of the downloaded file, either yaml or json. Accept header can also be used, but the query parameter will take precedence.",
"in": "query",
"name": "format",
"schema": {
"default": "yaml",
"type": "string"
}
}
],
"responses": {
@ -13504,6 +13656,15 @@
"default": false,
"type": "boolean"
}
},
{
"description": "Format of the downloaded file, either yaml or json. Accept header can also be used, but the query parameter will take precedence.",
"in": "query",
"name": "format",
"schema": {
"default": "yaml",
"type": "string"
}
}
],
"responses": {
@ -13809,6 +13970,15 @@
"default": false,
"type": "boolean"
}
},
{
"description": "Format of the downloaded file, either yaml or json. Accept header can also be used, but the query parameter will take precedence.",
"in": "query",
"name": "format",
"schema": {
"default": "yaml",
"type": "string"
}
}
],
"responses": {
@ -16724,7 +16894,6 @@
]
},
"put": {
"description": "If nested folders are enabled then it optionally expects a new parent folder UID that moves the folder and\nincludes it into the response.",
"operationId": "updateFolder",
"parameters": [
{
@ -16777,6 +16946,53 @@
]
}
},
"/folders/{folder_uid}/move": {
"post": {
"operationId": "moveFolder",
"parameters": [
{
"in": "path",
"name": "folder_uid",
"required": true,
"schema": {
"type": "string"
}
}
],
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/MoveFolderCommand"
}
}
},
"required": true,
"x-originalParamName": "body"
},
"responses": {
"200": {
"$ref": "#/components/responses/folderResponse"
},
"401": {
"$ref": "#/components/responses/unauthorisedError"
},
"403": {
"$ref": "#/components/responses/forbiddenError"
},
"404": {
"$ref": "#/components/responses/notFoundError"
},
"500": {
"$ref": "#/components/responses/internalServerError"
}
},
"summary": "Move folder.",
"tags": [
"folders"
]
}
},
"/folders/{folder_uid}/permissions": {
"get": {
"operationId": "getFolderPermissionList",