From 7a256b2ebba7bb0d509dd113fcfc0cc080a39b31 Mon Sep 17 00:00:00 2001 From: Alex Auvolat Date: Sun, 14 Sep 2025 17:25:04 +0200 Subject: [PATCH] openapi: fix query parameters (fix #1160) --- doc/api/garage-admin-v2.json | 36 ++++++++++--------- src/api/admin/api.rs | 31 +++++++++++++--- src/api/admin/openapi.rs | 68 +++++++++--------------------------- 3 files changed, 62 insertions(+), 73 deletions(-) diff --git a/doc/api/garage-admin-v2.json b/doc/api/garage-admin-v2.json index 61c4440d..a439becb 100644 --- a/doc/api/garage-admin-v2.json +++ b/doc/api/garage-admin-v2.json @@ -31,7 +31,7 @@ "parameters": [ { "name": "domain", - "in": "path", + "in": "query", "description": "The domain name to check for", "required": true, "schema": { @@ -408,7 +408,7 @@ "parameters": [ { "name": "node", - "in": "path", + "in": "query", "description": "Node ID to query, or `*` for all nodes, or `self` for the node responding to the request", "required": true, "schema": { @@ -443,7 +443,7 @@ "parameters": [ { "name": "id", - "in": "path", + "in": "query", "description": "Admin API token ID", "required": true, "schema": { @@ -472,6 +472,7 @@ { "name": "id", "in": "query", + "description": "ID of the bucket to delete", "required": true, "schema": { "type": "string" @@ -504,7 +505,7 @@ "parameters": [ { "name": "id", - "in": "path", + "in": "query", "description": "Access key ID", "required": true, "schema": { @@ -610,7 +611,7 @@ "parameters": [ { "name": "node", - "in": "path", + "in": "query", "description": "Node ID to query, or `*` for all nodes, or `self` for the node responding to the request", "required": true, "schema": { @@ -902,7 +903,7 @@ "parameters": [ { "name": "node", - "in": "path", + "in": "query", "description": "Node ID to query, or `*` for all nodes, or `self` for the node responding to the request", "required": true, "schema": { @@ -937,7 +938,7 @@ "parameters": [ { "name": "node", - "in": "path", + "in": "query", "description": "Node ID to query, or `*` for all nodes, or `self` for the node responding to the request", "required": true, "schema": { @@ -972,7 +973,7 @@ "parameters": [ { "name": "node", - "in": "path", + "in": "query", "description": "Node ID to query, or `*` for all nodes, or `self` for the node responding to the request", "required": true, "schema": { @@ -1017,7 +1018,7 @@ "parameters": [ { "name": "node", - "in": "path", + "in": "query", "description": "Node ID to query, or `*` for all nodes, or `self` for the node responding to the request", "required": true, "schema": { @@ -1141,7 +1142,7 @@ "parameters": [ { "name": "node", - "in": "path", + "in": "query", "description": "Node ID to query, or `*` for all nodes, or `self` for the node responding to the request", "required": true, "schema": { @@ -1210,7 +1211,7 @@ "parameters": [ { "name": "node", - "in": "path", + "in": "query", "description": "Node ID to query, or `*` for all nodes, or `self` for the node responding to the request", "required": true, "schema": { @@ -1293,7 +1294,7 @@ "parameters": [ { "name": "node", - "in": "path", + "in": "query", "description": "Node ID to query, or `*` for all nodes, or `self` for the node responding to the request", "required": true, "schema": { @@ -1362,7 +1363,7 @@ "parameters": [ { "name": "node", - "in": "path", + "in": "query", "description": "Node ID to query, or `*` for all nodes, or `self` for the node responding to the request", "required": true, "schema": { @@ -1441,7 +1442,7 @@ "parameters": [ { "name": "node", - "in": "path", + "in": "query", "description": "Node ID to query, or `*` for all nodes, or `self` for the node responding to the request", "required": true, "schema": { @@ -1510,7 +1511,7 @@ "parameters": [ { "name": "node", - "in": "path", + "in": "query", "description": "Node ID to query, or `*` for all nodes, or `self` for the node responding to the request", "required": true, "schema": { @@ -1555,7 +1556,7 @@ "parameters": [ { "name": "id", - "in": "path", + "in": "query", "description": "Admin API token ID", "required": true, "schema": { @@ -1601,6 +1602,7 @@ { "name": "id", "in": "query", + "description": "ID of the bucket to update", "required": true, "schema": { "type": "string" @@ -1682,7 +1684,7 @@ "parameters": [ { "name": "id", - "in": "path", + "in": "query", "description": "Access key ID", "required": true, "schema": { diff --git a/src/api/admin/api.rs b/src/api/admin/api.rs index 89adbee9..50bc5784 100644 --- a/src/api/admin/api.rs +++ b/src/api/admin/api.rs @@ -145,6 +145,13 @@ pub struct MultiResponse { pub error: HashMap, } +#[derive(Debug, Clone, Serialize, Deserialize, IntoParams)] +#[into_params(parameter_in = Query)] +pub struct MultiRequestQueryParams { + /// Node ID to query, or `*` for all nodes, or `self` for the node responding to the request + pub node: String, +} + // ********************************************** // Special endpoints // @@ -155,8 +162,10 @@ pub struct MultiResponse { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct OptionsRequest; -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, IntoParams)] +#[into_params(parameter_in = Query)] pub struct CheckDomainRequest { + /// The domain name to check for pub domain: String, } @@ -355,9 +364,12 @@ pub struct CreateAdminTokenResponse { // ---- UpdateAdminToken ---- -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, IntoParams)] +#[into_params(parameter_in = Query)] pub struct UpdateAdminTokenRequest { + /// Admin API token ID pub id: String, + #[param(ignore = true)] pub body: UpdateAdminTokenRequestBody, } @@ -384,8 +396,10 @@ pub struct UpdateAdminTokenResponse(pub GetAdminTokenInfoResponse); // ---- DeleteAdminToken ---- -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, IntoParams)] +#[into_params(parameter_in = Query)] pub struct DeleteAdminTokenRequest { + /// Admin API token ID pub id: String, } @@ -736,9 +750,12 @@ pub struct ImportKeyResponse(pub GetKeyInfoResponse); // ---- UpdateKey ---- -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, IntoParams)] +#[into_params(parameter_in = Query)] pub struct UpdateKeyRequest { + /// Access key ID pub id: String, + #[param(ignore = true)] pub body: UpdateKeyRequestBody, } @@ -763,8 +780,10 @@ pub struct UpdateKeyRequestBody { // ---- DeleteKey ---- -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, IntoParams)] +#[into_params(parameter_in = Query)] pub struct DeleteKeyRequest { + /// Access key ID pub id: String, } @@ -894,6 +913,7 @@ pub struct CreateBucketLocalAlias { #[derive(Debug, Clone, Serialize, Deserialize, IntoParams)] #[into_params(parameter_in = Query)] pub struct UpdateBucketRequest { + /// ID of the bucket to update pub id: String, #[param(ignore = true)] pub body: UpdateBucketRequestBody, @@ -922,6 +942,7 @@ pub struct UpdateBucketWebsiteAccess { #[derive(Debug, Clone, Serialize, Deserialize, IntoParams)] #[into_params(parameter_in = Query)] pub struct DeleteBucketRequest { + /// ID of the bucket to delete pub id: String, } diff --git a/src/api/admin/openapi.rs b/src/api/admin/openapi.rs index d216f950..675d3dfd 100644 --- a/src/api/admin/openapi.rs +++ b/src/api/admin/openapi.rs @@ -46,9 +46,7 @@ a static website for the requested domain. This is used by reverse proxies such as Caddy or Tricot, to avoid requesting TLS certificates for domain names that do not correspond to an actual website. ", - params( - ("domain" = String, description = "The domain name to check for"), - ), + params(CheckDomainRequest), security(()), responses( (status = 200, description = "The domain name redirects to a static website bucket"), @@ -167,9 +165,7 @@ fn CreateAdminToken() -> () {} Updates information about the specified admin API token. ", request_body = UpdateAdminTokenRequestBody, - params( - ("id" = String, description = "Admin API token ID"), - ), + params(UpdateAdminTokenRequest), responses( (status = 200, description = "Admin token has been updated", body = UpdateAdminTokenResponse), (status = 500, description = "Internal server error") @@ -181,9 +177,7 @@ fn UpdateAdminToken() -> () {} path = "/v2/DeleteAdminToken", tag = "Admin API token", description = "Delete an admin API token from the cluster, revoking all its permissions.", - params( - ("id" = String, description = "Admin API token ID"), - ), + params(DeleteAdminTokenRequest), responses( (status = 200, description = "Admin token has been deleted"), (status = 500, description = "Internal server error") @@ -391,9 +385,7 @@ Updates information about the specified API access key. *Note: the secret key is not returned in the response, `null` is sent instead.* ", request_body = UpdateKeyRequestBody, - params( - ("id" = String, description = "Access key ID"), - ), + params(UpdateKeyRequest), responses( (status = 200, description = "Access key has been updated", body = UpdateKeyResponse), (status = 500, description = "Internal server error") @@ -405,9 +397,7 @@ fn UpdateKey() -> () {} path = "/v2/DeleteKey", tag = "Access key", description = "Delete a key from the cluster. Its access will be removed from all the buckets. Buckets are not automatically deleted and can be dangling. You should manually delete them before. ", - params( - ("id" = String, description = "Access key ID"), - ), + params(DeleteKeyRequest), responses( (status = 200, description = "Access key has been deleted"), (status = 500, description = "Internal server error") @@ -626,9 +616,7 @@ fn RemoveBucketAlias() -> () {} description = " Return information about the Garage daemon running on one or several nodes. ", - params( - ("node" = String, description = "Node ID to query, or `*` for all nodes, or `self` for the node responding to the request"), - ), + params(MultiRequestQueryParams), responses( (status = 200, description = "Responses from individual cluster nodes", body = MultiResponse), (status = 500, description = "Internal server error") @@ -644,9 +632,7 @@ Fetch statistics for one or several Garage nodes. *Note: do not try to parse the `freeform` field of the response, it is given as a string specifically because its format is not stable.* ", - params( - ("node" = String, description = "Node ID to query, or `*` for all nodes, or `self` for the node responding to the request"), - ), + params(MultiRequestQueryParams), responses( (status = 200, description = "Responses from individual cluster nodes", body = MultiResponse), (status = 500, description = "Internal server error") @@ -660,9 +646,7 @@ fn GetNodeStatistics() -> () {} description = " Instruct one or several nodes to take a snapshot of their metadata databases. ", - params( - ("node" = String, description = "Node ID to query, or `*` for all nodes, or `self` for the node responding to the request"), - ), + params(MultiRequestQueryParams), responses( (status = 200, description = "Responses from individual cluster nodes", body = MultiResponse), (status = 500, description = "Internal server error") @@ -676,9 +660,7 @@ fn CreateMetadataSnapshot() -> () {} description = " Launch a repair operation on one or several cluster nodes. ", - params( - ("node" = String, description = "Node ID to query, or `*` for all nodes, or `self` for the node responding to the request"), - ), + params(MultiRequestQueryParams), request_body = LocalLaunchRepairOperationRequest, responses( (status = 200, description = "Responses from individual cluster nodes", body = MultiResponse), @@ -697,9 +679,7 @@ fn LaunchRepairOperation() -> () {} description = " List background workers currently running on one or several cluster nodes. ", - params( - ("node" = String, description = "Node ID to query, or `*` for all nodes, or `self` for the node responding to the request"), - ), + params(MultiRequestQueryParams), request_body = LocalListWorkersRequest, responses( (status = 200, description = "Responses from individual cluster nodes", body = MultiResponse), @@ -714,9 +694,7 @@ fn ListWorkers() -> () {} description = " Get information about the specified background worker on one or several cluster nodes. ", - params( - ("node" = String, description = "Node ID to query, or `*` for all nodes, or `self` for the node responding to the request"), - ), + params(MultiRequestQueryParams), request_body = LocalGetWorkerInfoRequest, responses( (status = 200, description = "Responses from individual cluster nodes", body = MultiResponse), @@ -731,9 +709,7 @@ fn GetWorkerInfo() -> () {} description = " Fetch values of one or several worker variables, from one or several cluster nodes. ", - params( - ("node" = String, description = "Node ID to query, or `*` for all nodes, or `self` for the node responding to the request"), - ), + params(MultiRequestQueryParams), request_body = LocalGetWorkerVariableRequest, responses( (status = 200, description = "Responses from individual cluster nodes", body = MultiResponse), @@ -748,9 +724,7 @@ fn GetWorkerVariable() -> () {} description = " Set the value for a worker variable, on one or several cluster nodes. ", - params( - ("node" = String, description = "Node ID to query, or `*` for all nodes, or `self` for the node responding to the request"), - ), + params(MultiRequestQueryParams), request_body = LocalSetWorkerVariableRequest, responses( (status = 200, description = "Responses from individual cluster nodes", body = MultiResponse), @@ -769,9 +743,7 @@ fn SetWorkerVariable() -> () {} description = " List data blocks that are currently in an errored state on one or several Garage nodes. ", - params( - ("node" = String, description = "Node ID to query, or `*` for all nodes, or `self` for the node responding to the request"), - ), + params(MultiRequestQueryParams), responses( (status = 200, description = "Responses from individual cluster nodes", body = MultiResponse), (status = 500, description = "Internal server error") @@ -785,9 +757,7 @@ fn ListBlockErrors() -> () {} description = " Get detailed information about a data block stored on a Garage node, including all object versions and in-progress multipart uploads that contain a reference to this block. ", - params( - ("node" = String, description = "Node ID to query, or `*` for all nodes, or `self` for the node responding to the request"), - ), + params(MultiRequestQueryParams), request_body = LocalGetBlockInfoRequest, responses( (status = 200, description = "Detailed block information", body = MultiResponse), @@ -802,9 +772,7 @@ fn GetBlockInfo() -> () {} description = " Instruct Garage node(s) to retry the resynchronization of one or several missing data block(s). ", - params( - ("node" = String, description = "Node ID to query, or `*` for all nodes, or `self` for the node responding to the request"), - ), + params(MultiRequestQueryParams), request_body = LocalRetryBlockResyncRequest, responses( (status = 200, description = "Responses from individual cluster nodes", body = MultiResponse), @@ -821,9 +789,7 @@ Purge references to one or several missing data blocks. This will remove all objects and in-progress multipart uploads that contain the specified data block(s). The objects will be permanently deleted from the buckets in which they appear. Use with caution. ", - params( - ("node" = String, description = "Node ID to query, or `*` for all nodes, or `self` for the node responding to the request"), - ), + params(MultiRequestQueryParams), request_body = LocalPurgeBlocksRequest, responses( (status = 200, description = "Responses from individual cluster nodes", body = MultiResponse),