From 4ab0d9b19b7dc07b37c86459a46ce8c5e5639ba0 Mon Sep 17 00:00:00 2001 From: "oxide-reflector-bot[bot]" <130185838+oxide-reflector-bot[bot]@users.noreply.github.com> Date: Mon, 14 Jul 2025 17:49:16 +0000 Subject: [PATCH 1/9] Rebuilt with latest dependency updates --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d61174af..6700f443 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2822,7 +2822,7 @@ dependencies = [ [[package]] name = "progenitor" version = "0.11.0" -source = "git+https://github.com/oxidecomputer/progenitor#dc722c035fabdca010565853ed7c0114b8f9fca9" +source = "git+https://github.com/oxidecomputer/progenitor#d4f449fe79b0507fe5dc8f3eca3465623c19d0e6" dependencies = [ "progenitor-impl", ] @@ -2845,7 +2845,7 @@ dependencies = [ [[package]] name = "progenitor-impl" version = "0.11.0" -source = "git+https://github.com/oxidecomputer/progenitor#dc722c035fabdca010565853ed7c0114b8f9fca9" +source = "git+https://github.com/oxidecomputer/progenitor#d4f449fe79b0507fe5dc8f3eca3465623c19d0e6" dependencies = [ "heck", "http 1.3.1", From f5c13adcfae5b4267d6c8e1372225f6019c67a57 Mon Sep 17 00:00:00 2001 From: "oxide-reflector-bot[bot]" <130185838+oxide-reflector-bot[bot]@users.noreply.github.com> Date: Thu, 17 Jul 2025 01:20:43 +0000 Subject: [PATCH 2/9] Rebuilt with latest dependency updates --- cli/docs/cli.json | 8 ++++---- cli/src/generated_cli.rs | 8 ++++---- oxide.json | 4 ++-- sdk/src/generated_sdk.rs | 20 ++++++++++---------- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/cli/docs/cli.json b/cli/docs/cli.json index 768d9878..91fd225d 100644 --- a/cli/docs/cli.json +++ b/cli/docs/cli.json @@ -168,8 +168,8 @@ { "long": "sort-by", "values": [ - "ascending", - "descending" + "time_and_id_ascending", + "time_and_id_descending" ] } ] @@ -701,8 +701,8 @@ { "long": "sort-by", "values": [ - "ascending", - "descending" + "time_and_id_ascending", + "time_and_id_descending" ] } ] diff --git a/cli/src/generated_cli.rs b/cli/src/generated_cli.rs index 25745b1d..4f7cff87 100644 --- a/cli/src/generated_cli.rs +++ b/cli/src/generated_cli.rs @@ -596,8 +596,8 @@ impl Cli { .long("sort-by") .value_parser(::clap::builder::TypedValueParser::map( ::clap::builder::PossibleValuesParser::new([ - types::TimeAndIdSortMode::Ascending.to_string(), - types::TimeAndIdSortMode::Descending.to_string(), + types::TimeAndIdSortMode::TimeAndIdAscending.to_string(), + types::TimeAndIdSortMode::TimeAndIdDescending.to_string(), ]), |s| types::TimeAndIdSortMode::try_from(s).unwrap(), )) @@ -1097,8 +1097,8 @@ impl Cli { .long("sort-by") .value_parser(::clap::builder::TypedValueParser::map( ::clap::builder::PossibleValuesParser::new([ - types::TimeAndIdSortMode::Ascending.to_string(), - types::TimeAndIdSortMode::Descending.to_string(), + types::TimeAndIdSortMode::TimeAndIdAscending.to_string(), + types::TimeAndIdSortMode::TimeAndIdDescending.to_string(), ]), |s| types::TimeAndIdSortMode::try_from(s).unwrap(), )) diff --git a/oxide.json b/oxide.json index 6584f45b..239807b8 100644 --- a/oxide.json +++ b/oxide.json @@ -27154,14 +27154,14 @@ "description": "sort in increasing order of timestamp and ID, i.e., earliest first", "type": "string", "enum": [ - "ascending" + "time_and_id_ascending" ] }, { "description": "sort in increasing order of timestamp and ID, i.e., most recent first", "type": "string", "enum": [ - "descending" + "time_and_id_descending" ] } ] diff --git a/sdk/src/generated_sdk.rs b/sdk/src/generated_sdk.rs index ce5d755f..8867383c 100644 --- a/sdk/src/generated_sdk.rs +++ b/sdk/src/generated_sdk.rs @@ -28574,7 +28574,7 @@ pub mod types { /// earliest first", /// "type": "string", /// "enum": [ - /// "ascending" + /// "time_and_id_ascending" /// ] /// }, /// { @@ -28582,7 +28582,7 @@ pub mod types { /// most recent first", /// "type": "string", /// "enum": [ - /// "descending" + /// "time_and_id_descending" /// ] /// } /// ] @@ -28604,12 +28604,12 @@ pub mod types { )] pub enum TimeAndIdSortMode { /// sort in increasing order of timestamp and ID, i.e., earliest first - #[serde(rename = "ascending")] - Ascending, + #[serde(rename = "time_and_id_ascending")] + TimeAndIdAscending, /// sort in increasing order of timestamp and ID, i.e., most recent /// first - #[serde(rename = "descending")] - Descending, + #[serde(rename = "time_and_id_descending")] + TimeAndIdDescending, } impl ::std::convert::From<&Self> for TimeAndIdSortMode { @@ -28621,8 +28621,8 @@ pub mod types { impl ::std::fmt::Display for TimeAndIdSortMode { fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { match *self { - Self::Ascending => write!(f, "ascending"), - Self::Descending => write!(f, "descending"), + Self::TimeAndIdAscending => write!(f, "time_and_id_ascending"), + Self::TimeAndIdDescending => write!(f, "time_and_id_descending"), } } } @@ -28631,8 +28631,8 @@ pub mod types { type Err = self::error::ConversionError; fn from_str(value: &str) -> ::std::result::Result { match value { - "ascending" => Ok(Self::Ascending), - "descending" => Ok(Self::Descending), + "time_and_id_ascending" => Ok(Self::TimeAndIdAscending), + "time_and_id_descending" => Ok(Self::TimeAndIdDescending), _ => Err("invalid value".into()), } } From f660c9a342fff7df61677bba2b0cbc1863c41467 Mon Sep 17 00:00:00 2001 From: "oxide-reflector-bot[bot]" <130185838+oxide-reflector-bot[bot]@users.noreply.github.com> Date: Thu, 17 Jul 2025 22:51:42 +0000 Subject: [PATCH 3/9] Rebuilt with latest dependency updates --- cli/src/generated_cli.rs | 264 ++++++++ oxide.json | 255 ++++++++ sdk-httpmock/src/generated_httpmock.rs | 394 ++++++++++++ sdk/src/generated_sdk.rs | 823 +++++++++++++++++++++++++ 4 files changed, 1736 insertions(+) diff --git a/cli/src/generated_cli.rs b/cli/src/generated_cli.rs index 4f7cff87..7af9456a 100644 --- a/cli/src/generated_cli.rs +++ b/cli/src/generated_cli.rs @@ -317,6 +317,10 @@ impl Cli { CliCommand::SiloUtilizationView => Self::cli_silo_utilization_view(), CliCommand::TimeseriesQuery => Self::cli_timeseries_query(), CliCommand::UserList => Self::cli_user_list(), + CliCommand::UserView => Self::cli_user_view(), + CliCommand::UserTokenList => Self::cli_user_token_list(), + CliCommand::UserLogout => Self::cli_user_logout(), + CliCommand::UserSessionList => Self::cli_user_session_list(), CliCommand::UtilizationView => Self::cli_utilization_view(), CliCommand::VpcFirewallRulesView => Self::cli_vpc_firewall_rules_view(), CliCommand::VpcFirewallRulesUpdate => Self::cli_vpc_firewall_rules_update(), @@ -7133,6 +7137,94 @@ impl Cli { .about("List users") } + pub fn cli_user_view() -> ::clap::Command { + ::clap::Command::new("") + .arg( + ::clap::Arg::new("user-id") + .long("user-id") + .value_parser(::clap::value_parser!(::uuid::Uuid)) + .required(true) + .help("ID of the user"), + ) + .about("Fetch user") + } + + pub fn cli_user_token_list() -> ::clap::Command { + ::clap::Command::new("") + .arg( + ::clap::Arg::new("limit") + .long("limit") + .value_parser(::clap::value_parser!(::std::num::NonZeroU32)) + .required(false) + .help("Maximum number of items returned by a single call"), + ) + .arg( + ::clap::Arg::new("sort-by") + .long("sort-by") + .value_parser(::clap::builder::TypedValueParser::map( + ::clap::builder::PossibleValuesParser::new([ + types::IdSortMode::IdAscending.to_string(), + ]), + |s| types::IdSortMode::try_from(s).unwrap(), + )) + .required(false), + ) + .arg( + ::clap::Arg::new("user-id") + .long("user-id") + .value_parser(::clap::value_parser!(::uuid::Uuid)) + .required(true) + .help("ID of the user"), + ) + .about("List user's access tokens") + } + + pub fn cli_user_logout() -> ::clap::Command { + ::clap::Command::new("") + .arg( + ::clap::Arg::new("user-id") + .long("user-id") + .value_parser(::clap::value_parser!(::uuid::Uuid)) + .required(true) + .help("ID of the user"), + ) + .about("Log user out") + .long_about( + "Silo admins can use this endpoint to log the specified user out by deleting all \ + of their tokens AND sessions. This cannot be undone.", + ) + } + + pub fn cli_user_session_list() -> ::clap::Command { + ::clap::Command::new("") + .arg( + ::clap::Arg::new("limit") + .long("limit") + .value_parser(::clap::value_parser!(::std::num::NonZeroU32)) + .required(false) + .help("Maximum number of items returned by a single call"), + ) + .arg( + ::clap::Arg::new("sort-by") + .long("sort-by") + .value_parser(::clap::builder::TypedValueParser::map( + ::clap::builder::PossibleValuesParser::new([ + types::IdSortMode::IdAscending.to_string(), + ]), + |s| types::IdSortMode::try_from(s).unwrap(), + )) + .required(false), + ) + .arg( + ::clap::Arg::new("user-id") + .long("user-id") + .value_parser(::clap::value_parser!(::uuid::Uuid)) + .required(true) + .help("ID of the user"), + ) + .about("List user's console sessions") + } + pub fn cli_utilization_view() -> ::clap::Command { ::clap::Command::new("").about("Fetch resource utilization for user's current silo") } @@ -8725,6 +8817,10 @@ impl Cli { CliCommand::SiloUtilizationView => self.execute_silo_utilization_view(matches).await, CliCommand::TimeseriesQuery => self.execute_timeseries_query(matches).await, CliCommand::UserList => self.execute_user_list(matches).await, + CliCommand::UserView => self.execute_user_view(matches).await, + CliCommand::UserTokenList => self.execute_user_token_list(matches).await, + CliCommand::UserLogout => self.execute_user_logout(matches).await, + CliCommand::UserSessionList => self.execute_user_session_list(matches).await, CliCommand::UtilizationView => self.execute_utilization_view(matches).await, CliCommand::VpcFirewallRulesView => self.execute_vpc_firewall_rules_view(matches).await, CliCommand::VpcFirewallRulesUpdate => { @@ -16779,6 +16875,134 @@ impl Cli { } } + pub async fn execute_user_view(&self, matches: &::clap::ArgMatches) -> anyhow::Result<()> { + let mut request = self.client.user_view(); + if let Some(value) = matches.get_one::<::uuid::Uuid>("user-id") { + request = request.user_id(value.clone()); + } + + self.config.execute_user_view(matches, &mut request)?; + let result = request.send().await; + match result { + Ok(r) => { + self.config.success_item(&r); + Ok(()) + } + Err(r) => { + self.config.error(&r); + Err(anyhow::Error::new(r)) + } + } + } + + pub async fn execute_user_token_list( + &self, + matches: &::clap::ArgMatches, + ) -> anyhow::Result<()> { + let mut request = self.client.user_token_list(); + if let Some(value) = matches.get_one::<::std::num::NonZeroU32>("limit") { + request = request.limit(value.clone()); + } + + if let Some(value) = matches.get_one::("sort-by") { + request = request.sort_by(value.clone()); + } + + if let Some(value) = matches.get_one::<::uuid::Uuid>("user-id") { + request = request.user_id(value.clone()); + } + + self.config.execute_user_token_list(matches, &mut request)?; + self.config + .list_start::(); + let mut stream = futures::StreamExt::take( + request.stream(), + matches + .get_one::("limit") + .map_or(usize::MAX, |x| x.get() as usize), + ); + loop { + match futures::TryStreamExt::try_next(&mut stream).await { + Err(r) => { + self.config.list_end_error(&r); + return Err(anyhow::Error::new(r)); + } + Ok(None) => { + self.config + .list_end_success::(); + return Ok(()); + } + Ok(Some(value)) => { + self.config.list_item(&value); + } + } + } + } + + pub async fn execute_user_logout(&self, matches: &::clap::ArgMatches) -> anyhow::Result<()> { + let mut request = self.client.user_logout(); + if let Some(value) = matches.get_one::<::uuid::Uuid>("user-id") { + request = request.user_id(value.clone()); + } + + self.config.execute_user_logout(matches, &mut request)?; + let result = request.send().await; + match result { + Ok(r) => { + self.config.success_no_item(&r); + Ok(()) + } + Err(r) => { + self.config.error(&r); + Err(anyhow::Error::new(r)) + } + } + } + + pub async fn execute_user_session_list( + &self, + matches: &::clap::ArgMatches, + ) -> anyhow::Result<()> { + let mut request = self.client.user_session_list(); + if let Some(value) = matches.get_one::<::std::num::NonZeroU32>("limit") { + request = request.limit(value.clone()); + } + + if let Some(value) = matches.get_one::("sort-by") { + request = request.sort_by(value.clone()); + } + + if let Some(value) = matches.get_one::<::uuid::Uuid>("user-id") { + request = request.user_id(value.clone()); + } + + self.config + .execute_user_session_list(matches, &mut request)?; + self.config.list_start::(); + let mut stream = futures::StreamExt::take( + request.stream(), + matches + .get_one::("limit") + .map_or(usize::MAX, |x| x.get() as usize), + ); + loop { + match futures::TryStreamExt::try_next(&mut stream).await { + Err(r) => { + self.config.list_end_error(&r); + return Err(anyhow::Error::new(r)); + } + Ok(None) => { + self.config + .list_end_success::(); + return Ok(()); + } + Ok(Some(value)) => { + self.config.list_item(&value); + } + } + } + } + pub async fn execute_utilization_view( &self, matches: &::clap::ArgMatches, @@ -19806,6 +20030,38 @@ pub trait CliConfig { Ok(()) } + fn execute_user_view( + &self, + matches: &::clap::ArgMatches, + request: &mut builder::UserView, + ) -> anyhow::Result<()> { + Ok(()) + } + + fn execute_user_token_list( + &self, + matches: &::clap::ArgMatches, + request: &mut builder::UserTokenList, + ) -> anyhow::Result<()> { + Ok(()) + } + + fn execute_user_logout( + &self, + matches: &::clap::ArgMatches, + request: &mut builder::UserLogout, + ) -> anyhow::Result<()> { + Ok(()) + } + + fn execute_user_session_list( + &self, + matches: &::clap::ArgMatches, + request: &mut builder::UserSessionList, + ) -> anyhow::Result<()> { + Ok(()) + } + fn execute_utilization_view( &self, matches: &::clap::ArgMatches, @@ -20279,6 +20535,10 @@ pub enum CliCommand { SiloUtilizationView, TimeseriesQuery, UserList, + UserView, + UserTokenList, + UserLogout, + UserSessionList, UtilizationView, VpcFirewallRulesView, VpcFirewallRulesUpdate, @@ -20551,6 +20811,10 @@ impl CliCommand { CliCommand::SiloUtilizationView, CliCommand::TimeseriesQuery, CliCommand::UserList, + CliCommand::UserView, + CliCommand::UserTokenList, + CliCommand::UserLogout, + CliCommand::UserSessionList, CliCommand::UtilizationView, CliCommand::VpcFirewallRulesView, CliCommand::VpcFirewallRulesUpdate, diff --git a/oxide.json b/oxide.json index 239807b8..44f3e1fe 100644 --- a/oxide.json +++ b/oxide.json @@ -11577,6 +11577,216 @@ } } }, + "/v1/users/{user_id}": { + "get": { + "tags": [ + "silos" + ], + "summary": "Fetch user", + "operationId": "user_view", + "parameters": [ + { + "in": "path", + "name": "user_id", + "description": "ID of the user", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/User" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/v1/users/{user_id}/access-tokens": { + "get": { + "tags": [ + "silos" + ], + "summary": "List user's access tokens", + "operationId": "user_token_list", + "parameters": [ + { + "in": "path", + "name": "user_id", + "description": "ID of the user", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "in": "query", + "name": "limit", + "description": "Maximum number of items returned by a single call", + "schema": { + "nullable": true, + "type": "integer", + "format": "uint32", + "minimum": 1 + } + }, + { + "in": "query", + "name": "page_token", + "description": "Token returned by previous call to retrieve the subsequent page", + "schema": { + "nullable": true, + "type": "string" + } + }, + { + "in": "query", + "name": "sort_by", + "schema": { + "$ref": "#/components/schemas/IdSortMode" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeviceAccessTokenResultsPage" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + }, + "x-dropshot-pagination": { + "required": [] + } + } + }, + "/v1/users/{user_id}/logout": { + "post": { + "tags": [ + "silos" + ], + "summary": "Log user out", + "description": "Silo admins can use this endpoint to log the specified user out by deleting all of their tokens AND sessions. This cannot be undone.", + "operationId": "user_logout", + "parameters": [ + { + "in": "path", + "name": "user_id", + "description": "ID of the user", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "204": { + "description": "resource updated" + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/v1/users/{user_id}/sessions": { + "get": { + "tags": [ + "silos" + ], + "summary": "List user's console sessions", + "operationId": "user_session_list", + "parameters": [ + { + "in": "path", + "name": "user_id", + "description": "ID of the user", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "in": "query", + "name": "limit", + "description": "Maximum number of items returned by a single call", + "schema": { + "nullable": true, + "type": "integer", + "format": "uint32", + "minimum": 1 + } + }, + { + "in": "query", + "name": "page_token", + "description": "Token returned by previous call to retrieve the subsequent page", + "schema": { + "nullable": true, + "type": "string" + } + }, + { + "in": "query", + "name": "sort_by", + "schema": { + "$ref": "#/components/schemas/IdSortMode" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ConsoleSessionResultsPage" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + }, + "x-dropshot-pagination": { + "required": [] + } + } + }, "/v1/utilization": { "get": { "tags": [ @@ -16165,6 +16375,51 @@ "items" ] }, + "ConsoleSession": { + "description": "View of a console session", + "type": "object", + "properties": { + "id": { + "description": "A unique, immutable, system-controlled identifier for the session", + "type": "string", + "format": "uuid" + }, + "time_created": { + "type": "string", + "format": "date-time" + }, + "time_last_used": { + "type": "string", + "format": "date-time" + } + }, + "required": [ + "id", + "time_created", + "time_last_used" + ] + }, + "ConsoleSessionResultsPage": { + "description": "A single page of results", + "type": "object", + "properties": { + "items": { + "description": "list of items on this page of results", + "type": "array", + "items": { + "$ref": "#/components/schemas/ConsoleSession" + } + }, + "next_page": { + "nullable": true, + "description": "token used to fetch the next page of results (if any)", + "type": "string" + } + }, + "required": [ + "items" + ] + }, "Cumulativedouble": { "description": "A cumulative or counter data type.", "type": "object", diff --git a/sdk-httpmock/src/generated_httpmock.rs b/sdk-httpmock/src/generated_httpmock.rs index ae396285..8948234a 100644 --- a/sdk-httpmock/src/generated_httpmock.rs +++ b/sdk-httpmock/src/generated_httpmock.rs @@ -18980,6 +18980,340 @@ pub mod operations { } } + pub struct UserViewWhen(::httpmock::When); + impl UserViewWhen { + pub fn new(inner: ::httpmock::When) -> Self { + Self( + inner + .method(::httpmock::Method::GET) + .path_matches(regex::Regex::new("^/v1/users/[^/]*$").unwrap()), + ) + } + + pub fn into_inner(self) -> ::httpmock::When { + self.0 + } + + pub fn user_id(self, value: &::uuid::Uuid) -> Self { + let re = regex::Regex::new(&format!("^/v1/users/{}$", value.to_string())).unwrap(); + Self(self.0.path_matches(re)) + } + } + + pub struct UserViewThen(::httpmock::Then); + impl UserViewThen { + pub fn new(inner: ::httpmock::Then) -> Self { + Self(inner) + } + + pub fn into_inner(self) -> ::httpmock::Then { + self.0 + } + + pub fn ok(self, value: &types::User) -> Self { + Self( + self.0 + .status(200u16) + .header("content-type", "application/json") + .json_body_obj(value), + ) + } + + pub fn client_error(self, status: u16, value: &types::Error) -> Self { + assert_eq!(status / 100u16, 4u16); + Self( + self.0 + .status(status) + .header("content-type", "application/json") + .json_body_obj(value), + ) + } + + pub fn server_error(self, status: u16, value: &types::Error) -> Self { + assert_eq!(status / 100u16, 5u16); + Self( + self.0 + .status(status) + .header("content-type", "application/json") + .json_body_obj(value), + ) + } + } + + pub struct UserTokenListWhen(::httpmock::When); + impl UserTokenListWhen { + pub fn new(inner: ::httpmock::When) -> Self { + Self( + inner + .method(::httpmock::Method::GET) + .path_matches(regex::Regex::new("^/v1/users/[^/]*/access-tokens$").unwrap()), + ) + } + + pub fn into_inner(self) -> ::httpmock::When { + self.0 + } + + pub fn user_id(self, value: &::uuid::Uuid) -> Self { + let re = regex::Regex::new(&format!("^/v1/users/{}/access-tokens$", value.to_string())) + .unwrap(); + Self(self.0.path_matches(re)) + } + + pub fn limit(self, value: T) -> Self + where + T: Into>, + { + if let Some(value) = value.into() { + Self(self.0.query_param("limit", value.to_string())) + } else { + Self(self.0.matches(|req| { + req.query_params + .as_ref() + .and_then(|qs| qs.iter().find(|(key, _)| key == "limit")) + .is_none() + })) + } + } + + pub fn page_token<'a, T>(self, value: T) -> Self + where + T: Into>, + { + if let Some(value) = value.into() { + Self(self.0.query_param("page_token", value.to_string())) + } else { + Self(self.0.matches(|req| { + req.query_params + .as_ref() + .and_then(|qs| qs.iter().find(|(key, _)| key == "page_token")) + .is_none() + })) + } + } + + pub fn sort_by(self, value: T) -> Self + where + T: Into>, + { + if let Some(value) = value.into() { + Self(self.0.query_param("sort_by", value.to_string())) + } else { + Self(self.0.matches(|req| { + req.query_params + .as_ref() + .and_then(|qs| qs.iter().find(|(key, _)| key == "sort_by")) + .is_none() + })) + } + } + } + + pub struct UserTokenListThen(::httpmock::Then); + impl UserTokenListThen { + pub fn new(inner: ::httpmock::Then) -> Self { + Self(inner) + } + + pub fn into_inner(self) -> ::httpmock::Then { + self.0 + } + + pub fn ok(self, value: &types::DeviceAccessTokenResultsPage) -> Self { + Self( + self.0 + .status(200u16) + .header("content-type", "application/json") + .json_body_obj(value), + ) + } + + pub fn client_error(self, status: u16, value: &types::Error) -> Self { + assert_eq!(status / 100u16, 4u16); + Self( + self.0 + .status(status) + .header("content-type", "application/json") + .json_body_obj(value), + ) + } + + pub fn server_error(self, status: u16, value: &types::Error) -> Self { + assert_eq!(status / 100u16, 5u16); + Self( + self.0 + .status(status) + .header("content-type", "application/json") + .json_body_obj(value), + ) + } + } + + pub struct UserLogoutWhen(::httpmock::When); + impl UserLogoutWhen { + pub fn new(inner: ::httpmock::When) -> Self { + Self( + inner + .method(::httpmock::Method::POST) + .path_matches(regex::Regex::new("^/v1/users/[^/]*/logout$").unwrap()), + ) + } + + pub fn into_inner(self) -> ::httpmock::When { + self.0 + } + + pub fn user_id(self, value: &::uuid::Uuid) -> Self { + let re = + regex::Regex::new(&format!("^/v1/users/{}/logout$", value.to_string())).unwrap(); + Self(self.0.path_matches(re)) + } + } + + pub struct UserLogoutThen(::httpmock::Then); + impl UserLogoutThen { + pub fn new(inner: ::httpmock::Then) -> Self { + Self(inner) + } + + pub fn into_inner(self) -> ::httpmock::Then { + self.0 + } + + pub fn no_content(self) -> Self { + Self(self.0.status(204u16)) + } + + pub fn client_error(self, status: u16, value: &types::Error) -> Self { + assert_eq!(status / 100u16, 4u16); + Self( + self.0 + .status(status) + .header("content-type", "application/json") + .json_body_obj(value), + ) + } + + pub fn server_error(self, status: u16, value: &types::Error) -> Self { + assert_eq!(status / 100u16, 5u16); + Self( + self.0 + .status(status) + .header("content-type", "application/json") + .json_body_obj(value), + ) + } + } + + pub struct UserSessionListWhen(::httpmock::When); + impl UserSessionListWhen { + pub fn new(inner: ::httpmock::When) -> Self { + Self( + inner + .method(::httpmock::Method::GET) + .path_matches(regex::Regex::new("^/v1/users/[^/]*/sessions$").unwrap()), + ) + } + + pub fn into_inner(self) -> ::httpmock::When { + self.0 + } + + pub fn user_id(self, value: &::uuid::Uuid) -> Self { + let re = + regex::Regex::new(&format!("^/v1/users/{}/sessions$", value.to_string())).unwrap(); + Self(self.0.path_matches(re)) + } + + pub fn limit(self, value: T) -> Self + where + T: Into>, + { + if let Some(value) = value.into() { + Self(self.0.query_param("limit", value.to_string())) + } else { + Self(self.0.matches(|req| { + req.query_params + .as_ref() + .and_then(|qs| qs.iter().find(|(key, _)| key == "limit")) + .is_none() + })) + } + } + + pub fn page_token<'a, T>(self, value: T) -> Self + where + T: Into>, + { + if let Some(value) = value.into() { + Self(self.0.query_param("page_token", value.to_string())) + } else { + Self(self.0.matches(|req| { + req.query_params + .as_ref() + .and_then(|qs| qs.iter().find(|(key, _)| key == "page_token")) + .is_none() + })) + } + } + + pub fn sort_by(self, value: T) -> Self + where + T: Into>, + { + if let Some(value) = value.into() { + Self(self.0.query_param("sort_by", value.to_string())) + } else { + Self(self.0.matches(|req| { + req.query_params + .as_ref() + .and_then(|qs| qs.iter().find(|(key, _)| key == "sort_by")) + .is_none() + })) + } + } + } + + pub struct UserSessionListThen(::httpmock::Then); + impl UserSessionListThen { + pub fn new(inner: ::httpmock::Then) -> Self { + Self(inner) + } + + pub fn into_inner(self) -> ::httpmock::Then { + self.0 + } + + pub fn ok(self, value: &types::ConsoleSessionResultsPage) -> Self { + Self( + self.0 + .status(200u16) + .header("content-type", "application/json") + .json_body_obj(value), + ) + } + + pub fn client_error(self, status: u16, value: &types::Error) -> Self { + assert_eq!(status / 100u16, 4u16); + Self( + self.0 + .status(status) + .header("content-type", "application/json") + .json_body_obj(value), + ) + } + + pub fn server_error(self, status: u16, value: &types::Error) -> Self { + assert_eq!(status / 100u16, 5u16); + Self( + self.0 + .status(status) + .header("content-type", "application/json") + .json_body_obj(value), + ) + } + } + pub struct UtilizationViewWhen(::httpmock::When); impl UtilizationViewWhen { pub fn new(inner: ::httpmock::When) -> Self { @@ -22522,6 +22856,18 @@ pub trait MockServerExt { fn user_list(&self, config_fn: F) -> ::httpmock::Mock where F: FnOnce(operations::UserListWhen, operations::UserListThen); + fn user_view(&self, config_fn: F) -> ::httpmock::Mock + where + F: FnOnce(operations::UserViewWhen, operations::UserViewThen); + fn user_token_list(&self, config_fn: F) -> ::httpmock::Mock + where + F: FnOnce(operations::UserTokenListWhen, operations::UserTokenListThen); + fn user_logout(&self, config_fn: F) -> ::httpmock::Mock + where + F: FnOnce(operations::UserLogoutWhen, operations::UserLogoutThen); + fn user_session_list(&self, config_fn: F) -> ::httpmock::Mock + where + F: FnOnce(operations::UserSessionListWhen, operations::UserSessionListThen); fn utilization_view(&self, config_fn: F) -> ::httpmock::Mock where F: FnOnce(operations::UtilizationViewWhen, operations::UtilizationViewThen); @@ -25681,6 +26027,54 @@ impl MockServerExt for ::httpmock::MockServer { }) } + fn user_view(&self, config_fn: F) -> ::httpmock::Mock + where + F: FnOnce(operations::UserViewWhen, operations::UserViewThen), + { + self.mock(|when, then| { + config_fn( + operations::UserViewWhen::new(when), + operations::UserViewThen::new(then), + ) + }) + } + + fn user_token_list(&self, config_fn: F) -> ::httpmock::Mock + where + F: FnOnce(operations::UserTokenListWhen, operations::UserTokenListThen), + { + self.mock(|when, then| { + config_fn( + operations::UserTokenListWhen::new(when), + operations::UserTokenListThen::new(then), + ) + }) + } + + fn user_logout(&self, config_fn: F) -> ::httpmock::Mock + where + F: FnOnce(operations::UserLogoutWhen, operations::UserLogoutThen), + { + self.mock(|when, then| { + config_fn( + operations::UserLogoutWhen::new(when), + operations::UserLogoutThen::new(then), + ) + }) + } + + fn user_session_list(&self, config_fn: F) -> ::httpmock::Mock + where + F: FnOnce(operations::UserSessionListWhen, operations::UserSessionListThen), + { + self.mock(|when, then| { + config_fn( + operations::UserSessionListWhen::new(when), + operations::UserSessionListThen::new(then), + ) + }) + } + fn utilization_view(&self, config_fn: F) -> ::httpmock::Mock where F: FnOnce(operations::UtilizationViewWhen, operations::UtilizationViewThen), diff --git a/sdk/src/generated_sdk.rs b/sdk/src/generated_sdk.rs index 8867383c..c6e5836a 100644 --- a/sdk/src/generated_sdk.rs +++ b/sdk/src/generated_sdk.rs @@ -6799,6 +6799,114 @@ pub mod types { } } + /// View of a console session + /// + ///
JSON schema + /// + /// ```json + /// { + /// "description": "View of a console session", + /// "type": "object", + /// "required": [ + /// "id", + /// "time_created", + /// "time_last_used" + /// ], + /// "properties": { + /// "id": { + /// "description": "A unique, immutable, system-controlled identifier + /// for the session", + /// "type": "string", + /// "format": "uuid" + /// }, + /// "time_created": { + /// "type": "string", + /// "format": "date-time" + /// }, + /// "time_last_used": { + /// "type": "string", + /// "format": "date-time" + /// } + /// } + /// } + /// ``` + ///
+ #[derive( + :: serde :: Deserialize, :: serde :: Serialize, Clone, Debug, schemars :: JsonSchema, + )] + pub struct ConsoleSession { + /// A unique, immutable, system-controlled identifier for the session + pub id: ::uuid::Uuid, + pub time_created: ::chrono::DateTime<::chrono::offset::Utc>, + pub time_last_used: ::chrono::DateTime<::chrono::offset::Utc>, + } + + impl ::std::convert::From<&ConsoleSession> for ConsoleSession { + fn from(value: &ConsoleSession) -> Self { + value.clone() + } + } + + impl ConsoleSession { + pub fn builder() -> builder::ConsoleSession { + Default::default() + } + } + + /// A single page of results + /// + ///
JSON schema + /// + /// ```json + /// { + /// "description": "A single page of results", + /// "type": "object", + /// "required": [ + /// "items" + /// ], + /// "properties": { + /// "items": { + /// "description": "list of items on this page of results", + /// "type": "array", + /// "items": { + /// "$ref": "#/components/schemas/ConsoleSession" + /// } + /// }, + /// "next_page": { + /// "description": "token used to fetch the next page of results (if + /// any)", + /// "type": [ + /// "string", + /// "null" + /// ] + /// } + /// } + /// } + /// ``` + ///
+ #[derive( + :: serde :: Deserialize, :: serde :: Serialize, Clone, Debug, schemars :: JsonSchema, + )] + pub struct ConsoleSessionResultsPage { + /// list of items on this page of results + pub items: ::std::vec::Vec, + /// token used to fetch the next page of results (if any) + #[serde(default, skip_serializing_if = "::std::option::Option::is_none")] + pub next_page: ::std::option::Option<::std::string::String>, + } + + impl ::std::convert::From<&ConsoleSessionResultsPage> for ConsoleSessionResultsPage { + fn from(value: &ConsoleSessionResultsPage) -> Self { + value.clone() + } + } + + impl ConsoleSessionResultsPage { + pub fn builder() -> builder::ConsoleSessionResultsPage { + Default::default() + } + } + /// A cumulative or counter data type. /// ///
JSON schema @@ -39423,6 +39531,150 @@ pub mod types { } } + #[derive(Clone, Debug)] + pub struct ConsoleSession { + id: ::std::result::Result<::uuid::Uuid, ::std::string::String>, + time_created: ::std::result::Result< + ::chrono::DateTime<::chrono::offset::Utc>, + ::std::string::String, + >, + time_last_used: ::std::result::Result< + ::chrono::DateTime<::chrono::offset::Utc>, + ::std::string::String, + >, + } + + impl ::std::default::Default for ConsoleSession { + fn default() -> Self { + Self { + id: Err("no value supplied for id".to_string()), + time_created: Err("no value supplied for time_created".to_string()), + time_last_used: Err("no value supplied for time_last_used".to_string()), + } + } + } + + impl ConsoleSession { + pub fn id(mut self, value: T) -> Self + where + T: ::std::convert::TryInto<::uuid::Uuid>, + T::Error: ::std::fmt::Display, + { + self.id = value + .try_into() + .map_err(|e| format!("error converting supplied value for id: {}", e)); + self + } + pub fn time_created(mut self, value: T) -> Self + where + T: ::std::convert::TryInto<::chrono::DateTime<::chrono::offset::Utc>>, + T::Error: ::std::fmt::Display, + { + self.time_created = value.try_into().map_err(|e| { + format!("error converting supplied value for time_created: {}", e) + }); + self + } + pub fn time_last_used(mut self, value: T) -> Self + where + T: ::std::convert::TryInto<::chrono::DateTime<::chrono::offset::Utc>>, + T::Error: ::std::fmt::Display, + { + self.time_last_used = value.try_into().map_err(|e| { + format!("error converting supplied value for time_last_used: {}", e) + }); + self + } + } + + impl ::std::convert::TryFrom for super::ConsoleSession { + type Error = super::error::ConversionError; + fn try_from( + value: ConsoleSession, + ) -> ::std::result::Result { + Ok(Self { + id: value.id?, + time_created: value.time_created?, + time_last_used: value.time_last_used?, + }) + } + } + + impl ::std::convert::From for ConsoleSession { + fn from(value: super::ConsoleSession) -> Self { + Self { + id: Ok(value.id), + time_created: Ok(value.time_created), + time_last_used: Ok(value.time_last_used), + } + } + } + + #[derive(Clone, Debug)] + pub struct ConsoleSessionResultsPage { + items: ::std::result::Result< + ::std::vec::Vec, + ::std::string::String, + >, + next_page: ::std::result::Result< + ::std::option::Option<::std::string::String>, + ::std::string::String, + >, + } + + impl ::std::default::Default for ConsoleSessionResultsPage { + fn default() -> Self { + Self { + items: Err("no value supplied for items".to_string()), + next_page: Ok(Default::default()), + } + } + } + + impl ConsoleSessionResultsPage { + pub fn items(mut self, value: T) -> Self + where + T: ::std::convert::TryInto<::std::vec::Vec>, + T::Error: ::std::fmt::Display, + { + self.items = value + .try_into() + .map_err(|e| format!("error converting supplied value for items: {}", e)); + self + } + pub fn next_page(mut self, value: T) -> Self + where + T: ::std::convert::TryInto<::std::option::Option<::std::string::String>>, + T::Error: ::std::fmt::Display, + { + self.next_page = value + .try_into() + .map_err(|e| format!("error converting supplied value for next_page: {}", e)); + self + } + } + + impl ::std::convert::TryFrom for super::ConsoleSessionResultsPage { + type Error = super::error::ConversionError; + fn try_from( + value: ConsoleSessionResultsPage, + ) -> ::std::result::Result { + Ok(Self { + items: value.items?, + next_page: value.next_page?, + }) + } + } + + impl ::std::convert::From for ConsoleSessionResultsPage { + fn from(value: super::ConsoleSessionResultsPage) -> Self { + Self { + items: Ok(value.items), + next_page: Ok(value.next_page), + } + } + } + #[derive(Clone, Debug)] pub struct Cumulativedouble { start_time: ::std::result::Result< @@ -62813,6 +63065,75 @@ pub trait ClientSilosExt { /// .await; /// ``` fn user_list(&self) -> builder::UserList; + /// Fetch user + /// + /// Sends a `GET` request to `/v1/users/{user_id}` + /// + /// Arguments: + /// - `user_id`: ID of the user + /// ```ignore + /// let response = client.user_view() + /// .user_id(user_id) + /// .send() + /// .await; + /// ``` + fn user_view(&self) -> builder::UserView; + /// List user's access tokens + /// + /// Sends a `GET` request to `/v1/users/{user_id}/access-tokens` + /// + /// Arguments: + /// - `user_id`: ID of the user + /// - `limit`: Maximum number of items returned by a single call + /// - `page_token`: Token returned by previous call to retrieve the + /// subsequent page + /// - `sort_by` + /// ```ignore + /// let response = client.user_token_list() + /// .user_id(user_id) + /// .limit(limit) + /// .page_token(page_token) + /// .sort_by(sort_by) + /// .send() + /// .await; + /// ``` + fn user_token_list(&self) -> builder::UserTokenList; + /// Log user out + /// + /// Silo admins can use this endpoint to log the specified user out by + /// deleting all of their tokens AND sessions. This cannot be undone. + /// + /// Sends a `POST` request to `/v1/users/{user_id}/logout` + /// + /// Arguments: + /// - `user_id`: ID of the user + /// ```ignore + /// let response = client.user_logout() + /// .user_id(user_id) + /// .send() + /// .await; + /// ``` + fn user_logout(&self) -> builder::UserLogout; + /// List user's console sessions + /// + /// Sends a `GET` request to `/v1/users/{user_id}/sessions` + /// + /// Arguments: + /// - `user_id`: ID of the user + /// - `limit`: Maximum number of items returned by a single call + /// - `page_token`: Token returned by previous call to retrieve the + /// subsequent page + /// - `sort_by` + /// ```ignore + /// let response = client.user_session_list() + /// .user_id(user_id) + /// .limit(limit) + /// .page_token(page_token) + /// .sort_by(sort_by) + /// .send() + /// .await; + /// ``` + fn user_session_list(&self) -> builder::UserSessionList; /// Fetch resource utilization for user's current silo /// /// Sends a `GET` request to `/v1/utilization` @@ -62870,6 +63191,22 @@ impl ClientSilosExt for Client { builder::UserList::new(self) } + fn user_view(&self) -> builder::UserView { + builder::UserView::new(self) + } + + fn user_token_list(&self) -> builder::UserTokenList { + builder::UserTokenList::new(self) + } + + fn user_logout(&self) -> builder::UserLogout { + builder::UserLogout::new(self) + } + + fn user_session_list(&self) -> builder::UserSessionList { + builder::UserSessionList::new(self) + } + fn utilization_view(&self) -> builder::UtilizationView { builder::UtilizationView::new(self) } @@ -92245,6 +92582,492 @@ pub mod builder { } } + /// Builder for [`ClientSilosExt::user_view`] + /// + /// [`ClientSilosExt::user_view`]: super::ClientSilosExt::user_view + #[derive(Debug, Clone)] + pub struct UserView<'a> { + client: &'a super::Client, + user_id: Result<::uuid::Uuid, String>, + } + + impl<'a> UserView<'a> { + pub fn new(client: &'a super::Client) -> Self { + Self { + client: client, + user_id: Err("user_id was not initialized".to_string()), + } + } + + pub fn user_id(mut self, value: V) -> Self + where + V: std::convert::TryInto<::uuid::Uuid>, + { + self.user_id = value + .try_into() + .map_err(|_| "conversion to `:: uuid :: Uuid` for user_id failed".to_string()); + self + } + + /// Sends a `GET` request to `/v1/users/{user_id}` + pub async fn send(self) -> Result, Error> { + let Self { client, user_id } = self; + let user_id = user_id.map_err(Error::InvalidRequest)?; + let url = format!( + "{}/v1/users/{}", + client.baseurl, + encode_path(&user_id.to_string()), + ); + let mut header_map = ::reqwest::header::HeaderMap::with_capacity(1usize); + header_map.append( + ::reqwest::header::HeaderName::from_static("api-version"), + ::reqwest::header::HeaderValue::from_static(super::Client::api_version()), + ); + #[allow(unused_mut)] + let mut request = client + .client + .get(url) + .header( + ::reqwest::header::ACCEPT, + ::reqwest::header::HeaderValue::from_static("application/json"), + ) + .headers(header_map) + .build()?; + let info = OperationInfo { + operation_id: "user_view", + }; + client.pre(&mut request, &info).await?; + let result = client.exec(request, &info).await; + client.post(&result, &info).await?; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + 400u16..=499u16 => Err(Error::ErrorResponse( + ResponseValue::from_response(response).await?, + )), + 500u16..=599u16 => Err(Error::ErrorResponse( + ResponseValue::from_response(response).await?, + )), + _ => Err(Error::UnexpectedResponse(response)), + } + } + } + + /// Builder for [`ClientSilosExt::user_token_list`] + /// + /// [`ClientSilosExt::user_token_list`]: super::ClientSilosExt::user_token_list + #[derive(Debug, Clone)] + pub struct UserTokenList<'a> { + client: &'a super::Client, + user_id: Result<::uuid::Uuid, String>, + limit: Result, String>, + page_token: Result, String>, + sort_by: Result, String>, + } + + impl<'a> UserTokenList<'a> { + pub fn new(client: &'a super::Client) -> Self { + Self { + client: client, + user_id: Err("user_id was not initialized".to_string()), + limit: Ok(None), + page_token: Ok(None), + sort_by: Ok(None), + } + } + + pub fn user_id(mut self, value: V) -> Self + where + V: std::convert::TryInto<::uuid::Uuid>, + { + self.user_id = value + .try_into() + .map_err(|_| "conversion to `:: uuid :: Uuid` for user_id failed".to_string()); + self + } + + pub fn limit(mut self, value: V) -> Self + where + V: std::convert::TryInto<::std::num::NonZeroU32>, + { + self.limit = value.try_into().map(Some).map_err(|_| { + "conversion to `:: std :: num :: NonZeroU32` for limit failed".to_string() + }); + self + } + + pub fn page_token(mut self, value: V) -> Self + where + V: std::convert::TryInto<::std::string::String>, + { + self.page_token = value.try_into().map(Some).map_err(|_| { + "conversion to `:: std :: string :: String` for page_token failed".to_string() + }); + self + } + + pub fn sort_by(mut self, value: V) -> Self + where + V: std::convert::TryInto, + { + self.sort_by = value + .try_into() + .map(Some) + .map_err(|_| "conversion to `IdSortMode` for sort_by failed".to_string()); + self + } + + /// Sends a `GET` request to `/v1/users/{user_id}/access-tokens` + pub async fn send( + self, + ) -> Result, Error> + { + let Self { + client, + user_id, + limit, + page_token, + sort_by, + } = self; + let user_id = user_id.map_err(Error::InvalidRequest)?; + let limit = limit.map_err(Error::InvalidRequest)?; + let page_token = page_token.map_err(Error::InvalidRequest)?; + let sort_by = sort_by.map_err(Error::InvalidRequest)?; + let url = format!( + "{}/v1/users/{}/access-tokens", + client.baseurl, + encode_path(&user_id.to_string()), + ); + let mut header_map = ::reqwest::header::HeaderMap::with_capacity(1usize); + header_map.append( + ::reqwest::header::HeaderName::from_static("api-version"), + ::reqwest::header::HeaderValue::from_static(super::Client::api_version()), + ); + #[allow(unused_mut)] + let mut request = client + .client + .get(url) + .header( + ::reqwest::header::ACCEPT, + ::reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&progenitor_client::QueryParam::new("limit", &limit)) + .query(&progenitor_client::QueryParam::new( + "page_token", + &page_token, + )) + .query(&progenitor_client::QueryParam::new("sort_by", &sort_by)) + .headers(header_map) + .build()?; + let info = OperationInfo { + operation_id: "user_token_list", + }; + client.pre(&mut request, &info).await?; + let result = client.exec(request, &info).await; + client.post(&result, &info).await?; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + 400u16..=499u16 => Err(Error::ErrorResponse( + ResponseValue::from_response(response).await?, + )), + 500u16..=599u16 => Err(Error::ErrorResponse( + ResponseValue::from_response(response).await?, + )), + _ => Err(Error::UnexpectedResponse(response)), + } + } + + /// Streams `GET` requests to `/v1/users/{user_id}/access-tokens` + pub fn stream( + self, + ) -> impl futures::Stream>> + + Unpin + + 'a { + use ::futures::StreamExt; + use ::futures::TryFutureExt; + use ::futures::TryStreamExt; + let next = Self { + page_token: Ok(None), + sort_by: Ok(None), + ..self.clone() + }; + self.send() + .map_ok(move |page| { + let page = page.into_inner(); + let first = futures::stream::iter(page.items).map(Ok); + let rest = futures::stream::try_unfold( + (page.next_page, next), + |(next_page, next)| async { + if next_page.is_none() { + Ok(None) + } else { + Self { + page_token: Ok(next_page), + ..next.clone() + } + .send() + .map_ok(|page| { + let page = page.into_inner(); + Some(( + futures::stream::iter(page.items).map(Ok), + (page.next_page, next), + )) + }) + .await + } + }, + ) + .try_flatten(); + first.chain(rest) + }) + .try_flatten_stream() + .boxed() + } + } + + /// Builder for [`ClientSilosExt::user_logout`] + /// + /// [`ClientSilosExt::user_logout`]: super::ClientSilosExt::user_logout + #[derive(Debug, Clone)] + pub struct UserLogout<'a> { + client: &'a super::Client, + user_id: Result<::uuid::Uuid, String>, + } + + impl<'a> UserLogout<'a> { + pub fn new(client: &'a super::Client) -> Self { + Self { + client: client, + user_id: Err("user_id was not initialized".to_string()), + } + } + + pub fn user_id(mut self, value: V) -> Self + where + V: std::convert::TryInto<::uuid::Uuid>, + { + self.user_id = value + .try_into() + .map_err(|_| "conversion to `:: uuid :: Uuid` for user_id failed".to_string()); + self + } + + /// Sends a `POST` request to `/v1/users/{user_id}/logout` + pub async fn send(self) -> Result, Error> { + let Self { client, user_id } = self; + let user_id = user_id.map_err(Error::InvalidRequest)?; + let url = format!( + "{}/v1/users/{}/logout", + client.baseurl, + encode_path(&user_id.to_string()), + ); + let mut header_map = ::reqwest::header::HeaderMap::with_capacity(1usize); + header_map.append( + ::reqwest::header::HeaderName::from_static("api-version"), + ::reqwest::header::HeaderValue::from_static(super::Client::api_version()), + ); + #[allow(unused_mut)] + let mut request = client + .client + .post(url) + .header( + ::reqwest::header::ACCEPT, + ::reqwest::header::HeaderValue::from_static("application/json"), + ) + .headers(header_map) + .build()?; + let info = OperationInfo { + operation_id: "user_logout", + }; + client.pre(&mut request, &info).await?; + let result = client.exec(request, &info).await; + client.post(&result, &info).await?; + let response = result?; + match response.status().as_u16() { + 204u16 => Ok(ResponseValue::empty(response)), + 400u16..=499u16 => Err(Error::ErrorResponse( + ResponseValue::from_response(response).await?, + )), + 500u16..=599u16 => Err(Error::ErrorResponse( + ResponseValue::from_response(response).await?, + )), + _ => Err(Error::UnexpectedResponse(response)), + } + } + } + + /// Builder for [`ClientSilosExt::user_session_list`] + /// + /// [`ClientSilosExt::user_session_list`]: super::ClientSilosExt::user_session_list + #[derive(Debug, Clone)] + pub struct UserSessionList<'a> { + client: &'a super::Client, + user_id: Result<::uuid::Uuid, String>, + limit: Result, String>, + page_token: Result, String>, + sort_by: Result, String>, + } + + impl<'a> UserSessionList<'a> { + pub fn new(client: &'a super::Client) -> Self { + Self { + client: client, + user_id: Err("user_id was not initialized".to_string()), + limit: Ok(None), + page_token: Ok(None), + sort_by: Ok(None), + } + } + + pub fn user_id(mut self, value: V) -> Self + where + V: std::convert::TryInto<::uuid::Uuid>, + { + self.user_id = value + .try_into() + .map_err(|_| "conversion to `:: uuid :: Uuid` for user_id failed".to_string()); + self + } + + pub fn limit(mut self, value: V) -> Self + where + V: std::convert::TryInto<::std::num::NonZeroU32>, + { + self.limit = value.try_into().map(Some).map_err(|_| { + "conversion to `:: std :: num :: NonZeroU32` for limit failed".to_string() + }); + self + } + + pub fn page_token(mut self, value: V) -> Self + where + V: std::convert::TryInto<::std::string::String>, + { + self.page_token = value.try_into().map(Some).map_err(|_| { + "conversion to `:: std :: string :: String` for page_token failed".to_string() + }); + self + } + + pub fn sort_by(mut self, value: V) -> Self + where + V: std::convert::TryInto, + { + self.sort_by = value + .try_into() + .map(Some) + .map_err(|_| "conversion to `IdSortMode` for sort_by failed".to_string()); + self + } + + /// Sends a `GET` request to `/v1/users/{user_id}/sessions` + pub async fn send( + self, + ) -> Result, Error> { + let Self { + client, + user_id, + limit, + page_token, + sort_by, + } = self; + let user_id = user_id.map_err(Error::InvalidRequest)?; + let limit = limit.map_err(Error::InvalidRequest)?; + let page_token = page_token.map_err(Error::InvalidRequest)?; + let sort_by = sort_by.map_err(Error::InvalidRequest)?; + let url = format!( + "{}/v1/users/{}/sessions", + client.baseurl, + encode_path(&user_id.to_string()), + ); + let mut header_map = ::reqwest::header::HeaderMap::with_capacity(1usize); + header_map.append( + ::reqwest::header::HeaderName::from_static("api-version"), + ::reqwest::header::HeaderValue::from_static(super::Client::api_version()), + ); + #[allow(unused_mut)] + let mut request = client + .client + .get(url) + .header( + ::reqwest::header::ACCEPT, + ::reqwest::header::HeaderValue::from_static("application/json"), + ) + .query(&progenitor_client::QueryParam::new("limit", &limit)) + .query(&progenitor_client::QueryParam::new( + "page_token", + &page_token, + )) + .query(&progenitor_client::QueryParam::new("sort_by", &sort_by)) + .headers(header_map) + .build()?; + let info = OperationInfo { + operation_id: "user_session_list", + }; + client.pre(&mut request, &info).await?; + let result = client.exec(request, &info).await; + client.post(&result, &info).await?; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + 400u16..=499u16 => Err(Error::ErrorResponse( + ResponseValue::from_response(response).await?, + )), + 500u16..=599u16 => Err(Error::ErrorResponse( + ResponseValue::from_response(response).await?, + )), + _ => Err(Error::UnexpectedResponse(response)), + } + } + + /// Streams `GET` requests to `/v1/users/{user_id}/sessions` + pub fn stream( + self, + ) -> impl futures::Stream>> + Unpin + 'a + { + use ::futures::StreamExt; + use ::futures::TryFutureExt; + use ::futures::TryStreamExt; + let next = Self { + page_token: Ok(None), + sort_by: Ok(None), + ..self.clone() + }; + self.send() + .map_ok(move |page| { + let page = page.into_inner(); + let first = futures::stream::iter(page.items).map(Ok); + let rest = futures::stream::try_unfold( + (page.next_page, next), + |(next_page, next)| async { + if next_page.is_none() { + Ok(None) + } else { + Self { + page_token: Ok(next_page), + ..next.clone() + } + .send() + .map_ok(|page| { + let page = page.into_inner(); + Some(( + futures::stream::iter(page.items).map(Ok), + (page.next_page, next), + )) + }) + .await + } + }, + ) + .try_flatten(); + first.chain(rest) + }) + .try_flatten_stream() + .boxed() + } + } + /// Builder for [`ClientSilosExt::utilization_view`] /// /// [`ClientSilosExt::utilization_view`]: super::ClientSilosExt::utilization_view From 6cccbbf7b54016a14510352f75b1b96e4d126d29 Mon Sep 17 00:00:00 2001 From: "oxide-reflector-bot[bot]" <130185838+oxide-reflector-bot[bot]@users.noreply.github.com> Date: Fri, 18 Jul 2025 00:14:44 +0000 Subject: [PATCH 4/9] Rebuilt with latest dependency updates --- oxide.json | 4 ++-- sdk/src/generated_sdk.rs | 15 ++++++++------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/oxide.json b/oxide.json index 44f3e1fe..64660356 100644 --- a/oxide.json +++ b/oxide.json @@ -22549,7 +22549,7 @@ }, "rib_priority": { "nullable": true, - "description": "Local preference for route. Higher preference indictes precedence within and across protocols.", + "description": "Route RIB priority. Higher priority indicates precedence within and across protocols.", "type": "integer", "format": "uint8", "minimum": 0 @@ -24825,7 +24825,7 @@ }, "rib_priority": { "nullable": true, - "description": "RIB Priority indicating priority within and across protocols.", + "description": "Route RIB priority. Higher priority indicates precedence within and across protocols.", "type": "integer", "format": "uint8", "minimum": 0 diff --git a/sdk/src/generated_sdk.rs b/sdk/src/generated_sdk.rs index c6e5836a..b434be6c 100644 --- a/sdk/src/generated_sdk.rs +++ b/sdk/src/generated_sdk.rs @@ -21881,8 +21881,8 @@ pub mod types { /// "format": "ip" /// }, /// "rib_priority": { - /// "description": "Local preference for route. Higher preference - /// indictes precedence within and across protocols.", + /// "description": "Route RIB priority. Higher priority indicates + /// precedence within and across protocols.", /// "type": [ /// "integer", /// "null" @@ -21911,8 +21911,8 @@ pub mod types { pub dst: IpNet, /// The route gateway. pub gw: ::std::net::IpAddr, - /// Local preference for route. Higher preference indictes precedence - /// within and across protocols. + /// Route RIB priority. Higher priority indicates precedence within and + /// across protocols. #[serde(default, skip_serializing_if = "::std::option::Option::is_none")] pub rib_priority: ::std::option::Option, /// VLAN id the gateway is reachable over. @@ -27535,8 +27535,8 @@ pub mod types { /// "format": "uuid" /// }, /// "rib_priority": { - /// "description": "RIB Priority indicating priority within and across - /// protocols.", + /// "description": "Route RIB priority. Higher priority indicates + /// precedence within and across protocols.", /// "type": [ /// "integer", /// "null" @@ -27570,7 +27570,8 @@ pub mod types { pub interface_name: Name, /// The port settings object this route configuration belongs to. pub port_settings_id: ::uuid::Uuid, - /// RIB Priority indicating priority within and across protocols. + /// Route RIB priority. Higher priority indicates precedence within and + /// across protocols. #[serde(default, skip_serializing_if = "::std::option::Option::is_none")] pub rib_priority: ::std::option::Option, /// The VLAN identifier for the route. Use this if the gateway is From 623c0109615f576737abb70cc83ebf44e9f3684f Mon Sep 17 00:00:00 2001 From: "oxide-reflector-bot[bot]" <130185838+oxide-reflector-bot[bot]@users.noreply.github.com> Date: Fri, 18 Jul 2025 20:51:56 +0000 Subject: [PATCH 5/9] Rebuilt with latest dependency updates --- cli/src/generated_cli.rs | 113 +++++++- oxide.json | 81 ++++++ sdk-httpmock/src/generated_httpmock.rs | 85 ++++++ sdk/src/generated_sdk.rs | 378 ++++++++++++++++++++++++- 4 files changed, 654 insertions(+), 3 deletions(-) diff --git a/cli/src/generated_cli.rs b/cli/src/generated_cli.rs index 7af9456a..ea382797 100644 --- a/cli/src/generated_cli.rs +++ b/cli/src/generated_cli.rs @@ -23,6 +23,7 @@ impl Cli { CliCommand::SupportBundleList => Self::cli_support_bundle_list(), CliCommand::SupportBundleCreate => Self::cli_support_bundle_create(), CliCommand::SupportBundleView => Self::cli_support_bundle_view(), + CliCommand::SupportBundleUpdate => Self::cli_support_bundle_update(), CliCommand::SupportBundleDelete => Self::cli_support_bundle_delete(), CliCommand::SupportBundleDownload => Self::cli_support_bundle_download(), CliCommand::SupportBundleHead => Self::cli_support_bundle_head(), @@ -611,7 +612,29 @@ impl Cli { } pub fn cli_support_bundle_create() -> ::clap::Command { - ::clap::Command::new("").about("Create a new support bundle") + ::clap::Command::new("") + .arg( + ::clap::Arg::new("user-comment") + .long("user-comment") + .value_parser(::clap::value_parser!(::std::string::String)) + .required(false) + .help("User comment for the support bundle"), + ) + .arg( + ::clap::Arg::new("json-body") + .long("json-body") + .value_name("JSON-FILE") + .required(false) + .value_parser(::clap::value_parser!(std::path::PathBuf)) + .help("Path to a file that contains the full json body."), + ) + .arg( + ::clap::Arg::new("json-body-template") + .long("json-body-template") + .action(::clap::ArgAction::SetTrue) + .help("XXX"), + ) + .about("Create a new support bundle") } pub fn cli_support_bundle_view() -> ::clap::Command { @@ -626,6 +649,39 @@ impl Cli { .about("View a support bundle") } + pub fn cli_support_bundle_update() -> ::clap::Command { + ::clap::Command::new("") + .arg( + ::clap::Arg::new("bundle-id") + .long("bundle-id") + .value_parser(::clap::value_parser!(::uuid::Uuid)) + .required(true) + .help("ID of the support bundle"), + ) + .arg( + ::clap::Arg::new("user-comment") + .long("user-comment") + .value_parser(::clap::value_parser!(::std::string::String)) + .required(false) + .help("User comment for the support bundle"), + ) + .arg( + ::clap::Arg::new("json-body") + .long("json-body") + .value_name("JSON-FILE") + .required(false) + .value_parser(::clap::value_parser!(std::path::PathBuf)) + .help("Path to a file that contains the full json body."), + ) + .arg( + ::clap::Arg::new("json-body-template") + .long("json-body-template") + .action(::clap::ArgAction::SetTrue) + .help("XXX"), + ) + .about("Update a support bundle") + } + pub fn cli_support_bundle_delete() -> ::clap::Command { ::clap::Command::new("") .arg( @@ -8367,6 +8423,7 @@ impl Cli { CliCommand::SupportBundleList => self.execute_support_bundle_list(matches).await, CliCommand::SupportBundleCreate => self.execute_support_bundle_create(matches).await, CliCommand::SupportBundleView => self.execute_support_bundle_view(matches).await, + CliCommand::SupportBundleUpdate => self.execute_support_bundle_update(matches).await, CliCommand::SupportBundleDelete => self.execute_support_bundle_delete(matches).await, CliCommand::SupportBundleDownload => { self.execute_support_bundle_download(matches).await @@ -9137,6 +9194,16 @@ impl Cli { matches: &::clap::ArgMatches, ) -> anyhow::Result<()> { let mut request = self.client.support_bundle_create(); + if let Some(value) = matches.get_one::<::std::string::String>("user-comment") { + request = request.body_map(|body| body.user_comment(value.clone())) + } + + if let Some(value) = matches.get_one::("json-body") { + let body_txt = std::fs::read_to_string(value).unwrap(); + let body_value = serde_json::from_str::(&body_txt).unwrap(); + request = request.body(body_value); + } + self.config .execute_support_bundle_create(matches, &mut request)?; let result = request.send().await; @@ -9176,6 +9243,40 @@ impl Cli { } } + pub async fn execute_support_bundle_update( + &self, + matches: &::clap::ArgMatches, + ) -> anyhow::Result<()> { + let mut request = self.client.support_bundle_update(); + if let Some(value) = matches.get_one::<::uuid::Uuid>("bundle-id") { + request = request.bundle_id(value.clone()); + } + + if let Some(value) = matches.get_one::<::std::string::String>("user-comment") { + request = request.body_map(|body| body.user_comment(value.clone())) + } + + if let Some(value) = matches.get_one::("json-body") { + let body_txt = std::fs::read_to_string(value).unwrap(); + let body_value = serde_json::from_str::(&body_txt).unwrap(); + request = request.body(body_value); + } + + self.config + .execute_support_bundle_update(matches, &mut request)?; + let result = request.send().await; + match result { + Ok(r) => { + self.config.success_item(&r); + Ok(()) + } + Err(r) => { + self.config.error(&r); + Err(anyhow::Error::new(r)) + } + } + } + pub async fn execute_support_bundle_delete( &self, matches: &::clap::ArgMatches, @@ -18206,6 +18307,14 @@ pub trait CliConfig { Ok(()) } + fn execute_support_bundle_update( + &self, + matches: &::clap::ArgMatches, + request: &mut builder::SupportBundleUpdate, + ) -> anyhow::Result<()> { + Ok(()) + } + fn execute_support_bundle_delete( &self, matches: &::clap::ArgMatches, @@ -20307,6 +20416,7 @@ pub enum CliCommand { SupportBundleList, SupportBundleCreate, SupportBundleView, + SupportBundleUpdate, SupportBundleDelete, SupportBundleDownload, SupportBundleHead, @@ -20583,6 +20693,7 @@ impl CliCommand { CliCommand::SupportBundleList, CliCommand::SupportBundleCreate, CliCommand::SupportBundleView, + CliCommand::SupportBundleUpdate, CliCommand::SupportBundleDelete, CliCommand::SupportBundleDownload, CliCommand::SupportBundleHead, diff --git a/oxide.json b/oxide.json index 64660356..bd91bcc6 100644 --- a/oxide.json +++ b/oxide.json @@ -365,6 +365,16 @@ ], "summary": "Create a new support bundle", "operationId": "support_bundle_create", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SupportBundleCreate" + } + } + }, + "required": true + }, "responses": { "201": { "description": "successful creation", @@ -423,6 +433,53 @@ } } }, + "put": { + "tags": [ + "experimental" + ], + "summary": "Update a support bundle", + "operationId": "support_bundle_update", + "parameters": [ + { + "in": "path", + "name": "bundle_id", + "description": "ID of the support bundle", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SupportBundleUpdate" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SupportBundleInfo" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + }, "delete": { "tags": [ "experimental" @@ -24173,6 +24230,16 @@ "items" ] }, + "SupportBundleCreate": { + "type": "object", + "properties": { + "user_comment": { + "nullable": true, + "description": "User comment for the support bundle", + "type": "string" + } + } + }, "SupportBundleInfo": { "type": "object", "properties": { @@ -24192,6 +24259,10 @@ "time_created": { "type": "string", "format": "date-time" + }, + "user_comment": { + "nullable": true, + "type": "string" } }, "required": [ @@ -24254,6 +24325,16 @@ } ] }, + "SupportBundleUpdate": { + "type": "object", + "properties": { + "user_comment": { + "nullable": true, + "description": "User comment for the support bundle", + "type": "string" + } + } + }, "Switch": { "description": "An operator's view of a Switch.", "type": "object", diff --git a/sdk-httpmock/src/generated_httpmock.rs b/sdk-httpmock/src/generated_httpmock.rs index 8948234a..0ca6d417 100644 --- a/sdk-httpmock/src/generated_httpmock.rs +++ b/sdk-httpmock/src/generated_httpmock.rs @@ -557,6 +557,10 @@ pub mod operations { pub fn into_inner(self) -> ::httpmock::When { self.0 } + + pub fn body(self, value: &types::SupportBundleCreate) -> Self { + Self(self.0.json_body_obj(value)) + } } pub struct SupportBundleCreateThen(::httpmock::Then); @@ -661,6 +665,72 @@ pub mod operations { } } + pub struct SupportBundleUpdateWhen(::httpmock::When); + impl SupportBundleUpdateWhen { + pub fn new(inner: ::httpmock::When) -> Self { + Self(inner.method(::httpmock::Method::PUT).path_matches( + regex::Regex::new("^/experimental/v1/system/support-bundles/[^/]*$").unwrap(), + )) + } + + pub fn into_inner(self) -> ::httpmock::When { + self.0 + } + + pub fn bundle_id(self, value: &::uuid::Uuid) -> Self { + let re = regex::Regex::new(&format!( + "^/experimental/v1/system/support-bundles/{}$", + value.to_string() + )) + .unwrap(); + Self(self.0.path_matches(re)) + } + + pub fn body(self, value: &types::SupportBundleUpdate) -> Self { + Self(self.0.json_body_obj(value)) + } + } + + pub struct SupportBundleUpdateThen(::httpmock::Then); + impl SupportBundleUpdateThen { + pub fn new(inner: ::httpmock::Then) -> Self { + Self(inner) + } + + pub fn into_inner(self) -> ::httpmock::Then { + self.0 + } + + pub fn ok(self, value: &types::SupportBundleInfo) -> Self { + Self( + self.0 + .status(200u16) + .header("content-type", "application/json") + .json_body_obj(value), + ) + } + + pub fn client_error(self, status: u16, value: &types::Error) -> Self { + assert_eq!(status / 100u16, 4u16); + Self( + self.0 + .status(status) + .header("content-type", "application/json") + .json_body_obj(value), + ) + } + + pub fn server_error(self, status: u16, value: &types::Error) -> Self { + assert_eq!(status / 100u16, 5u16); + Self( + self.0 + .status(status) + .header("content-type", "application/json") + .json_body_obj(value), + ) + } + } + pub struct SupportBundleDeleteWhen(::httpmock::When); impl SupportBundleDeleteWhen { pub fn new(inner: ::httpmock::When) -> Self { @@ -21962,6 +22032,9 @@ pub trait MockServerExt { fn support_bundle_view(&self, config_fn: F) -> ::httpmock::Mock where F: FnOnce(operations::SupportBundleViewWhen, operations::SupportBundleViewThen); + fn support_bundle_update(&self, config_fn: F) -> ::httpmock::Mock + where + F: FnOnce(operations::SupportBundleUpdateWhen, operations::SupportBundleUpdateThen); fn support_bundle_delete(&self, config_fn: F) -> ::httpmock::Mock where F: FnOnce(operations::SupportBundleDeleteWhen, operations::SupportBundleDeleteThen); @@ -23081,6 +23154,18 @@ impl MockServerExt for ::httpmock::MockServer { }) } + fn support_bundle_update(&self, config_fn: F) -> ::httpmock::Mock + where + F: FnOnce(operations::SupportBundleUpdateWhen, operations::SupportBundleUpdateThen), + { + self.mock(|when, then| { + config_fn( + operations::SupportBundleUpdateWhen::new(when), + operations::SupportBundleUpdateThen::new(then), + ) + }) + } + fn support_bundle_delete(&self, config_fn: F) -> ::httpmock::Mock where F: FnOnce(operations::SupportBundleDeleteWhen, operations::SupportBundleDeleteThen), diff --git a/sdk/src/generated_sdk.rs b/sdk/src/generated_sdk.rs index b434be6c..915e9785 100644 --- a/sdk/src/generated_sdk.rs +++ b/sdk/src/generated_sdk.rs @@ -25848,6 +25848,54 @@ pub mod types { } } + /// `SupportBundleCreate` + /// + ///
JSON schema + /// + /// ```json + /// { + /// "type": "object", + /// "properties": { + /// "user_comment": { + /// "description": "User comment for the support bundle", + /// "type": [ + /// "string", + /// "null" + /// ] + /// } + /// } + /// } + /// ``` + ///
+ #[derive( + :: serde :: Deserialize, :: serde :: Serialize, Clone, Debug, schemars :: JsonSchema, + )] + pub struct SupportBundleCreate { + /// User comment for the support bundle + #[serde(default, skip_serializing_if = "::std::option::Option::is_none")] + pub user_comment: ::std::option::Option<::std::string::String>, + } + + impl ::std::convert::From<&SupportBundleCreate> for SupportBundleCreate { + fn from(value: &SupportBundleCreate) -> Self { + value.clone() + } + } + + impl ::std::default::Default for SupportBundleCreate { + fn default() -> Self { + Self { + user_comment: Default::default(), + } + } + } + + impl SupportBundleCreate { + pub fn builder() -> builder::SupportBundleCreate { + Default::default() + } + } + /// `SupportBundleInfo` /// ///
JSON schema @@ -25880,6 +25928,12 @@ pub mod types { /// "time_created": { /// "type": "string", /// "format": "date-time" + /// }, + /// "user_comment": { + /// "type": [ + /// "string", + /// "null" + /// ] /// } /// } /// } @@ -25895,6 +25949,8 @@ pub mod types { pub reason_for_failure: ::std::option::Option<::std::string::String>, pub state: SupportBundleState, pub time_created: ::chrono::DateTime<::chrono::offset::Utc>, + #[serde(default, skip_serializing_if = "::std::option::Option::is_none")] + pub user_comment: ::std::option::Option<::std::string::String>, } impl ::std::convert::From<&SupportBundleInfo> for SupportBundleInfo { @@ -26107,6 +26163,54 @@ pub mod types { } } + /// `SupportBundleUpdate` + /// + ///
JSON schema + /// + /// ```json + /// { + /// "type": "object", + /// "properties": { + /// "user_comment": { + /// "description": "User comment for the support bundle", + /// "type": [ + /// "string", + /// "null" + /// ] + /// } + /// } + /// } + /// ``` + ///
+ #[derive( + :: serde :: Deserialize, :: serde :: Serialize, Clone, Debug, schemars :: JsonSchema, + )] + pub struct SupportBundleUpdate { + /// User comment for the support bundle + #[serde(default, skip_serializing_if = "::std::option::Option::is_none")] + pub user_comment: ::std::option::Option<::std::string::String>, + } + + impl ::std::convert::From<&SupportBundleUpdate> for SupportBundleUpdate { + fn from(value: &SupportBundleUpdate) -> Self { + value.clone() + } + } + + impl ::std::default::Default for SupportBundleUpdate { + fn default() -> Self { + Self { + user_comment: Default::default(), + } + } + } + + impl SupportBundleUpdate { + pub fn builder() -> builder::SupportBundleUpdate { + Default::default() + } + } + /// An operator's view of a Switch. /// ///
JSON schema @@ -53989,6 +54093,54 @@ pub mod types { } } + #[derive(Clone, Debug)] + pub struct SupportBundleCreate { + user_comment: ::std::result::Result< + ::std::option::Option<::std::string::String>, + ::std::string::String, + >, + } + + impl ::std::default::Default for SupportBundleCreate { + fn default() -> Self { + Self { + user_comment: Ok(Default::default()), + } + } + } + + impl SupportBundleCreate { + pub fn user_comment(mut self, value: T) -> Self + where + T: ::std::convert::TryInto<::std::option::Option<::std::string::String>>, + T::Error: ::std::fmt::Display, + { + self.user_comment = value.try_into().map_err(|e| { + format!("error converting supplied value for user_comment: {}", e) + }); + self + } + } + + impl ::std::convert::TryFrom for super::SupportBundleCreate { + type Error = super::error::ConversionError; + fn try_from( + value: SupportBundleCreate, + ) -> ::std::result::Result { + Ok(Self { + user_comment: value.user_comment?, + }) + } + } + + impl ::std::convert::From for SupportBundleCreate { + fn from(value: super::SupportBundleCreate) -> Self { + Self { + user_comment: Ok(value.user_comment), + } + } + } + #[derive(Clone, Debug)] pub struct SupportBundleInfo { id: ::std::result::Result, @@ -54003,6 +54155,10 @@ pub mod types { ::chrono::DateTime<::chrono::offset::Utc>, ::std::string::String, >, + user_comment: ::std::result::Result< + ::std::option::Option<::std::string::String>, + ::std::string::String, + >, } impl ::std::default::Default for SupportBundleInfo { @@ -54015,6 +54171,7 @@ pub mod types { reason_for_failure: Ok(Default::default()), state: Err("no value supplied for state".to_string()), time_created: Err("no value supplied for time_created".to_string()), + user_comment: Ok(Default::default()), } } } @@ -54076,6 +54233,16 @@ pub mod types { }); self } + pub fn user_comment(mut self, value: T) -> Self + where + T: ::std::convert::TryInto<::std::option::Option<::std::string::String>>, + T::Error: ::std::fmt::Display, + { + self.user_comment = value.try_into().map_err(|e| { + format!("error converting supplied value for user_comment: {}", e) + }); + self + } } impl ::std::convert::TryFrom for super::SupportBundleInfo { @@ -54089,6 +54256,7 @@ pub mod types { reason_for_failure: value.reason_for_failure?, state: value.state?, time_created: value.time_created?, + user_comment: value.user_comment?, }) } } @@ -54101,6 +54269,7 @@ pub mod types { reason_for_failure: Ok(value.reason_for_failure), state: Ok(value.state), time_created: Ok(value.time_created), + user_comment: Ok(value.user_comment), } } } @@ -54170,6 +54339,54 @@ pub mod types { } } + #[derive(Clone, Debug)] + pub struct SupportBundleUpdate { + user_comment: ::std::result::Result< + ::std::option::Option<::std::string::String>, + ::std::string::String, + >, + } + + impl ::std::default::Default for SupportBundleUpdate { + fn default() -> Self { + Self { + user_comment: Ok(Default::default()), + } + } + } + + impl SupportBundleUpdate { + pub fn user_comment(mut self, value: T) -> Self + where + T: ::std::convert::TryInto<::std::option::Option<::std::string::String>>, + T::Error: ::std::fmt::Display, + { + self.user_comment = value.try_into().map_err(|e| { + format!("error converting supplied value for user_comment: {}", e) + }); + self + } + } + + impl ::std::convert::TryFrom for super::SupportBundleUpdate { + type Error = super::error::ConversionError; + fn try_from( + value: SupportBundleUpdate, + ) -> ::std::result::Result { + Ok(Self { + user_comment: value.user_comment?, + }) + } + } + + impl ::std::convert::From for SupportBundleUpdate { + fn from(value: super::SupportBundleUpdate) -> Self { + Self { + user_comment: Ok(value.user_comment), + } + } + } + #[derive(Clone, Debug)] pub struct Switch { baseboard: ::std::result::Result, @@ -61205,6 +61422,7 @@ pub trait ClientExperimentalExt { /// /// ```ignore /// let response = client.support_bundle_create() + /// .body(body) /// .send() /// .await; /// ``` @@ -61223,6 +61441,22 @@ pub trait ClientExperimentalExt { /// .await; /// ``` fn support_bundle_view(&self) -> builder::SupportBundleView; + /// Update a support bundle + /// + /// Sends a `PUT` request to + /// `/experimental/v1/system/support-bundles/{bundle_id}` + /// + /// Arguments: + /// - `bundle_id`: ID of the support bundle + /// - `body` + /// ```ignore + /// let response = client.support_bundle_update() + /// .bundle_id(bundle_id) + /// .body(body) + /// .send() + /// .await; + /// ``` + fn support_bundle_update(&self) -> builder::SupportBundleUpdate; /// Delete an existing support bundle /// /// May also be used to cancel a support bundle which is currently being @@ -61695,6 +61929,10 @@ impl ClientExperimentalExt for Client { builder::SupportBundleView::new(self) } + fn support_bundle_update(&self) -> builder::SupportBundleUpdate { + builder::SupportBundleUpdate::new(self) + } + fn support_bundle_delete(&self) -> builder::SupportBundleDelete { builder::SupportBundleDelete::new(self) } @@ -67225,18 +67463,47 @@ pub mod builder { #[derive(Debug, Clone)] pub struct SupportBundleCreate<'a> { client: &'a super::Client, + body: Result, } impl<'a> SupportBundleCreate<'a> { pub fn new(client: &'a super::Client) -> Self { - Self { client: client } + Self { + client: client, + body: Ok(::std::default::Default::default()), + } + } + + pub fn body(mut self, value: V) -> Self + where + V: std::convert::TryInto, + >::Error: std::fmt::Display, + { + self.body = value + .try_into() + .map(From::from) + .map_err(|s| format!("conversion to `SupportBundleCreate` for body failed: {}", s)); + self + } + + pub fn body_map(mut self, f: F) -> Self + where + F: std::ops::FnOnce( + types::builder::SupportBundleCreate, + ) -> types::builder::SupportBundleCreate, + { + self.body = self.body.map(f); + self } /// Sends a `POST` request to `/experimental/v1/system/support-bundles` pub async fn send( self, ) -> Result, Error> { - let Self { client } = self; + let Self { client, body } = self; + let body = body + .and_then(|v| types::SupportBundleCreate::try_from(v).map_err(|e| e.to_string())) + .map_err(Error::InvalidRequest)?; let url = format!("{}/experimental/v1/system/support-bundles", client.baseurl,); let mut header_map = ::reqwest::header::HeaderMap::with_capacity(1usize); header_map.append( @@ -67251,6 +67518,7 @@ pub mod builder { ::reqwest::header::ACCEPT, ::reqwest::header::HeaderValue::from_static("application/json"), ) + .json(&body) .headers(header_map) .build()?; let info = OperationInfo { @@ -67347,6 +67615,112 @@ pub mod builder { } } + /// Builder for [`ClientExperimentalExt::support_bundle_update`] + /// + /// [`ClientExperimentalExt::support_bundle_update`]: super::ClientExperimentalExt::support_bundle_update + #[derive(Debug, Clone)] + pub struct SupportBundleUpdate<'a> { + client: &'a super::Client, + bundle_id: Result<::uuid::Uuid, String>, + body: Result, + } + + impl<'a> SupportBundleUpdate<'a> { + pub fn new(client: &'a super::Client) -> Self { + Self { + client: client, + bundle_id: Err("bundle_id was not initialized".to_string()), + body: Ok(::std::default::Default::default()), + } + } + + pub fn bundle_id(mut self, value: V) -> Self + where + V: std::convert::TryInto<::uuid::Uuid>, + { + self.bundle_id = value + .try_into() + .map_err(|_| "conversion to `:: uuid :: Uuid` for bundle_id failed".to_string()); + self + } + + pub fn body(mut self, value: V) -> Self + where + V: std::convert::TryInto, + >::Error: std::fmt::Display, + { + self.body = value + .try_into() + .map(From::from) + .map_err(|s| format!("conversion to `SupportBundleUpdate` for body failed: {}", s)); + self + } + + pub fn body_map(mut self, f: F) -> Self + where + F: std::ops::FnOnce( + types::builder::SupportBundleUpdate, + ) -> types::builder::SupportBundleUpdate, + { + self.body = self.body.map(f); + self + } + + /// Sends a `PUT` request to + /// `/experimental/v1/system/support-bundles/{bundle_id}` + pub async fn send( + self, + ) -> Result, Error> { + let Self { + client, + bundle_id, + body, + } = self; + let bundle_id = bundle_id.map_err(Error::InvalidRequest)?; + let body = body + .and_then(|v| types::SupportBundleUpdate::try_from(v).map_err(|e| e.to_string())) + .map_err(Error::InvalidRequest)?; + let url = format!( + "{}/experimental/v1/system/support-bundles/{}", + client.baseurl, + encode_path(&bundle_id.to_string()), + ); + let mut header_map = ::reqwest::header::HeaderMap::with_capacity(1usize); + header_map.append( + ::reqwest::header::HeaderName::from_static("api-version"), + ::reqwest::header::HeaderValue::from_static(super::Client::api_version()), + ); + #[allow(unused_mut)] + let mut request = client + .client + .put(url) + .header( + ::reqwest::header::ACCEPT, + ::reqwest::header::HeaderValue::from_static("application/json"), + ) + .json(&body) + .headers(header_map) + .build()?; + let info = OperationInfo { + operation_id: "support_bundle_update", + }; + client.pre(&mut request, &info).await?; + let result = client.exec(request, &info).await; + client.post(&result, &info).await?; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + 400u16..=499u16 => Err(Error::ErrorResponse( + ResponseValue::from_response(response).await?, + )), + 500u16..=599u16 => Err(Error::ErrorResponse( + ResponseValue::from_response(response).await?, + )), + _ => Err(Error::UnexpectedResponse(response)), + } + } + } + /// Builder for [`ClientExperimentalExt::support_bundle_delete`] /// /// [`ClientExperimentalExt::support_bundle_delete`]: super::ClientExperimentalExt::support_bundle_delete From 696affe9eca74afc89830e8ca036db0df9ba3ec7 Mon Sep 17 00:00:00 2001 From: "oxide-reflector-bot[bot]" <130185838+oxide-reflector-bot[bot]@users.noreply.github.com> Date: Thu, 24 Jul 2025 21:41:17 +0000 Subject: [PATCH 6/9] Rebuilt with latest dependency updates --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4a28eb0a..1061c6b7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2822,7 +2822,7 @@ dependencies = [ [[package]] name = "progenitor" version = "0.11.0" -source = "git+https://github.com/oxidecomputer/progenitor#d4f449fe79b0507fe5dc8f3eca3465623c19d0e6" +source = "git+https://github.com/oxidecomputer/progenitor#e3de5fd0ec6423b3f456bd10bee39f1a91e1238d" dependencies = [ "progenitor-impl", ] @@ -2845,7 +2845,7 @@ dependencies = [ [[package]] name = "progenitor-impl" version = "0.11.0" -source = "git+https://github.com/oxidecomputer/progenitor#d4f449fe79b0507fe5dc8f3eca3465623c19d0e6" +source = "git+https://github.com/oxidecomputer/progenitor#e3de5fd0ec6423b3f456bd10bee39f1a91e1238d" dependencies = [ "heck", "http 1.3.1", From 424e78ef3b0a8f1fa9d635c6b83203a399bd713a Mon Sep 17 00:00:00 2001 From: "oxide-reflector-bot[bot]" <130185838+oxide-reflector-bot[bot]@users.noreply.github.com> Date: Wed, 30 Jul 2025 02:40:00 +0000 Subject: [PATCH 7/9] Rebuilt with latest dependency updates --- oxide.json | 41 ++++++++++++++++++++++++++++ sdk/src/generated_sdk.rs | 58 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+) diff --git a/oxide.json b/oxide.json index bd91bcc6..360ed34a 100644 --- a/oxide.json +++ b/oxide.json @@ -17885,6 +17885,47 @@ }, "ExternalIp": { "oneOf": [ + { + "description": "A source NAT IP address.\n\nSNAT addresses are ephemeral addresses used only for outbound connectivity.", + "type": "object", + "properties": { + "first_port": { + "description": "The first usable port within the IP address.", + "type": "integer", + "format": "uint16", + "minimum": 0 + }, + "ip": { + "description": "The IP address.", + "type": "string", + "format": "ip" + }, + "ip_pool_id": { + "description": "ID of the IP Pool from which the address is taken.", + "type": "string", + "format": "uuid" + }, + "kind": { + "type": "string", + "enum": [ + "snat" + ] + }, + "last_port": { + "description": "The last usable port within the IP address.", + "type": "integer", + "format": "uint16", + "minimum": 0 + } + }, + "required": [ + "first_port", + "ip", + "ip_pool_id", + "kind", + "last_port" + ] + }, { "type": "object", "properties": { diff --git a/sdk/src/generated_sdk.rs b/sdk/src/generated_sdk.rs index 915e9785..26b154ca 100644 --- a/sdk/src/generated_sdk.rs +++ b/sdk/src/generated_sdk.rs @@ -9641,6 +9641,49 @@ pub mod types { /// { /// "oneOf": [ /// { + /// "description": "A source NAT IP address.\n\nSNAT addresses are + /// ephemeral addresses used only for outbound connectivity.", + /// "type": "object", + /// "required": [ + /// "first_port", + /// "ip", + /// "ip_pool_id", + /// "kind", + /// "last_port" + /// ], + /// "properties": { + /// "first_port": { + /// "description": "The first usable port within the IP address.", + /// "type": "integer", + /// "format": "uint16", + /// "minimum": 0.0 + /// }, + /// "ip": { + /// "description": "The IP address.", + /// "type": "string", + /// "format": "ip" + /// }, + /// "ip_pool_id": { + /// "description": "ID of the IP Pool from which the address is + /// taken.", + /// "type": "string", + /// "format": "uuid" + /// }, + /// "kind": { + /// "type": "string", + /// "enum": [ + /// "snat" + /// ] + /// }, + /// "last_port": { + /// "description": "The last usable port within the IP address.", + /// "type": "integer", + /// "format": "uint16", + /// "minimum": 0.0 + /// } + /// } + /// }, + /// { /// "type": "object", /// "required": [ /// "ip", @@ -9753,6 +9796,21 @@ pub mod types { )] #[serde(tag = "kind")] pub enum ExternalIp { + /// A source NAT IP address. + /// + /// SNAT addresses are ephemeral addresses used only for outbound + /// connectivity. + #[serde(rename = "snat")] + Snat { + /// The first usable port within the IP address. + first_port: u16, + /// The IP address. + ip: ::std::net::IpAddr, + /// ID of the IP Pool from which the address is taken. + ip_pool_id: ::uuid::Uuid, + /// The last usable port within the IP address. + last_port: u16, + }, #[serde(rename = "ephemeral")] Ephemeral { ip: ::std::net::IpAddr, From 94bafd2aa179ab4bebf102fe8ca97d95bb46ca8a Mon Sep 17 00:00:00 2001 From: "oxide-reflector-bot[bot]" <130185838+oxide-reflector-bot[bot]@users.noreply.github.com> Date: Wed, 30 Jul 2025 22:40:25 +0000 Subject: [PATCH 8/9] Rebuilt with latest dependency updates --- cli/src/generated_cli.rs | 148 ---------- oxide.json | 132 +-------- sdk-httpmock/src/generated_httpmock.rs | 178 ------------ sdk/src/generated_sdk.rs | 379 ------------------------- 4 files changed, 8 insertions(+), 829 deletions(-) diff --git a/cli/src/generated_cli.rs b/cli/src/generated_cli.rs index ea382797..3d4c7ad8 100644 --- a/cli/src/generated_cli.rs +++ b/cli/src/generated_cli.rs @@ -86,7 +86,6 @@ impl Cli { CliCommand::DiskBulkWriteImportStart => Self::cli_disk_bulk_write_import_start(), CliCommand::DiskBulkWriteImportStop => Self::cli_disk_bulk_write_import_stop(), CliCommand::DiskFinalizeImport => Self::cli_disk_finalize_import(), - CliCommand::DiskMetricsList => Self::cli_disk_metrics_list(), CliCommand::FloatingIpList => Self::cli_floating_ip_list(), CliCommand::FloatingIpCreate => Self::cli_floating_ip_create(), CliCommand::FloatingIpView => Self::cli_floating_ip_view(), @@ -1965,78 +1964,6 @@ impl Cli { .about("Confirm disk block import completion") } - pub fn cli_disk_metrics_list() -> ::clap::Command { - ::clap::Command::new("") - .arg( - ::clap::Arg::new("disk") - .long("disk") - .value_parser(::clap::value_parser!(types::NameOrId)) - .required(true), - ) - .arg( - ::clap::Arg::new("end-time") - .long("end-time") - .value_parser(::clap::value_parser!( - ::chrono::DateTime<::chrono::offset::Utc> - )) - .required(true) - .help("An exclusive end time of metrics."), - ) - .arg( - ::clap::Arg::new("limit") - .long("limit") - .value_parser(::clap::value_parser!(::std::num::NonZeroU32)) - .required(false) - .help("Maximum number of items returned by a single call"), - ) - .arg( - ::clap::Arg::new("metric") - .long("metric") - .value_parser(::clap::builder::TypedValueParser::map( - ::clap::builder::PossibleValuesParser::new([ - types::DiskMetricName::Activated.to_string(), - types::DiskMetricName::Flush.to_string(), - types::DiskMetricName::Read.to_string(), - types::DiskMetricName::ReadBytes.to_string(), - types::DiskMetricName::Write.to_string(), - types::DiskMetricName::WriteBytes.to_string(), - ]), - |s| types::DiskMetricName::try_from(s).unwrap(), - )) - .required(true), - ) - .arg( - ::clap::Arg::new("order") - .long("order") - .value_parser(::clap::builder::TypedValueParser::map( - ::clap::builder::PossibleValuesParser::new([ - types::PaginationOrder::Ascending.to_string(), - types::PaginationOrder::Descending.to_string(), - ]), - |s| types::PaginationOrder::try_from(s).unwrap(), - )) - .required(false) - .help("Query result order"), - ) - .arg( - ::clap::Arg::new("project") - .long("project") - .value_parser(::clap::value_parser!(types::NameOrId)) - .required(false) - .help("Name or ID of the project"), - ) - .arg( - ::clap::Arg::new("start-time") - .long("start-time") - .value_parser(::clap::value_parser!( - ::chrono::DateTime<::chrono::offset::Utc> - )) - .required(true) - .help("An inclusive start time of metrics."), - ) - .about("Fetch disk metrics") - } - pub fn cli_floating_ip_list() -> ::clap::Command { ::clap::Command::new("") .arg( @@ -8519,7 +8446,6 @@ impl Cli { self.execute_disk_bulk_write_import_stop(matches).await } CliCommand::DiskFinalizeImport => self.execute_disk_finalize_import(matches).await, - CliCommand::DiskMetricsList => self.execute_disk_metrics_list(matches).await, CliCommand::FloatingIpList => self.execute_floating_ip_list(matches).await, CliCommand::FloatingIpCreate => self.execute_floating_ip_create(matches).await, CliCommand::FloatingIpView => self.execute_floating_ip_view(matches).await, @@ -10883,70 +10809,6 @@ impl Cli { } } - pub async fn execute_disk_metrics_list( - &self, - matches: &::clap::ArgMatches, - ) -> anyhow::Result<()> { - let mut request = self.client.disk_metrics_list(); - if let Some(value) = matches.get_one::("disk") { - request = request.disk(value.clone()); - } - - if let Some(value) = - matches.get_one::<::chrono::DateTime<::chrono::offset::Utc>>("end-time") - { - request = request.end_time(value.clone()); - } - - if let Some(value) = matches.get_one::<::std::num::NonZeroU32>("limit") { - request = request.limit(value.clone()); - } - - if let Some(value) = matches.get_one::("metric") { - request = request.metric(value.clone()); - } - - if let Some(value) = matches.get_one::("order") { - request = request.order(value.clone()); - } - - if let Some(value) = matches.get_one::("project") { - request = request.project(value.clone()); - } - - if let Some(value) = - matches.get_one::<::chrono::DateTime<::chrono::offset::Utc>>("start-time") - { - request = request.start_time(value.clone()); - } - - self.config - .execute_disk_metrics_list(matches, &mut request)?; - self.config.list_start::(); - let mut stream = futures::StreamExt::take( - request.stream(), - matches - .get_one::("limit") - .map_or(usize::MAX, |x| x.get() as usize), - ); - loop { - match futures::TryStreamExt::try_next(&mut stream).await { - Err(r) => { - self.config.list_end_error(&r); - return Err(anyhow::Error::new(r)); - } - Ok(None) => { - self.config - .list_end_success::(); - return Ok(()); - } - Ok(Some(value)) => { - self.config.list_item(&value); - } - } - } - } - pub async fn execute_floating_ip_list( &self, matches: &::clap::ArgMatches, @@ -18699,14 +18561,6 @@ pub trait CliConfig { Ok(()) } - fn execute_disk_metrics_list( - &self, - matches: &::clap::ArgMatches, - request: &mut builder::DiskMetricsList, - ) -> anyhow::Result<()> { - Ok(()) - } - fn execute_floating_ip_list( &self, matches: &::clap::ArgMatches, @@ -20465,7 +20319,6 @@ pub enum CliCommand { DiskBulkWriteImportStart, DiskBulkWriteImportStop, DiskFinalizeImport, - DiskMetricsList, FloatingIpList, FloatingIpCreate, FloatingIpView, @@ -20742,7 +20595,6 @@ impl CliCommand { CliCommand::DiskBulkWriteImportStart, CliCommand::DiskBulkWriteImportStop, CliCommand::DiskFinalizeImport, - CliCommand::DiskMetricsList, CliCommand::FloatingIpList, CliCommand::FloatingIpCreate, CliCommand::FloatingIpView, diff --git a/oxide.json b/oxide.json index 360ed34a..b7102303 100644 --- a/oxide.json +++ b/oxide.json @@ -2800,111 +2800,6 @@ } } }, - "/v1/disks/{disk}/metrics/{metric}": { - "get": { - "tags": [ - "disks" - ], - "summary": "Fetch disk metrics", - "operationId": "disk_metrics_list", - "parameters": [ - { - "in": "path", - "name": "disk", - "required": true, - "schema": { - "$ref": "#/components/schemas/NameOrId" - } - }, - { - "in": "path", - "name": "metric", - "required": true, - "schema": { - "$ref": "#/components/schemas/DiskMetricName" - } - }, - { - "in": "query", - "name": "end_time", - "description": "An exclusive end time of metrics.", - "schema": { - "type": "string", - "format": "date-time" - } - }, - { - "in": "query", - "name": "limit", - "description": "Maximum number of items returned by a single call", - "schema": { - "nullable": true, - "type": "integer", - "format": "uint32", - "minimum": 1 - } - }, - { - "in": "query", - "name": "order", - "description": "Query result order", - "schema": { - "$ref": "#/components/schemas/PaginationOrder" - } - }, - { - "in": "query", - "name": "page_token", - "description": "Token returned by previous call to retrieve the subsequent page", - "schema": { - "nullable": true, - "type": "string" - } - }, - { - "in": "query", - "name": "start_time", - "description": "An inclusive start time of metrics.", - "schema": { - "type": "string", - "format": "date-time" - } - }, - { - "in": "query", - "name": "project", - "description": "Name or ID of the project", - "schema": { - "$ref": "#/components/schemas/NameOrId" - } - } - ], - "responses": { - "200": { - "description": "successful operation", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/MeasurementResultsPage" - } - } - } - }, - "4XX": { - "$ref": "#/components/responses/Error" - }, - "5XX": { - "$ref": "#/components/responses/Error" - } - }, - "x-dropshot-pagination": { - "required": [ - "end_time", - "start_time" - ] - } - } - }, "/v1/floating-ips": { "get": { "tags": [ @@ -27543,25 +27438,6 @@ } ] }, - "DiskMetricName": { - "type": "string", - "enum": [ - "activated", - "flush", - "read", - "read_bytes", - "write", - "write_bytes" - ] - }, - "PaginationOrder": { - "description": "The order in which the client wants to page through the requested collection", - "type": "string", - "enum": [ - "ascending", - "descending" - ] - }, "IdSortMode": { "description": "Supported set of sort modes for scanning by id only.\n\nCurrently, we only support scanning in ascending order.", "oneOf": [ @@ -27582,6 +27458,14 @@ "ram_provisioned" ] }, + "PaginationOrder": { + "description": "The order in which the client wants to page through the requested collection", + "type": "string", + "enum": [ + "ascending", + "descending" + ] + }, "NameSortMode": { "description": "Supported set of sort modes for scanning by name only\n\nCurrently, we only support scanning in ascending order.", "oneOf": [ diff --git a/sdk-httpmock/src/generated_httpmock.rs b/sdk-httpmock/src/generated_httpmock.rs index 0ca6d417..bc50d4bf 100644 --- a/sdk-httpmock/src/generated_httpmock.rs +++ b/sdk-httpmock/src/generated_httpmock.rs @@ -4572,169 +4572,6 @@ pub mod operations { } } - pub struct DiskMetricsListWhen(::httpmock::When); - impl DiskMetricsListWhen { - pub fn new(inner: ::httpmock::When) -> Self { - Self( - inner - .method(::httpmock::Method::GET) - .path_matches(regex::Regex::new("^/v1/disks/[^/]*/metrics/[^/]*$").unwrap()), - ) - } - - pub fn into_inner(self) -> ::httpmock::When { - self.0 - } - - pub fn disk(self, value: &types::NameOrId) -> Self { - let re = regex::Regex::new(&format!("^/v1/disks/{}/metrics/.*$", value.to_string())) - .unwrap(); - Self(self.0.path_matches(re)) - } - - pub fn metric(self, value: types::DiskMetricName) -> Self { - let re = regex::Regex::new(&format!("^/v1/disks/.*/metrics/{}$", value.to_string())) - .unwrap(); - Self(self.0.path_matches(re)) - } - - pub fn end_time<'a, T>(self, value: T) -> Self - where - T: Into>>, - { - if let Some(value) = value.into() { - Self(self.0.query_param("end_time", value.to_string())) - } else { - Self(self.0.matches(|req| { - req.query_params - .as_ref() - .and_then(|qs| qs.iter().find(|(key, _)| key == "end_time")) - .is_none() - })) - } - } - - pub fn limit(self, value: T) -> Self - where - T: Into>, - { - if let Some(value) = value.into() { - Self(self.0.query_param("limit", value.to_string())) - } else { - Self(self.0.matches(|req| { - req.query_params - .as_ref() - .and_then(|qs| qs.iter().find(|(key, _)| key == "limit")) - .is_none() - })) - } - } - - pub fn order(self, value: T) -> Self - where - T: Into>, - { - if let Some(value) = value.into() { - Self(self.0.query_param("order", value.to_string())) - } else { - Self(self.0.matches(|req| { - req.query_params - .as_ref() - .and_then(|qs| qs.iter().find(|(key, _)| key == "order")) - .is_none() - })) - } - } - - pub fn page_token<'a, T>(self, value: T) -> Self - where - T: Into>, - { - if let Some(value) = value.into() { - Self(self.0.query_param("page_token", value.to_string())) - } else { - Self(self.0.matches(|req| { - req.query_params - .as_ref() - .and_then(|qs| qs.iter().find(|(key, _)| key == "page_token")) - .is_none() - })) - } - } - - pub fn project<'a, T>(self, value: T) -> Self - where - T: Into>, - { - if let Some(value) = value.into() { - Self(self.0.query_param("project", value.to_string())) - } else { - Self(self.0.matches(|req| { - req.query_params - .as_ref() - .and_then(|qs| qs.iter().find(|(key, _)| key == "project")) - .is_none() - })) - } - } - - pub fn start_time<'a, T>(self, value: T) -> Self - where - T: Into>>, - { - if let Some(value) = value.into() { - Self(self.0.query_param("start_time", value.to_string())) - } else { - Self(self.0.matches(|req| { - req.query_params - .as_ref() - .and_then(|qs| qs.iter().find(|(key, _)| key == "start_time")) - .is_none() - })) - } - } - } - - pub struct DiskMetricsListThen(::httpmock::Then); - impl DiskMetricsListThen { - pub fn new(inner: ::httpmock::Then) -> Self { - Self(inner) - } - - pub fn into_inner(self) -> ::httpmock::Then { - self.0 - } - - pub fn ok(self, value: &types::MeasurementResultsPage) -> Self { - Self( - self.0 - .status(200u16) - .header("content-type", "application/json") - .json_body_obj(value), - ) - } - - pub fn client_error(self, status: u16, value: &types::Error) -> Self { - assert_eq!(status / 100u16, 4u16); - Self( - self.0 - .status(status) - .header("content-type", "application/json") - .json_body_obj(value), - ) - } - - pub fn server_error(self, status: u16, value: &types::Error) -> Self { - assert_eq!(status / 100u16, 5u16); - Self( - self.0 - .status(status) - .header("content-type", "application/json") - .json_body_obj(value), - ) - } - } - pub struct FloatingIpListWhen(::httpmock::When); impl FloatingIpListWhen { pub fn new(inner: ::httpmock::When) -> Self { @@ -22212,9 +22049,6 @@ pub trait MockServerExt { fn disk_finalize_import(&self, config_fn: F) -> ::httpmock::Mock where F: FnOnce(operations::DiskFinalizeImportWhen, operations::DiskFinalizeImportThen); - fn disk_metrics_list(&self, config_fn: F) -> ::httpmock::Mock - where - F: FnOnce(operations::DiskMetricsListWhen, operations::DiskMetricsListThen); fn floating_ip_list(&self, config_fn: F) -> ::httpmock::Mock where F: FnOnce(operations::FloatingIpListWhen, operations::FloatingIpListThen); @@ -23775,18 +23609,6 @@ impl MockServerExt for ::httpmock::MockServer { }) } - fn disk_metrics_list(&self, config_fn: F) -> ::httpmock::Mock - where - F: FnOnce(operations::DiskMetricsListWhen, operations::DiskMetricsListThen), - { - self.mock(|when, then| { - config_fn( - operations::DiskMetricsListWhen::new(when), - operations::DiskMetricsListThen::new(then), - ) - }) - } - fn floating_ip_list(&self, config_fn: F) -> ::httpmock::Mock where F: FnOnce(operations::FloatingIpListWhen, operations::FloatingIpListThen), diff --git a/sdk/src/generated_sdk.rs b/sdk/src/generated_sdk.rs index 26b154ca..0f68766f 100644 --- a/sdk/src/generated_sdk.rs +++ b/sdk/src/generated_sdk.rs @@ -8670,111 +8670,6 @@ pub mod types { } } - /// `DiskMetricName` - /// - ///
JSON schema - /// - /// ```json - /// { - /// "type": "string", - /// "enum": [ - /// "activated", - /// "flush", - /// "read", - /// "read_bytes", - /// "write", - /// "write_bytes" - /// ] - /// } - /// ``` - ///
- #[derive( - :: serde :: Deserialize, - :: serde :: Serialize, - Clone, - Copy, - Debug, - Eq, - Hash, - Ord, - PartialEq, - PartialOrd, - schemars :: JsonSchema, - )] - pub enum DiskMetricName { - #[serde(rename = "activated")] - Activated, - #[serde(rename = "flush")] - Flush, - #[serde(rename = "read")] - Read, - #[serde(rename = "read_bytes")] - ReadBytes, - #[serde(rename = "write")] - Write, - #[serde(rename = "write_bytes")] - WriteBytes, - } - - impl ::std::convert::From<&Self> for DiskMetricName { - fn from(value: &DiskMetricName) -> Self { - value.clone() - } - } - - impl ::std::fmt::Display for DiskMetricName { - fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { - match *self { - Self::Activated => write!(f, "activated"), - Self::Flush => write!(f, "flush"), - Self::Read => write!(f, "read"), - Self::ReadBytes => write!(f, "read_bytes"), - Self::Write => write!(f, "write"), - Self::WriteBytes => write!(f, "write_bytes"), - } - } - } - - impl ::std::str::FromStr for DiskMetricName { - type Err = self::error::ConversionError; - fn from_str(value: &str) -> ::std::result::Result { - match value { - "activated" => Ok(Self::Activated), - "flush" => Ok(Self::Flush), - "read" => Ok(Self::Read), - "read_bytes" => Ok(Self::ReadBytes), - "write" => Ok(Self::Write), - "write_bytes" => Ok(Self::WriteBytes), - _ => Err("invalid value".into()), - } - } - } - - impl ::std::convert::TryFrom<&str> for DiskMetricName { - type Error = self::error::ConversionError; - fn try_from(value: &str) -> ::std::result::Result { - value.parse() - } - } - - impl ::std::convert::TryFrom<&::std::string::String> for DiskMetricName { - type Error = self::error::ConversionError; - fn try_from( - value: &::std::string::String, - ) -> ::std::result::Result { - value.parse() - } - } - - impl ::std::convert::TryFrom<::std::string::String> for DiskMetricName { - type Error = self::error::ConversionError; - fn try_from( - value: ::std::string::String, - ) -> ::std::result::Result { - value.parse() - } - } - /// `DiskPath` /// ///
JSON schema @@ -61321,34 +61216,6 @@ pub trait ClientDisksExt { /// .await; /// ``` fn disk_finalize_import(&self) -> builder::DiskFinalizeImport; - /// Fetch disk metrics - /// - /// Sends a `GET` request to `/v1/disks/{disk}/metrics/{metric}` - /// - /// Arguments: - /// - `disk` - /// - `metric` - /// - `end_time`: An exclusive end time of metrics. - /// - `limit`: Maximum number of items returned by a single call - /// - `order`: Query result order - /// - `page_token`: Token returned by previous call to retrieve the - /// subsequent page - /// - `project`: Name or ID of the project - /// - `start_time`: An inclusive start time of metrics. - /// ```ignore - /// let response = client.disk_metrics_list() - /// .disk(disk) - /// .metric(metric) - /// .end_time(end_time) - /// .limit(limit) - /// .order(order) - /// .page_token(page_token) - /// .project(project) - /// .start_time(start_time) - /// .send() - /// .await; - /// ``` - fn disk_metrics_list(&self) -> builder::DiskMetricsList; } impl ClientDisksExt for Client { @@ -61383,10 +61250,6 @@ impl ClientDisksExt for Client { fn disk_finalize_import(&self) -> builder::DiskFinalizeImport { builder::DiskFinalizeImport::new(self) } - - fn disk_metrics_list(&self) -> builder::DiskMetricsList { - builder::DiskMetricsList::new(self) - } } /// Experimental, unstable interfaces, primarily for use by Oxide personnel @@ -73023,248 +72886,6 @@ pub mod builder { } } - /// Builder for [`ClientDisksExt::disk_metrics_list`] - /// - /// [`ClientDisksExt::disk_metrics_list`]: super::ClientDisksExt::disk_metrics_list - #[derive(Debug, Clone)] - pub struct DiskMetricsList<'a> { - client: &'a super::Client, - disk: Result, - metric: Result, - end_time: Result>, String>, - limit: Result, String>, - order: Result, String>, - page_token: Result, String>, - project: Result, String>, - start_time: Result>, String>, - } - - impl<'a> DiskMetricsList<'a> { - pub fn new(client: &'a super::Client) -> Self { - Self { - client: client, - disk: Err("disk was not initialized".to_string()), - metric: Err("metric was not initialized".to_string()), - end_time: Ok(None), - limit: Ok(None), - order: Ok(None), - page_token: Ok(None), - project: Ok(None), - start_time: Ok(None), - } - } - - pub fn disk(mut self, value: V) -> Self - where - V: std::convert::TryInto, - { - self.disk = value - .try_into() - .map_err(|_| "conversion to `NameOrId` for disk failed".to_string()); - self - } - - pub fn metric(mut self, value: V) -> Self - where - V: std::convert::TryInto, - { - self.metric = value - .try_into() - .map_err(|_| "conversion to `DiskMetricName` for metric failed".to_string()); - self - } - - pub fn end_time(mut self, value: V) -> Self - where - V: std::convert::TryInto<::chrono::DateTime<::chrono::offset::Utc>>, - { - self.end_time = value.try_into().map(Some).map_err(|_| { - "conversion to `:: chrono :: DateTime < :: chrono :: offset :: Utc >` for end_time \ - failed" - .to_string() - }); - self - } - - pub fn limit(mut self, value: V) -> Self - where - V: std::convert::TryInto<::std::num::NonZeroU32>, - { - self.limit = value.try_into().map(Some).map_err(|_| { - "conversion to `:: std :: num :: NonZeroU32` for limit failed".to_string() - }); - self - } - - pub fn order(mut self, value: V) -> Self - where - V: std::convert::TryInto, - { - self.order = value - .try_into() - .map(Some) - .map_err(|_| "conversion to `PaginationOrder` for order failed".to_string()); - self - } - - pub fn page_token(mut self, value: V) -> Self - where - V: std::convert::TryInto<::std::string::String>, - { - self.page_token = value.try_into().map(Some).map_err(|_| { - "conversion to `:: std :: string :: String` for page_token failed".to_string() - }); - self - } - - pub fn project(mut self, value: V) -> Self - where - V: std::convert::TryInto, - { - self.project = value - .try_into() - .map(Some) - .map_err(|_| "conversion to `NameOrId` for project failed".to_string()); - self - } - - pub fn start_time(mut self, value: V) -> Self - where - V: std::convert::TryInto<::chrono::DateTime<::chrono::offset::Utc>>, - { - self.start_time = value.try_into().map(Some).map_err(|_| { - "conversion to `:: chrono :: DateTime < :: chrono :: offset :: Utc >` for \ - start_time failed" - .to_string() - }); - self - } - - /// Sends a `GET` request to `/v1/disks/{disk}/metrics/{metric}` - pub async fn send( - self, - ) -> Result, Error> { - let Self { - client, - disk, - metric, - end_time, - limit, - order, - page_token, - project, - start_time, - } = self; - let disk = disk.map_err(Error::InvalidRequest)?; - let metric = metric.map_err(Error::InvalidRequest)?; - let end_time = end_time.map_err(Error::InvalidRequest)?; - let limit = limit.map_err(Error::InvalidRequest)?; - let order = order.map_err(Error::InvalidRequest)?; - let page_token = page_token.map_err(Error::InvalidRequest)?; - let project = project.map_err(Error::InvalidRequest)?; - let start_time = start_time.map_err(Error::InvalidRequest)?; - let url = format!( - "{}/v1/disks/{}/metrics/{}", - client.baseurl, - encode_path(&disk.to_string()), - encode_path(&metric.to_string()), - ); - let mut header_map = ::reqwest::header::HeaderMap::with_capacity(1usize); - header_map.append( - ::reqwest::header::HeaderName::from_static("api-version"), - ::reqwest::header::HeaderValue::from_static(super::Client::api_version()), - ); - #[allow(unused_mut)] - let mut request = client - .client - .get(url) - .header( - ::reqwest::header::ACCEPT, - ::reqwest::header::HeaderValue::from_static("application/json"), - ) - .query(&progenitor_client::QueryParam::new("end_time", &end_time)) - .query(&progenitor_client::QueryParam::new("limit", &limit)) - .query(&progenitor_client::QueryParam::new("order", &order)) - .query(&progenitor_client::QueryParam::new( - "page_token", - &page_token, - )) - .query(&progenitor_client::QueryParam::new("project", &project)) - .query(&progenitor_client::QueryParam::new( - "start_time", - &start_time, - )) - .headers(header_map) - .build()?; - let info = OperationInfo { - operation_id: "disk_metrics_list", - }; - client.pre(&mut request, &info).await?; - let result = client.exec(request, &info).await; - client.post(&result, &info).await?; - let response = result?; - match response.status().as_u16() { - 200u16 => ResponseValue::from_response(response).await, - 400u16..=499u16 => Err(Error::ErrorResponse( - ResponseValue::from_response(response).await?, - )), - 500u16..=599u16 => Err(Error::ErrorResponse( - ResponseValue::from_response(response).await?, - )), - _ => Err(Error::UnexpectedResponse(response)), - } - } - - /// Streams `GET` requests to `/v1/disks/{disk}/metrics/{metric}` - pub fn stream( - self, - ) -> impl futures::Stream>> + Unpin + 'a - { - use ::futures::StreamExt; - use ::futures::TryFutureExt; - use ::futures::TryStreamExt; - let next = Self { - end_time: Ok(None), - order: Ok(None), - page_token: Ok(None), - project: Ok(None), - start_time: Ok(None), - ..self.clone() - }; - self.send() - .map_ok(move |page| { - let page = page.into_inner(); - let first = futures::stream::iter(page.items).map(Ok); - let rest = futures::stream::try_unfold( - (page.next_page, next), - |(next_page, next)| async { - if next_page.is_none() { - Ok(None) - } else { - Self { - page_token: Ok(next_page), - ..next.clone() - } - .send() - .map_ok(|page| { - let page = page.into_inner(); - Some(( - futures::stream::iter(page.items).map(Ok), - (page.next_page, next), - )) - }) - .await - } - }, - ) - .try_flatten(); - first.chain(rest) - }) - .try_flatten_stream() - .boxed() - } - } - /// Builder for [`ClientFloatingIpsExt::floating_ip_list`] /// /// [`ClientFloatingIpsExt::floating_ip_list`]: super::ClientFloatingIpsExt::floating_ip_list From d948f893f8d6c9ebe9bd8e05b5edcb3f4ac755c2 Mon Sep 17 00:00:00 2001 From: "Adam H. Leventhal" Date: Fri, 1 Aug 2025 12:18:59 -0700 Subject: [PATCH 9/9] new subcommands --- cli/docs/cli.json | 181 +++++++++++++++++++++++++++-------------- cli/src/cli_builder.rs | 9 +- 2 files changed, 127 insertions(+), 63 deletions(-) diff --git a/cli/docs/cli.json b/cli/docs/cli.json index 91fd225d..0490e7fa 100644 --- a/cli/docs/cli.json +++ b/cli/docs/cli.json @@ -616,10 +616,22 @@ "name": "create", "about": "Create a new support bundle", "args": [ + { + "long": "json-body", + "help": "Path to a file that contains the full json body." + }, + { + "long": "json-body-template", + "help": "XXX" + }, { "long": "profile", "help": "Configuration profile to use for commands", "global": true + }, + { + "long": "user-comment", + "help": "User comment for the support bundle" } ] }, @@ -707,6 +719,33 @@ } ] }, + { + "name": "update", + "about": "Update a support bundle", + "args": [ + { + "long": "bundle-id", + "help": "ID of the support bundle" + }, + { + "long": "json-body", + "help": "Path to a file that contains the full json body." + }, + { + "long": "json-body-template", + "help": "XXX" + }, + { + "long": "profile", + "help": "Configuration profile to use for commands", + "global": true + }, + { + "long": "user-comment", + "help": "User comment for the support bundle" + } + ] + }, { "name": "view", "about": "View a support bundle", @@ -1304,67 +1343,6 @@ } ] }, - { - "name": "metrics", - "args": [ - { - "long": "profile", - "help": "Configuration profile to use for commands", - "global": true - } - ], - "subcommands": [ - { - "name": "list", - "about": "Fetch disk metrics", - "args": [ - { - "long": "disk" - }, - { - "long": "end-time", - "help": "An exclusive end time of metrics." - }, - { - "long": "limit", - "help": "Maximum number of items returned by a single call" - }, - { - "long": "metric", - "values": [ - "activated", - "flush", - "read", - "read_bytes", - "write", - "write_bytes" - ] - }, - { - "long": "order", - "values": [ - "ascending", - "descending" - ], - "help": "Query result order" - }, - { - "long": "profile", - "help": "Configuration profile to use for commands", - "global": true - }, - { - "long": "project", - "help": "Name or ID of the project" - }, - { - "long": "start-time", - "help": "An inclusive start time of metrics." - } - ] - } - ] - }, { "name": "view", "about": "Fetch disk", @@ -7921,6 +7899,87 @@ ] } ] + }, + { + "name": "list-sessions", + "about": "List user's console sessions", + "args": [ + { + "long": "limit", + "help": "Maximum number of items returned by a single call" + }, + { + "long": "profile", + "help": "Configuration profile to use for commands", + "global": true + }, + { + "long": "sort-by", + "values": [ + "id_ascending" + ] + }, + { + "long": "user-id", + "help": "ID of the user" + } + ] + }, + { + "name": "list-tokens", + "about": "List user's access tokens", + "args": [ + { + "long": "limit", + "help": "Maximum number of items returned by a single call" + }, + { + "long": "profile", + "help": "Configuration profile to use for commands", + "global": true + }, + { + "long": "sort-by", + "values": [ + "id_ascending" + ] + }, + { + "long": "user-id", + "help": "ID of the user" + } + ] + }, + { + "name": "logout", + "about": "Log user out", + "long_about": "Silo admins can use this endpoint to log the specified user out by deleting all of their tokens AND sessions. This cannot be undone.", + "args": [ + { + "long": "profile", + "help": "Configuration profile to use for commands", + "global": true + }, + { + "long": "user-id", + "help": "ID of the user" + } + ] + }, + { + "name": "view", + "about": "Fetch user", + "args": [ + { + "long": "profile", + "help": "Configuration profile to use for commands", + "global": true + }, + { + "long": "user-id", + "help": "ID of the user" + } + ] } ] }, diff --git a/cli/src/cli_builder.rs b/cli/src/cli_builder.rs index 30f90bc5..e6b2132b 100644 --- a/cli/src/cli_builder.rs +++ b/cli/src/cli_builder.rs @@ -489,7 +489,6 @@ fn xxx<'a>(command: CliCommand) -> Option<&'a str> { CliCommand::SiloIpPoolList => Some("silo ip-pool list"), CliCommand::UtilizationView => Some("utilization"), - CliCommand::UserList => Some("user list"), // VPCs CliCommand::VpcList => Some("vpc list"), @@ -597,7 +596,6 @@ fn xxx<'a>(command: CliCommand) -> Option<&'a str> { CliCommand::DiskCreate => Some("disk create"), CliCommand::DiskView => Some("disk view"), CliCommand::DiskDelete => Some("disk delete"), - CliCommand::DiskMetricsList => Some("disk metrics list"), CliCommand::DiskBulkWriteImportStart => Some("disk import start"), CliCommand::DiskBulkWriteImport => Some("disk import write"), @@ -677,6 +675,12 @@ fn xxx<'a>(command: CliCommand) -> Option<&'a str> { CliCommand::CurrentUserAccessTokenList => Some("current-user access-token list"), CliCommand::CurrentUserAccessTokenDelete => Some("current-user access-token delete"), + CliCommand::UserList => Some("user list"), + CliCommand::UserView => Some("user view"), + CliCommand::UserTokenList => Some("user list-tokens"), + CliCommand::UserLogout => Some("user logout"), + CliCommand::UserSessionList => Some("user list-sessions"), + CliCommand::FloatingIpAttach => Some("floating-ip attach"), CliCommand::FloatingIpCreate => Some("floating-ip create"), CliCommand::FloatingIpDelete => Some("floating-ip delete"), @@ -722,6 +726,7 @@ fn xxx<'a>(command: CliCommand) -> Option<&'a str> { CliCommand::SupportBundleCreate => Some("bundle create"), CliCommand::SupportBundleView => Some("bundle view"), CliCommand::SupportBundleDelete => Some("bundle delete"), + CliCommand::SupportBundleUpdate => Some("bundle update"), // Implemented as custom command to specify output file CliCommand::SupportBundleDownload => None,