From c65cd5833aff0aa4f666457554e6b1486940a313 Mon Sep 17 00:00:00 2001 From: Sofia Papagiannaki <1632407+papagian@users.noreply.github.com> Date: Thu, 30 Mar 2023 11:46:11 +0300 Subject: [PATCH] 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 --- pkg/api/folder.go | 42 ++- pkg/api/folder_test.go | 12 +- pkg/services/folder/folderimpl/sqlstore.go | 3 + .../folder/folderimpl/sqlstore_test.go | 7 +- public/api-merged.json | 240 +++++++++++++++-- public/openapi3.json | 252 ++++++++++++++++-- 6 files changed, 506 insertions(+), 50 deletions(-) diff --git a/pkg/api/folder.go b/pkg/api/folder.go index dcd3ac767c6..57e7881dc49 100644 --- a/pkg/api/folder.go +++ b/pkg/api/folder.go @@ -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 diff --git a/pkg/api/folder_test.go b/pkg/api/folder_test.go index 8cd8ca8054c..6640397e63e 100644 --- a/pkg/api/folder_test.go +++ b/pkg/api/folder_test.go @@ -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", diff --git a/pkg/services/folder/folderimpl/sqlstore.go b/pkg/services/folder/folderimpl/sqlstore.go index c05cb02280b..98f06cf650f 100644 --- a/pkg/services/folder/folderimpl/sqlstore.go +++ b/pkg/services/folder/folderimpl/sqlstore.go @@ -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 := ` diff --git a/pkg/services/folder/folderimpl/sqlstore_test.go b/pkg/services/folder/folderimpl/sqlstore_test.go index 3a6219d0b36..7899afd6a6a 100644 --- a/pkg/services/folder/folderimpl/sqlstore_test.go +++ b/pkg/services/folder/folderimpl/sqlstore_test.go @@ -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) { diff --git a/public/api-merged.json b/public/api-merged.json index 405e76fca9d..78f01f1a17c 100644 --- a/public/api-merged.json +++ b/public/api-merged.json @@ -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", diff --git a/public/openapi3.json b/public/openapi3.json index 3633e9cee72..2dd00ca9996 100644 --- a/public/openapi3.json +++ b/public/openapi3.json @@ -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",