From 7c1365a2c790e29b77e638c35df3ccf0c77559aa Mon Sep 17 00:00:00 2001 From: MasterPtato Date: Sat, 17 Jun 2023 21:10:15 +0000 Subject: [PATCH 1/3] Remove fallible uses of `as` in api services --- errors/cloud/invalid-config.md | 9 ++ errors/mm/invalid-version-config.md | 9 ++ errors/upload/too-large.md | 10 ++ lib/convert/src/convert/chat.rs | 5 +- lib/convert/src/convert/group.rs | 4 +- lib/convert/src/convert/party.rs | 4 +- lib/convert/src/impls/cloud/mod.rs | 50 ++++---- .../src/impls/cloud/namespace/matchmaker.rs | 14 +- .../cloud/version/matchmaker/game_mode.rs | 52 ++++---- .../cloud/version/matchmaker/lobby_group.rs | 38 +++--- .../src/impls/cloud/version/matchmaker/mod.rs | 43 ++++--- lib/convert/src/impls/mod.rs | 37 ++++++ svc/api/chat/src/route/chat.rs | 7 +- svc/api/cloud/src/convert/group.rs | 4 +- svc/api/cloud/src/fetch/mod.rs | 2 +- svc/api/cloud/src/route/games/avatars.rs | 12 +- svc/api/cloud/src/route/games/builds.rs | 4 +- svc/api/cloud/src/route/games/cdn.rs | 4 +- svc/api/cloud/src/route/games/matchmaker.rs | 4 +- svc/api/cloud/src/route/games/mod.rs | 25 ++-- .../cloud/src/route/games/namespaces/logs.rs | 6 +- .../cloud/src/route/games/namespaces/mod.rs | 121 +++++++++++++----- svc/api/cloud/src/route/groups.rs | 2 +- svc/api/cloud/src/route/tiers.rs | 6 +- svc/api/group/src/convert/group.rs | 4 +- svc/api/group/src/route/groups.rs | 10 +- svc/api/identity/src/route/events.rs | 6 +- svc/api/identity/src/route/identities.rs | 19 ++- svc/api/matchmaker/src/route/lobbies.rs | 36 +++--- svc/api/matchmaker/src/route/players.rs | 14 +- svc/api/party/src/convert/party.rs | 6 +- svc/api/party/src/route/parties.rs | 4 +- svc/api/portal/src/build.rs | 4 +- svc/pkg/build/ops/create/src/lib.rs | 6 +- svc/pkg/cdn/ops/site-create/src/lib.rs | 4 +- 35 files changed, 384 insertions(+), 201 deletions(-) create mode 100644 errors/cloud/invalid-config.md create mode 100644 errors/mm/invalid-version-config.md create mode 100644 errors/upload/too-large.md diff --git a/errors/cloud/invalid-config.md b/errors/cloud/invalid-config.md new file mode 100644 index 0000000000..143dcc4cba --- /dev/null +++ b/errors/cloud/invalid-config.md @@ -0,0 +1,9 @@ +--- +name = "CLOUD_INVALID_CONFIG" +description = "The given config was invalid: {error}" +http_status = 400 +--- + +# Matchmaker Invalid Config + +The given config was invalid. diff --git a/errors/mm/invalid-version-config.md b/errors/mm/invalid-version-config.md new file mode 100644 index 0000000000..019015e0c8 --- /dev/null +++ b/errors/mm/invalid-version-config.md @@ -0,0 +1,9 @@ +--- +name = "MATCHMAKER_INVALID_VERSION_CONFIG" +description = "The given version config was invalid: {error}" +http_status = 400 +--- + +# Matchmaker Invalid Version Config + +The given version config was invalid. diff --git a/errors/upload/too-large.md b/errors/upload/too-large.md new file mode 100644 index 0000000000..833db72de6 --- /dev/null +++ b/errors/upload/too-large.md @@ -0,0 +1,10 @@ +--- +name = "UPLOAD_TOO_LARGE" +description = "Upload too large (maximum size {max})." +description_basic = "Upload too large." +http_status = 400 +--- + +# Upload Too Large + +The given upload was too large. diff --git a/lib/convert/src/convert/chat.rs b/lib/convert/src/convert/chat.rs index 58abeafaa9..d9d0e98f23 100644 --- a/lib/convert/src/convert/chat.rs +++ b/lib/convert/src/convert/chat.rs @@ -1,3 +1,5 @@ +use std::convert::TryInto; + use rivet_api::models; use rivet_operation::prelude::*; use types::rivet::backend::{self, pkg::*}; @@ -270,7 +272,8 @@ pub fn thread( .iter() .find(|t| t.thread_id == thread.thread_id) .map(|t| t.unread_count) - .unwrap_or_default() as i64, + .unwrap_or_default() + .try_into()?, external: Box::new(models::ChatThreadExternalLinks { chat: util::route::thread(&thread_id), }), diff --git a/lib/convert/src/convert/group.rs b/lib/convert/src/convert/group.rs index 64d99ee6c9..714814bdc0 100644 --- a/lib/convert/src/convert/group.rs +++ b/lib/convert/src/convert/group.rs @@ -1,3 +1,5 @@ +use std::convert::TryInto; + use rivet_api::models; use rivet_operation::prelude::*; use types::rivet::backend::{self, pkg::*}; @@ -57,7 +59,7 @@ pub fn summary( is_current_identity_member, publicity: internal_unwrap_owned!(backend::team::Publicity::from_i32(team.publicity)) .api_into(), - member_count: member_count as i32, + member_count: member_count.try_into()?, owner_identity_id: owner_user_id, is_developer, }) diff --git a/lib/convert/src/convert/party.rs b/lib/convert/src/convert/party.rs index a1d83bbcb6..ba85a5cdc5 100644 --- a/lib/convert/src/convert/party.rs +++ b/lib/convert/src/convert/party.rs @@ -1,3 +1,5 @@ +use std::convert::TryInto; + use rivet_api::models; use rivet_operation::prelude::*; use types::rivet::backend; @@ -42,7 +44,7 @@ pub fn summary( Ok(models::PartySummary { party_id, create_ts: util::timestamp::to_string(party.create_ts)?, - party_size: party.party_size as i32, + party_size: party.party_size.try_into()?, activity: Box::new(convert::party::activity(party.state.as_ref(), games)?), // TODO: Only party leader should be able to see this publicity: Box::new(models::PartyPublicity { diff --git a/lib/convert/src/impls/cloud/mod.rs b/lib/convert/src/impls/cloud/mod.rs index 9e4e7dc299..067e53473d 100644 --- a/lib/convert/src/impls/cloud/mod.rs +++ b/lib/convert/src/impls/cloud/mod.rs @@ -27,11 +27,11 @@ pub fn analytics_lobby_summary_from_lobby( is_idle: player_count.total_player_count == 0, is_closed: lobby.is_closed, is_outdated, - max_players_normal: lobby.max_players_normal as i32, - max_players_direct: lobby.max_players_direct as i32, - max_players_party: lobby.max_players_party as i32, - total_player_count: player_count.total_player_count as i32, - registered_player_count: player_count.registered_player_count as i32, + max_players_normal: lobby.max_players_normal.try_into()?, + max_players_direct: lobby.max_players_direct.try_into()?, + max_players_party: lobby.max_players_party.try_into()?, + total_player_count: player_count.total_player_count.try_into()?, + registered_player_count: player_count.registered_player_count.try_into()?, }) } @@ -98,15 +98,17 @@ impl ApiTryFrom for models::SvcPerf { } } -impl ApiFrom for models::SvcMetrics { - fn api_from(value: job_run::metrics_log::response::Metrics) -> models::SvcMetrics { - models::SvcMetrics { +impl ApiTryFrom for models::SvcMetrics { + type Error = GlobalError; + + fn try_from(value: job_run::metrics_log::response::Metrics) -> GlobalResult { + Ok(models::SvcMetrics { job: value.job, cpu: value.cpu, - memory: value.memory.into_iter().map(|v| v as i64).collect(), - memory_max: value.memory_max.into_iter().map(|v| v as i64).collect(), - allocated_memory: value.allocated_memory as i64, - } + memory: value.memory.into_iter().map(|v| v.try_into()).collect::, _>>()?, + memory_max: value.memory_max.into_iter().map(|v| v.try_into()).collect::, _>>()?, + allocated_memory: value.allocated_memory.try_into()?, + }) } } @@ -143,7 +145,7 @@ impl ApiTryFrom for backend::upload::PrepareFile { type Error = GlobalError; fn try_from(value: models::UploadPrepareFile) -> GlobalResult { - internal_assert!(value.content_length >= 0); + assert_with!(value.content_length >= 0, MATCHMAKER_INVALID_VERSION_CONFIG, error = "`file.content_length` out of bounds"); Ok(backend::upload::PrepareFile { path: value.path, @@ -219,17 +221,19 @@ mod openapi { } } -impl ApiFrom for models::RegionTier { - fn api_from(value: backend::region::Tier) -> models::RegionTier { - models::RegionTier { +impl ApiTryFrom for models::RegionTier { + type Error = GlobalError; + + fn try_from(value: backend::region::Tier) -> GlobalResult { + Ok(models::RegionTier { tier_name_id: value.tier_name_id.to_owned(), - rivet_cores_numerator: value.rivet_cores_numerator as i32, - rivet_cores_denominator: value.rivet_cores_denominator as i32, - cpu: value.cpu as i64, - memory: value.memory as i64, - disk: value.disk as i64, - bandwidth: value.bandwidth as i64, - } + rivet_cores_numerator: value.rivet_cores_numerator.try_into()?, + rivet_cores_denominator: value.rivet_cores_denominator.try_into()?, + cpu: value.cpu.try_into()?, + memory: value.memory.try_into()?, + disk: value.disk.try_into()?, + bandwidth: value.bandwidth.try_into()?, + }) } } diff --git a/lib/convert/src/impls/cloud/namespace/matchmaker.rs b/lib/convert/src/impls/cloud/namespace/matchmaker.rs index 9aed1d3d4d..5234edb881 100644 --- a/lib/convert/src/impls/cloud/namespace/matchmaker.rs +++ b/lib/convert/src/impls/cloud/namespace/matchmaker.rs @@ -1,3 +1,5 @@ +use std::convert::TryInto; + use proto::backend; use rivet_api::models; use rivet_operation::prelude::*; @@ -9,12 +11,12 @@ impl ApiTryFrom for models::CloudMatchmake fn try_from(value: backend::matchmaker::NamespaceConfig) -> GlobalResult { Ok(models::CloudMatchmakerNamespaceConfig { - lobby_count_max: value.lobby_count_max as i32, - max_players_per_client: value.max_players_per_client as i32, - max_players_per_client_vpn: value.max_players_per_client_vpn as i32, - max_players_per_client_proxy: value.max_players_per_client_proxy as i32, - max_players_per_client_tor: value.max_players_per_client_tor as i32, - max_players_per_client_hosting: value.max_players_per_client_hosting as i32, + lobby_count_max: value.lobby_count_max.try_into()?, + max_players_per_client: value.max_players_per_client.try_into()?, + max_players_per_client_vpn: value.max_players_per_client_vpn.try_into()?, + max_players_per_client_proxy: value.max_players_per_client_proxy.try_into()?, + max_players_per_client_tor: value.max_players_per_client_tor.try_into()?, + max_players_per_client_hosting: value.max_players_per_client_hosting.try_into()?, }) } } diff --git a/lib/convert/src/impls/cloud/version/matchmaker/game_mode.rs b/lib/convert/src/impls/cloud/version/matchmaker/game_mode.rs index 1f4f426298..edd2366713 100644 --- a/lib/convert/src/impls/cloud/version/matchmaker/game_mode.rs +++ b/lib/convert/src/impls/cloud/version/matchmaker/game_mode.rs @@ -23,7 +23,7 @@ pub fn game_mode_to_proto( let max_players_normal = game_mode .max_players .or(matchmaker.max_players) - .unwrap_or(util_mm::defaults::MAX_PLAYERS_NORMAL as i32); + .unwrap_or_else(|| util_mm::defaults::MAX_PLAYERS_NORMAL as i32); let max_players_direct = game_mode .max_players_direct .or(matchmaker.max_players_direct) @@ -34,9 +34,9 @@ pub fn game_mode_to_proto( .unwrap_or(max_players_normal); // TODO: Make this return a 400 error instead - internal_assert!(max_players_normal >= 0); - internal_assert!(max_players_direct >= 0); - internal_assert!(max_players_party >= 0); + assert_with!(max_players_normal >= 0, MATCHMAKER_INVALID_VERSION_CONFIG, error = "`max_players` out of bounds"); + assert_with!(max_players_direct >= 0, MATCHMAKER_INVALID_VERSION_CONFIG, error = "`max_players_direct` out of bounds"); + assert_with!(max_players_party >= 0, MATCHMAKER_INVALID_VERSION_CONFIG, error = "`max_players_party` out of bounds"); // Derive runtime let runtime = if let Some(either_runtime) = game_mode.docker.as_ref().or(matchmaker.docker.as_ref()) { @@ -83,8 +83,12 @@ pub fn game_mode_to_proto( GlobalResult::Ok(backend::matchmaker::lobby_runtime::Port { label, - target_port: value.port.map(|x| x as u32), - port_range: value.port_range.map(|x| (*x).api_into()), + target_port: value.port.map(|x| { + assert_with!(x >= 0, MATCHMAKER_INVALID_VERSION_CONFIG, error = "`port` out of bounds"); + + Ok(x.try_into()?) + }).transpose()?, + port_range: value.port_range.map(|x| (*x).try_into()).transpose()?, proxy_protocol: ApiInto::< backend::matchmaker::lobby_runtime::ProxyProtocol, >::api_into(proxy_protocol) as i32, @@ -108,9 +112,9 @@ pub fn game_mode_to_proto( .iter() .map(|(k, v)| region_to_proto(k.clone(), v, game_mode, matchmaker, regions_data)) .collect::>()?, - max_players_normal: max_players_normal as u32, - max_players_direct: max_players_direct as u32, - max_players_party: max_players_party as u32, + max_players_normal: max_players_normal.try_into()?, + max_players_direct: max_players_direct.try_into()?, + max_players_party: max_players_party.try_into()?, runtime, }) @@ -152,11 +156,12 @@ pub fn game_mode_to_openapi( GlobalResult::Ok(( x.label.clone(), models::CloudVersionMatchmakerGameModeRuntimeDockerPort { - port: x.target_port.map(|x| x as i32), + port: x.target_port.map(|x| x.try_into()).transpose()?, port_range: x .port_range .clone() - .map(ApiInto::api_into) + .map(ApiTryInto::try_into) + .transpose()? .map(Box::new), protocol: Some( internal_unwrap_owned!( @@ -202,9 +207,9 @@ pub fn game_mode_to_openapi( .map(|x| region_to_openapi(x, regions_data)) .collect::>>()?, ), - max_players: Some(value.max_players_normal as i32), - max_players_direct: Some(value.max_players_direct as i32), - max_players_party: Some(value.max_players_party as i32), + max_players: Some(value.max_players_normal.try_into()?), + max_players_direct: Some(value.max_players_direct.try_into()?), + max_players_party: Some(value.max_players_party.try_into()?), docker: Some(Box::new(docker)), @@ -249,10 +254,7 @@ fn region_to_proto( Ok(backend::matchmaker::lobby_group::Region { region_id, tier_name_id, - idle_lobbies: Some(backend::matchmaker::lobby_group::IdleLobbies { - min_idle_lobbies: idle_lobbies.min as u32, - max_idle_lobbies: idle_lobbies.max as u32, - }), + idle_lobbies: Some((*idle_lobbies).try_into()?), }) } @@ -288,12 +290,12 @@ impl ApiTryFrom fn try_from( value: models::CloudVersionMatchmakerGameModeIdleLobbiesConfig, ) -> GlobalResult { - internal_assert!(value.min >= 0); - internal_assert!(value.max >= 0); - + assert_with!(value.min >= 0, MATCHMAKER_INVALID_VERSION_CONFIG, error = "`idle_lobbies.min` out of bounds"); + assert_with!(value.max >= 0, MATCHMAKER_INVALID_VERSION_CONFIG, error = "`idle_lobbies.max` out of bounds"); + Ok(backend::matchmaker::lobby_group::IdleLobbies { - min_idle_lobbies: value.min as u32, - max_idle_lobbies: value.max as u32, + min_idle_lobbies: value.min.try_into()?, + max_idle_lobbies: value.max.try_into()?, }) } } @@ -305,8 +307,8 @@ impl ApiTryFrom fn try_from(value: backend::matchmaker::lobby_group::IdleLobbies) -> GlobalResult { Ok(models::CloudVersionMatchmakerGameModeIdleLobbiesConfig { - min: value.min_idle_lobbies as i32, - max: value.max_idle_lobbies as i32, + min: value.min_idle_lobbies.try_into()?, + max: value.max_idle_lobbies.try_into()?, }) } } diff --git a/lib/convert/src/impls/cloud/version/matchmaker/lobby_group.rs b/lib/convert/src/impls/cloud/version/matchmaker/lobby_group.rs index 90abb81f57..d4104a2a05 100644 --- a/lib/convert/src/impls/cloud/version/matchmaker/lobby_group.rs +++ b/lib/convert/src/impls/cloud/version/matchmaker/lobby_group.rs @@ -8,10 +8,10 @@ impl ApiTryFrom for backend::matchmake type Error = GlobalError; fn try_from(value: models::CloudVersionMatchmakerLobbyGroup) -> GlobalResult { - internal_assert!(value.max_players_normal >= 0); - internal_assert!(value.max_players_direct >= 0); - internal_assert!(value.max_players_party >= 0); - + assert_with!(value.max_players_normal >= 0, MATCHMAKER_INVALID_VERSION_CONFIG, error = "`max_players` out of bounds"); + assert_with!(value.max_players_direct >= 0, MATCHMAKER_INVALID_VERSION_CONFIG, error = "`max_players_direct` out of bounds"); + assert_with!(value.max_players_party >= 0, MATCHMAKER_INVALID_VERSION_CONFIG, error = "`max_players_party` out of bounds"); + Ok(backend::matchmaker::LobbyGroup { name_id: value.name_id, @@ -41,9 +41,9 @@ impl ApiTryFrom for models::CloudVersionMatchma .into_iter() .map(ApiTryFrom::try_from) .collect::, _>>()?, - max_players_normal: value.max_players_normal as i32, - max_players_direct: value.max_players_direct as i32, - max_players_party: value.max_players_party as i32, + max_players_normal: value.max_players_normal.try_into()?, + max_players_direct: value.max_players_direct.try_into()?, + max_players_party: value.max_players_party.try_into()?, runtime: Box::new(internal_unwrap_owned!(value.runtime).try_into()?), }) @@ -90,12 +90,12 @@ impl ApiTryFrom fn try_from( value: models::CloudVersionMatchmakerLobbyGroupIdleLobbiesConfig, ) -> GlobalResult { - internal_assert!(value.min_idle_lobbies >= 0); - internal_assert!(value.max_idle_lobbies >= 0); + assert_with!(value.min_idle_lobbies >= 0, MATCHMAKER_INVALID_VERSION_CONFIG, error = "`min_idle_lobbies` out of bounds"); + assert_with!(value.max_idle_lobbies >= 0, MATCHMAKER_INVALID_VERSION_CONFIG, error = "`max_idle_lobbies` out of bounds"); Ok(backend::matchmaker::lobby_group::IdleLobbies { - min_idle_lobbies: value.min_idle_lobbies as u32, - max_idle_lobbies: value.max_idle_lobbies as u32, + min_idle_lobbies: value.min_idle_lobbies.try_into()?, + max_idle_lobbies: value.max_idle_lobbies.try_into()?, }) } } @@ -107,8 +107,8 @@ impl ApiTryFrom fn try_from(value: backend::matchmaker::lobby_group::IdleLobbies) -> GlobalResult { Ok(models::CloudVersionMatchmakerLobbyGroupIdleLobbiesConfig { - min_idle_lobbies: value.min_idle_lobbies as i32, - max_idle_lobbies: value.max_idle_lobbies as i32, + min_idle_lobbies: value.min_idle_lobbies.try_into()?, + max_idle_lobbies: value.max_idle_lobbies.try_into()?, }) } } @@ -212,8 +212,12 @@ impl ApiTryFrom Ok(backend::matchmaker::lobby_runtime::Port { label: value.label, - target_port: value.target_port.map(|x| x as u32), - port_range: value.port_range.map(|x| (*x).api_into()), + target_port: value.target_port.map(|x| { + assert_with!(x >= 0, MATCHMAKER_INVALID_VERSION_CONFIG, error = "`target_port` out of bounds"); + + Ok(x.try_into()?) + }).transpose()?, + port_range: value.port_range.map(|x| (*x).try_into()).transpose()?, proxy_protocol: (ApiInto::::api_into( value.proxy_protocol, )) as i32, @@ -230,8 +234,8 @@ impl ApiTryFrom fn try_from(value: backend::matchmaker::lobby_runtime::Port) -> GlobalResult { Ok(models::CloudVersionMatchmakerLobbyGroupRuntimeDockerPort { label: value.label, - target_port: value.target_port.map(|x| x as i32), - port_range: value.port_range.map(ApiInto::api_into).map(Box::new), + target_port: value.target_port.map(|x| x.try_into()).transpose()?, + port_range: value.port_range.map(ApiTryInto::try_into).transpose()?.map(Box::new), proxy_protocol: internal_unwrap_owned!( backend::matchmaker::lobby_runtime::ProxyProtocol::from_i32(value.proxy_protocol) ) diff --git a/lib/convert/src/impls/cloud/version/matchmaker/mod.rs b/lib/convert/src/impls/cloud/version/matchmaker/mod.rs index d0a646983d..cbefa9eefa 100644 --- a/lib/convert/src/impls/cloud/version/matchmaker/mod.rs +++ b/lib/convert/src/impls/cloud/version/matchmaker/mod.rs @@ -106,27 +106,33 @@ pub async fn config_to_openapi( }) } -impl ApiFrom +impl ApiTryFrom for backend::matchmaker::lobby_runtime::PortRange { - fn api_from( - value: models::CloudVersionMatchmakerPortRange, - ) -> backend::matchmaker::lobby_runtime::PortRange { - backend::matchmaker::lobby_runtime::PortRange { - min: value.min as u32, - max: value.max as u32, - } + type Error = GlobalError; + + fn try_from(value: models::CloudVersionMatchmakerPortRange, + ) -> GlobalResult { + assert_with!(value.min >= 0, MATCHMAKER_INVALID_VERSION_CONFIG, error = "`port_range.min` out of bounds"); + assert_with!(value.max >= 0, MATCHMAKER_INVALID_VERSION_CONFIG, error = "`port_range.max` out of bounds"); + + Ok(backend::matchmaker::lobby_runtime::PortRange { + min: value.min.try_into()?, + max: value.max.try_into()?, + }) } } -impl ApiFrom +impl ApiTryFrom for models::CloudVersionMatchmakerPortRange { - fn api_from(value: backend::matchmaker::lobby_runtime::PortRange) -> Self { - models::CloudVersionMatchmakerPortRange { - min: value.min as i32, - max: value.max as i32, - } + type Error = GlobalError; + + fn try_from(value: backend::matchmaker::lobby_runtime::PortRange) -> GlobalResult { + Ok(models::CloudVersionMatchmakerPortRange { + min: value.min.try_into()?, + max: value.max.try_into()?, + }) } } @@ -248,11 +254,12 @@ impl ApiTryFrom for backend::captcha::Cap type Error = GlobalError; fn try_from(value: models::CloudVersionMatchmakerCaptcha) -> GlobalResult { - internal_assert!(value.requests_before_reverify >= 0); - internal_assert!(value.verification_ttl >= 0); + assert_with!(value.requests_before_reverify >= 0, MATCHMAKER_INVALID_VERSION_CONFIG, error = "`requests_before_reverify` out of bounds"); + assert_with!(value.verification_ttl >= 0, MATCHMAKER_INVALID_VERSION_CONFIG, error = "`verification_ttl` out of bounds"); + Ok(backend::captcha::CaptchaConfig { - requests_before_reverify: value.requests_before_reverify as u32, + requests_before_reverify: value.requests_before_reverify.try_into()?, verification_ttl: value.verification_ttl, hcaptcha: value.hcaptcha.map(|x| (*x).api_into()), turnstile: None, @@ -265,7 +272,7 @@ impl ApiTryFrom for models::CloudVersionMatchma fn try_from(value: backend::captcha::CaptchaConfig) -> GlobalResult { Ok(models::CloudVersionMatchmakerCaptcha { - requests_before_reverify: value.requests_before_reverify as i32, + requests_before_reverify: value.requests_before_reverify.try_into()?, verification_ttl: value.verification_ttl, hcaptcha: value .hcaptcha diff --git a/lib/convert/src/impls/mod.rs b/lib/convert/src/impls/mod.rs index 756f98d40a..8004caa7d3 100644 --- a/lib/convert/src/impls/mod.rs +++ b/lib/convert/src/impls/mod.rs @@ -8,3 +8,40 @@ pub mod kv; pub mod party; pub mod portal; pub mod user; + +// Reimplement conversions for ease of use in this module +mod num { + use crate::ApiTryFrom; + + impl ApiTryFrom for u32 { + type Error = std::num::TryFromIntError; + + fn try_from(v: i32) -> Result { + std::convert::TryInto::try_into(v) + } + } + + impl ApiTryFrom for i32 { + type Error = std::num::TryFromIntError; + + fn try_from(v: u32) -> Result { + std::convert::TryInto::try_into(v) + } + } + + impl ApiTryFrom for i64 { + type Error = std::num::TryFromIntError; + + fn try_from(v: u64) -> Result { + std::convert::TryInto::try_into(v) + } + } + + impl ApiTryFrom for u64 { + type Error = std::num::TryFromIntError; + + fn try_from(v: i64) -> Result { + std::convert::TryInto::try_into(v) + } + } +} diff --git a/svc/api/chat/src/route/chat.rs b/svc/api/chat/src/route/chat.rs index 6822ffb77f..0610b58330 100644 --- a/svc/api/chat/src/route/chat.rs +++ b/svc/api/chat/src/route/chat.rs @@ -185,7 +185,12 @@ pub async fn thread_history( ) -> GlobalResult { let current_user_id = ctx.auth().dual_user(ctx.op_ctx()).await?; - internal_assert!(query.count <= 512, "`count` parameter too high"); + assert_with!( + query.count <= 512, + API_BAD_QUERY_PARAMETER, + parameter = "count", + error = "parameter too high" + ); assert::chat_thread_participant(&ctx, thread_id, current_user_id).await?; diff --git a/svc/api/cloud/src/convert/group.rs b/svc/api/cloud/src/convert/group.rs index 9060978c50..d174d8ea90 100644 --- a/svc/api/cloud/src/convert/group.rs +++ b/svc/api/cloud/src/convert/group.rs @@ -1,3 +1,5 @@ +use std::convert::TryInto; + use proto::backend::{self, pkg::*}; use rivet_cloud_server::models; use rivet_convert::ApiInto; @@ -38,7 +40,7 @@ pub fn summary( is_current_identity_member, publicity: internal_unwrap_owned!(backend::team::Publicity::from_i32(team.publicity)) .api_into(), - member_count: member_count as i32, + member_count: member_count.try_into()?, owner_identity_id: owner_user_id.to_string(), is_developer, }) diff --git a/svc/api/cloud/src/fetch/mod.rs b/svc/api/cloud/src/fetch/mod.rs index 7e199de996..73f4e25ab6 100644 --- a/svc/api/cloud/src/fetch/mod.rs +++ b/svc/api/cloud/src/fetch/mod.rs @@ -125,7 +125,7 @@ pub async fn game_summary_fetch( name_id: game.name_id.clone(), display_name: game.display_name.clone(), developer_group_id: developer_team_id.to_string(), - total_player_count: total_player_count as i32, + total_player_count: total_player_count.try_into()?, logo_url: util::route::game_logo( game.logo_upload_id.as_ref().map(common::Uuid::as_uuid), game.logo_file_name.as_ref(), diff --git a/svc/api/cloud/src/route/games/avatars.rs b/svc/api/cloud/src/route/games/avatars.rs index 309df4dd14..6270c99709 100644 --- a/svc/api/cloud/src/route/games/avatars.rs +++ b/svc/api/cloud/src/route/games/avatars.rs @@ -95,10 +95,14 @@ pub async fn prepare_avatar_upload( let user_id = ctx.auth().claims()?.as_user().ok().map(|x| x.user_id); - internal_assert!(body.content_length >= 0, "upload invalid"); - internal_assert!( + assert_with!( + body.content_length >= 0, + CLOUD_INVALID_CONFIG, + error = "`content_length` out of bounds" + ); + assert_with!( body.content_length < MAX_AVATAR_UPLOAD_SIZE, - "upload too large" + UPLOAD_TOO_LARGE ); let ext = if body.path.ends_with(".png") { @@ -116,7 +120,7 @@ pub async fn prepare_avatar_upload( backend::upload::PrepareFile { path: format!("image.{ext}"), mime: Some(format!("image/{ext}")), - content_length: body.content_length as u64, + content_length: body.content_length.try_into()?, nsfw_score_threshold: Some(util_nsfw::score_thresholds::USER_AVATAR), ..Default::default() }, diff --git a/svc/api/cloud/src/route/games/builds.rs b/svc/api/cloud/src/route/games/builds.rs index e2c65bfb57..165e83ba63 100644 --- a/svc/api/cloud/src/route/games/builds.rs +++ b/svc/api/cloud/src/route/games/builds.rs @@ -50,7 +50,9 @@ pub async fn get_builds( upload_id: internal_unwrap!(build.upload_id).as_uuid().to_string(), display_name: build.display_name.clone(), create_ts: util::timestamp::to_chrono(build.create_ts)?, - content_length: upload.map_or(0, |upload| upload.content_length) as i64, + content_length: upload + .map_or(0, |upload| upload.content_length) + .try_into()?, complete: upload.map_or(true, |upload| upload.complete_ts.is_some()), }) }) diff --git a/svc/api/cloud/src/route/games/cdn.rs b/svc/api/cloud/src/route/games/cdn.rs index d122b0ab07..6cca4b4aef 100644 --- a/svc/api/cloud/src/route/games/cdn.rs +++ b/svc/api/cloud/src/route/games/cdn.rs @@ -50,7 +50,9 @@ pub async fn get_sites( upload_id: internal_unwrap!(site.upload_id).as_uuid().to_string(), display_name: site.display_name.clone(), create_ts: util::timestamp::to_chrono(site.create_ts)?, - content_length: upload.map_or(0, |upload| upload.content_length) as i64, + content_length: upload + .map_or(0, |upload| upload.content_length) + .try_into()?, complete: upload.map_or(true, |upload| upload.complete_ts.is_some()), }) }) diff --git a/svc/api/cloud/src/route/games/matchmaker.rs b/svc/api/cloud/src/route/games/matchmaker.rs index 080573f99c..10bfaf1388 100644 --- a/svc/api/cloud/src/route/games/matchmaker.rs +++ b/svc/api/cloud/src/route/games/matchmaker.rs @@ -117,7 +117,7 @@ pub async fn get_lobby_logs( models::LogStream::StdOut => backend::nomad_log::StreamType::StdOut, models::LogStream::StdErr => backend::nomad_log::StreamType::StdErr, _ => { - util::panic_with!( + panic_with!( API_BAD_QUERY_PARAMETER, parameter = "stream", error = r#"Must be one of "std_out" or "std_err""#, @@ -213,7 +213,7 @@ pub async fn export_lobby_logs( models::LogStream::StdOut => backend::nomad_log::StreamType::StdOut, models::LogStream::StdErr => backend::nomad_log::StreamType::StdErr, models::LogStream::Unknown(_) => { - util::panic_with!(API_BAD_BODY, error = r#"Invalid "stream""#,); + panic_with!(API_BAD_BODY, error = r#"Invalid "stream""#,); } }; diff --git a/svc/api/cloud/src/route/games/mod.rs b/svc/api/cloud/src/route/games/mod.rs index c06adccebf..ac2c5b1830 100644 --- a/svc/api/cloud/src/route/games/mod.rs +++ b/svc/api/cloud/src/route/games/mod.rs @@ -4,13 +4,11 @@ use api_helper::{ anchor::{WatchIndexQuery, WatchResponse}, ctx::Ctx, }; -use futures_util::FutureExt; use proto::backend::{self, pkg::*}; use rivet_claims::ClaimsDecode; use rivet_cloud_server::models; use rivet_convert::{ApiInto, ApiTryFrom, ApiTryInto}; use rivet_operation::prelude::*; -use serde::Deserialize; use crate::{auth::Auth, convert, fetch}; @@ -472,11 +470,12 @@ pub async fn prepare_logo_upload( let user_id = ctx.auth().claims()?.as_user().ok().map(|x| x.user_id); - internal_assert!(body.content_length >= 0, "upload invalid"); - internal_assert!( - body.content_length < MAX_LOGO_UPLOAD_SIZE, - "upload too large" + assert_with!( + body.content_length >= 0, + CLOUD_INVALID_CONFIG, + error = "`content_length` out of bounds" ); + assert_with!(body.content_length < MAX_LOGO_UPLOAD_SIZE, UPLOAD_TOO_LARGE); let ext = if body.path.ends_with(".png") { "png" @@ -493,7 +492,7 @@ pub async fn prepare_logo_upload( backend::upload::PrepareFile { path: format!("logo.{ext}"), mime: Some(format!("image/{ext}")), - content_length: body.content_length as u64, + content_length: body.content_length.try_into()?, nsfw_score_threshold: Some(util_nsfw::score_thresholds::GAME_LOGO), ..Default::default() }, @@ -539,10 +538,14 @@ pub async fn prepare_banner_upload( let user_id = ctx.auth().claims()?.as_user().ok().map(|x| x.user_id); - internal_assert!(body.content_length >= 0, "upload invalid"); - internal_assert!( + assert_with!( + body.content_length >= 0, + CLOUD_INVALID_CONFIG, + error = "`content_length` out of bounds" + ); + assert_with!( body.content_length < MAX_BANNER_UPLOAD_SIZE, - "upload too large" + UPLOAD_TOO_LARGE ); let ext = if body.path.ends_with(".png") { @@ -560,7 +563,7 @@ pub async fn prepare_banner_upload( backend::upload::PrepareFile { path: format!("banner.{ext}"), mime: Some(format!("image/{ext}")), - content_length: body.content_length as u64, + content_length: body.content_length.try_into()?, nsfw_score_threshold: Some(util_nsfw::score_thresholds::GAME_BANNER), ..Default::default() }, diff --git a/svc/api/cloud/src/route/games/namespaces/logs.rs b/svc/api/cloud/src/route/games/namespaces/logs.rs index 5d0cd68900..42050afa70 100644 --- a/svc/api/cloud/src/route/games/namespaces/logs.rs +++ b/svc/api/cloud/src/route/games/namespaces/logs.rs @@ -4,7 +4,7 @@ use proto::{ common, }; use rivet_cloud_server::models; -use rivet_convert::ApiInto; +use rivet_convert::ApiTryInto; use rivet_operation::prelude::*; use serde::{Deserialize, Serialize}; use std::collections::HashSet; @@ -109,7 +109,7 @@ pub async fn get_lobby( Ok(models::GetNamespaceLobbyResponse { lobby: lobby.summary, perf_lists: Vec::new(), - metrics: metrics.map(ApiInto::api_into), + metrics: metrics.map(ApiTryInto::try_into).transpose()?, // Deprecated stdout_presigned_urls: Vec::new(), @@ -194,7 +194,7 @@ async fn fetch_lobby_logs(ctx: &Ctx, lobby_ids: Vec) -> GlobalResult models::LogsLobbyStatus::Stopped(models::LogsLobbyStatusStopped { stop_ts: util::timestamp::to_chrono(stop_ts)?, failed, - exit_code: exit_code as i32, + exit_code: exit_code.try_into()?, }) } else { models::LogsLobbyStatus::Running(models::Unit {}) diff --git a/svc/api/cloud/src/route/games/namespaces/mod.rs b/svc/api/cloud/src/route/games/namespaces/mod.rs index 283075f487..b35c0dabc7 100644 --- a/svc/api/cloud/src/route/games/namespaces/mod.rs +++ b/svc/api/cloud/src/route/games/namespaces/mod.rs @@ -276,33 +276,72 @@ pub async fn create_token_development( let _ns_data = assert::namespace_for_game(&ctx, game_id, namespace_id).await?; let lobby_ports = match (body.ports, body.lobby_ports) { - (Some(_), Some(_)) => internal_panic!("can not specify both ports and lobby_ports"), + (Some(_), Some(_)) => panic_with!( + CLOUD_INVALID_CONFIG, + error = "can not specify both `ports` and `lobby_ports`" + ), (Some(ports), None) => ports .into_iter() - .map(|(label, port)| backend::matchmaker::lobby_runtime::Port { - label, - target_port: port.port.map(|x| x as u32), - port_range: port.port_range.map(|x| { - backend::matchmaker::lobby_runtime::PortRange { - min: x.min as u32, - max: x.max as u32, - } - }), - proxy_protocol: - (ApiInto::::api_into( - port.protocol, - )) as i32, - proxy_kind: backend::matchmaker::lobby_runtime::ProxyKind::GameGuard as i32, + .map(|(label, port)| { + Ok(backend::matchmaker::lobby_runtime::Port { + label, + target_port: if let Some(port) = port.port { + assert_with!( + port >= 0, + CLOUD_INVALID_CONFIG, + error = "`port` out of bounds" + ); + + Some(port.try_into()?) + } else { + None + }, + port_range: if let Some(port_range) = port.port_range { + assert_with!( + port_range.min >= 0, + CLOUD_INVALID_CONFIG, + error = "`port_range.min` out of bounds" + ); + assert_with!( + port_range.max >= 0, + CLOUD_INVALID_CONFIG, + error = "`port_range.max` out of bounds" + ); + + Some(backend::matchmaker::lobby_runtime::PortRange { + min: port_range.min.try_into()?, + max: port_range.max.try_into()?, + }) + } else { + None + }, + proxy_protocol: + (ApiInto::::api_into( + port.protocol, + )) as i32, + proxy_kind: backend::matchmaker::lobby_runtime::ProxyKind::GameGuard as i32, + }) }) - .collect::>(), + .collect::>>()?, (None, Some(ports)) => { // Deprecated ports .into_iter() .map(|port| { + let target_port = unwrap_with_owned!( + port.target_port, + CLOUD_INVALID_CONFIG, + error = "expected `target_port`" + ); + assert_with!( + target_port >= 0, + CLOUD_INVALID_CONFIG, + error = "`target_port` out of bounds" + ); + Ok(backend::matchmaker::lobby_runtime::Port { label: port.label, - target_port: Some(internal_unwrap_owned!(port.target_port) as u32), + target_port: Some(target_port.try_into()?), port_range: None, proxy_protocol: (ApiInto::::api_into( @@ -458,17 +497,25 @@ pub async fn update_mm_config( ) -> GlobalResult { let _ns_data = assert::namespace_for_game(&ctx, game_id, namespace_id).await?; - internal_assert!(body.lobby_count_max >= 0, "invalid `lobby_count_max`"); - internal_assert!(body.max_players >= 0, "invalid `max_players`"); + assert_with!( + body.lobby_count_max >= 0, + CLOUD_INVALID_CONFIG, + error = "`lobby_count_max` out of bounds" + ); + assert_with!( + body.max_players >= 0, + CLOUD_INVALID_CONFIG, + error = "`max_players` out of bounds" + ); let _res = op!([ctx] mm_config_namespace_config_set { namespace_id: Some(namespace_id.into()), - lobby_count_max: body.lobby_count_max as u32, - max_players_per_client: body.max_players as u32, - max_players_per_client_vpn: body.max_players as u32, - max_players_per_client_proxy: body.max_players as u32, - max_players_per_client_tor: body.max_players as u32, - max_players_per_client_hosting: body.max_players as u32, + lobby_count_max: body.lobby_count_max.try_into()?, + max_players_per_client: body.max_players.try_into()?, + max_players_per_client_vpn: body.max_players.try_into()?, + max_players_per_client_proxy: body.max_players.try_into()?, + max_players_per_client_tor: body.max_players.try_into()?, + max_players_per_client_hosting: body.max_players.try_into()?, }) .await?; @@ -581,17 +628,25 @@ pub async fn validate_mm_config( ) -> GlobalResult { let _ns_data = assert::namespace_for_game(&ctx, game_id, namespace_id).await?; - internal_assert!(body.lobby_count_max >= 0, "invalid `lobby_count_max`"); - internal_assert!(body.max_players >= 0, "invalid `max_players`"); + assert_with!( + body.lobby_count_max >= 0, + CLOUD_INVALID_CONFIG, + error = "`lobby_count_max` out of bounds" + ); + assert_with!( + body.max_players >= 0, + CLOUD_INVALID_CONFIG, + error = "`max_players` out of bounds" + ); let res = op!([ctx] mm_config_namespace_config_validate { namespace_id: Some(namespace_id.into()), - lobby_count_max: body.lobby_count_max as u32, - max_players_per_client: body.max_players as u32, - max_players_per_client_vpn: body.max_players as u32, - max_players_per_client_proxy: body.max_players as u32, - max_players_per_client_tor: body.max_players as u32, - max_players_per_client_hosting: body.max_players as u32, + lobby_count_max: body.lobby_count_max.try_into()?, + max_players_per_client: body.max_players.try_into()?, + max_players_per_client_vpn: body.max_players.try_into()?, + max_players_per_client_proxy: body.max_players.try_into()?, + max_players_per_client_tor: body.max_players.try_into()?, + max_players_per_client_hosting: body.max_players.try_into()?, }) .await?; diff --git a/svc/api/cloud/src/route/groups.rs b/svc/api/cloud/src/route/groups.rs index e1108f3389..8f29650ba0 100644 --- a/svc/api/cloud/src/route/groups.rs +++ b/svc/api/cloud/src/route/groups.rs @@ -1,7 +1,7 @@ use api_helper::{anchor::WatchIndexQuery, ctx::Ctx}; use proto::backend; use rivet_cloud_server::models; -use rivet_convert::{ApiInto, ApiTryInto}; +use rivet_convert::ApiInto; use rivet_operation::prelude::*; use serde::{Deserialize, Serialize}; diff --git a/svc/api/cloud/src/route/tiers.rs b/svc/api/cloud/src/route/tiers.rs index be677b2efe..72b86253d8 100644 --- a/svc/api/cloud/src/route/tiers.rs +++ b/svc/api/cloud/src/route/tiers.rs @@ -1,6 +1,6 @@ use api_helper::{anchor::WatchIndexQuery, ctx::Ctx}; use rivet_cloud_server::models; -use rivet_convert::ApiInto; +use rivet_convert::ApiTryInto; use rivet_operation::prelude::*; use crate::auth::Auth; @@ -23,7 +23,7 @@ pub async fn list_tiers( .tiers .clone() .into_iter() - .map(ApiInto::api_into) - .collect::>(), + .map(ApiTryInto::try_into) + .collect::>>()?, }) } diff --git a/svc/api/group/src/convert/group.rs b/svc/api/group/src/convert/group.rs index a70995ebf8..af65c3639c 100644 --- a/svc/api/group/src/convert/group.rs +++ b/svc/api/group/src/convert/group.rs @@ -1,5 +1,5 @@ use proto::backend::{self, pkg::*}; -use rivet_convert::ApiInto; +use rivet_convert::{ApiInto, ApiTryInto}; use rivet_group_server::models; use rivet_operation::prelude::*; @@ -56,7 +56,7 @@ pub fn summary( is_current_identity_member, publicity: internal_unwrap_owned!(backend::team::Publicity::from_i32(team.publicity)) .api_into(), - member_count: member_count as i32, + member_count: member_count.try_into()?, owner_identity_id: owner_user_id.to_string(), is_developer, }) diff --git a/svc/api/group/src/route/groups.rs b/svc/api/group/src/route/groups.rs index b5cf4c0a45..817bbb8fa8 100644 --- a/svc/api/group/src/route/groups.rs +++ b/svc/api/group/src/route/groups.rs @@ -10,7 +10,7 @@ use proto::{ backend::{self, pkg::*}, common, }; -use rivet_convert::ApiInto; +use rivet_convert::{ApiInto, ApiTryInto}; use rivet_group_server::models; use rivet_operation::prelude::*; use serde::{Deserialize, Serialize}; @@ -888,9 +888,9 @@ pub async fn prepare_avatar_upload( assert::user_registered(&ctx, user_ent.user_id).await?; internal_assert!(body.content_length >= 0, "Upload invalid"); - internal_assert!( + assert_with!( body.content_length < MAX_AVATAR_UPLOAD_SIZE, - "upload too large" + UPLOAD_TOO_LARGE ); let ext = if body.path.ends_with(".png") { @@ -908,7 +908,7 @@ pub async fn prepare_avatar_upload( backend::upload::PrepareFile { path: format!("image.{}", ext), mime: Some(format!("image/{}", ext)), - content_length: body.content_length as u64, + content_length: body.content_length.try_into()?, nsfw_score_threshold: Some(util_nsfw::score_thresholds::TEAM_AVATAR), ..Default::default() }, @@ -990,7 +990,7 @@ pub async fn create_invite( let res = msg!([ctx] team_invite::msg::create(group_id) -> team_invite::msg::create_complete { team_id: Some(group_id.into()), ttl: body.ttl, - max_use_count: body.use_count.map(|v| v as u64), + max_use_count: body.use_count.map(|v| v.try_into()).transpose()?, }) .await?; diff --git a/svc/api/identity/src/route/events.rs b/svc/api/identity/src/route/events.rs index 81a7947893..bc34cf8601 100644 --- a/svc/api/identity/src/route/events.rs +++ b/svc/api/identity/src/route/events.rs @@ -7,7 +7,7 @@ use proto::{ common, }; use rivet_api::models; -use rivet_convert::{convert, fetch}; +use rivet_convert::{convert, fetch, ApiTryInto}; use rivet_operation::prelude::*; use std::collections::{HashMap, HashSet}; @@ -789,8 +789,8 @@ fn build_port( hostname: network.ip.clone(), port: None, port_range: Some(Box::new(models::MatchmakerJoinPortRange { - min: port_range.min as i32, - max: port_range.max as i32, + min: port_range.min.try_into()?, + max: port_range.max.try_into()?, })), is_tls: false, }) diff --git a/svc/api/identity/src/route/identities.rs b/svc/api/identity/src/route/identities.rs index eeec35a2af..2b5a2bdadd 100644 --- a/svc/api/identity/src/route/identities.rs +++ b/svc/api/identity/src/route/identities.rs @@ -12,7 +12,7 @@ use proto::{ }; use rivet_api::models; use rivet_claims::ClaimsDecode; -use rivet_convert::{convert, fetch, ApiInto}; +use rivet_convert::{convert, fetch, ApiInto, ApiTryInto}; use rivet_operation::prelude::*; use serde::{Deserialize, Serialize}; @@ -431,7 +431,7 @@ pub async fn update_profile( msg!([ctx] user::msg::profile_set(user_ent.user_id) -> user::msg::update { user_id: Some(user_ent.user_id.into()), display_name: body.display_name.clone(), - account_number: body.account_number.map(|n| n as u32), + account_number: body.account_number.map(|n| n.try_into()).transpose()?, bio: body.bio.clone(), }) .await?; @@ -589,13 +589,18 @@ pub async fn validate_profile( ) -> GlobalResult { let user_ent = ctx.auth().user(ctx.op_ctx()).await?; + internal_assert!( + body.account_number.unwrap_or_default() >= 0, + "invalid parameter account_number`" + ); + let res = op!([ctx] user_profile_validate { user_id: Some(user_ent.user_id.into()), display_name: body.display_name.clone(), account_number: body.account_number - .filter(|n| n > &0) - .map(|n| n as u32), + .map(|n| n.try_into()) + .transpose()?, bio: body.bio.clone(), }) .await?; @@ -618,9 +623,9 @@ pub async fn prepare_avatar_upload( assert::user_registered(&ctx, user_ent.user_id).await?; internal_assert!(body.content_length >= 0, "upload invalid"); - internal_assert!( + assert_with!( body.content_length < MAX_AVATAR_UPLOAD_SIZE, - "upload too large" + UPLOAD_TOO_LARGE ); let ext = if body.path.ends_with(".png") { @@ -638,7 +643,7 @@ pub async fn prepare_avatar_upload( backend::upload::PrepareFile { path: format!("image.{}", ext), mime: Some(format!("image/{}", ext)), - content_length: body.content_length as u64, + content_length: body.content_length.try_into()?, nsfw_score_threshold: Some(util_nsfw::score_thresholds::USER_AVATAR), ..Default::default() }, diff --git a/svc/api/matchmaker/src/route/lobbies.rs b/svc/api/matchmaker/src/route/lobbies.rs index b6603cbf7b..f6475bcb83 100644 --- a/svc/api/matchmaker/src/route/lobbies.rs +++ b/svc/api/matchmaker/src/route/lobbies.rs @@ -862,14 +862,17 @@ async fn dev_mock_lobby( .target_port .map(|port| format!("{}:{port}", ns_dev_ent.hostname)), hostname: ns_dev_ent.hostname.clone(), - port: port.target_port.map(|x| x as i32), + port: port.target_port.map(|x| x.try_into()).transpose()?, port_range: port .port_range .as_ref() - .map(|x| models::MatchmakerJoinPortRange { - min: x.min as i32, - max: x.max as i32, + .map(|x| { + GlobalResult::Ok(models::MatchmakerJoinPortRange { + min: x.min.try_into()?, + max: x.max.try_into()?, + }) }) + .transpose()? .map(Box::new), is_tls: matches!( port.proxy_protocol, @@ -948,17 +951,20 @@ fn build_port( .first() .map(|hostname| (proxied_port, hostname)) }) - .map(|(proxied_port, hostname)| models::MatchmakerJoinPort { - host: Some(format!("{}:{}", hostname, proxied_port.ingress_port)), - hostname: hostname.clone(), - port: Some(proxied_port.ingress_port as i32), - port_range: None, - is_tls: matches!( - mm_proxy_protocol, - MmProxyProtocol::Https | MmProxyProtocol::TcpTls - ), + .map(|(proxied_port, hostname)| { + GlobalResult::Ok(models::MatchmakerJoinPort { + host: Some(format!("{}:{}", hostname, proxied_port.ingress_port)), + hostname: hostname.clone(), + port: Some(proxied_port.ingress_port.try_into()?), + port_range: None, + is_tls: matches!( + mm_proxy_protocol, + MmProxyProtocol::Https | MmProxyProtocol::TcpTls + ), + }) }) .next() + .transpose()? } (MmProxyKind::None, MmProxyProtocol::Tcp | MmProxyProtocol::Udp) => { let port_range = internal_unwrap!(port.port_range); @@ -973,8 +979,8 @@ fn build_port( hostname: network.ip.clone(), port: None, port_range: Some(Box::new(models::MatchmakerJoinPortRange { - min: port_range.min as i32, - max: port_range.max as i32, + min: port_range.min.try_into()?, + max: port_range.max.try_into()?, })), is_tls: false, }) diff --git a/svc/api/matchmaker/src/route/players.rs b/svc/api/matchmaker/src/route/players.rs index 07c5d9dfc4..a0590e3d4a 100644 --- a/svc/api/matchmaker/src/route/players.rs +++ b/svc/api/matchmaker/src/route/players.rs @@ -1,3 +1,5 @@ +use std::convert::TryInto; + use api_helper::{anchor::WatchIndexQuery, ctx::Ctx}; use proto::backend::pkg::*; use rivet_api::models; @@ -240,11 +242,13 @@ pub async fn statistics( } } - let player_count = internal_unwrap_owned!(lobby_player_counts_res - .lobbies - .iter() - .find(|l| l.lobby_id == lobby.lobby_id)) - .total_player_count as i64; + let player_count = TryInto::::try_into( + internal_unwrap_owned!(lobby_player_counts_res + .lobbies + .iter() + .find(|l| l.lobby_id == lobby.lobby_id)) + .total_player_count, + )?; let lobby_group_id = internal_unwrap!(lobby.lobby_group_id).as_uuid(); let lobby_group = internal_unwrap_owned!(all_lobby_groups.get(&lobby_group_id)); let region = internal_unwrap_owned!(regions_res diff --git a/svc/api/party/src/convert/party.rs b/svc/api/party/src/convert/party.rs index 6eb25caaeb..c108cf74ff 100644 --- a/svc/api/party/src/convert/party.rs +++ b/svc/api/party/src/convert/party.rs @@ -1,3 +1,5 @@ +use std::convert::TryInto; + use proto::backend; use rivet_operation::prelude::*; use rivet_party_server::models; @@ -42,7 +44,7 @@ pub fn summary( Ok(models::PartySummary { party_id: party_id.to_string(), create_ts: util::timestamp::to_chrono(party.create_ts)?, - party_size: party.party_size as i32, + party_size: party.party_size.try_into()?, activity: convert::party::activity(party.state.as_ref(), games)?, publicity: models::PartyPublicity { public: convert::party::publicity_level(publicity.public), @@ -72,7 +74,7 @@ pub fn profile( Ok(models::PartyProfile { party_id: party_id.to_string(), create_ts: util::timestamp::to_chrono(party.create_ts)?, - party_size: party.party_size as i32, + party_size: party.party_size.try_into()?, activity: convert::party::activity(party.state.as_ref(), games)?, publicity: publicity(internal_unwrap!(party.publicity)), external: models::PartyExternalLinks { diff --git a/svc/api/party/src/route/parties.rs b/svc/api/party/src/route/parties.rs index f99c0e3c90..193589633d 100644 --- a/svc/api/party/src/route/parties.rs +++ b/svc/api/party/src/route/parties.rs @@ -1,3 +1,5 @@ +use std::convert::TryInto; + use api_helper::{ anchor::{WatchIndexQuery, WatchResponse}, ctx::Ctx, @@ -326,7 +328,7 @@ pub async fn create_party( msg!([ctx] party::msg::create(party_id) -> party::msg::create_complete { party_id: Some(party_id.into()), leader_user_id: Some(current_user_id.into()), - party_size: body.party_size as u32, + party_size: body.party_size.try_into()?, initial_state: initial_state, publicity: body.publicity.map(|x| party::msg::create::message::Publicity { public: x.public.map(convert::party::publicity_level_to_proto), diff --git a/svc/api/portal/src/build.rs b/svc/api/portal/src/build.rs index ecb5561406..c8eb22e8ff 100644 --- a/svc/api/portal/src/build.rs +++ b/svc/api/portal/src/build.rs @@ -1,6 +1,6 @@ use api_helper::ctx::Ctx; use proto::{backend, common}; -use rivet_convert::ApiInto; +use rivet_convert::{ApiInto, ApiTryInto}; use rivet_operation::prelude::*; use rivet_portal_server::models; @@ -72,7 +72,7 @@ pub async fn group_summaries( team_data.publicity )) .api_into(), - member_count: member_count as i32, + member_count: member_count.try_into()?, owner_identity_id: owner_user_id.to_string(), is_developer, }) diff --git a/svc/pkg/build/ops/create/src/lib.rs b/svc/pkg/build/ops/create/src/lib.rs index 23665bb8f7..740bf32322 100644 --- a/svc/pkg/build/ops/create/src/lib.rs +++ b/svc/pkg/build/ops/create/src/lib.rs @@ -45,9 +45,9 @@ async fn handle( ); internal_assert!(util::check::docker_ident(tag_tag), "invalid image tag tag"); - internal_assert!( - image_file.content_length <= MAX_UPLOAD_SIZE, - "upload too large" + assert_with!( + image_file.content_length < MAX_UPLOAD_SIZE, + UPLOAD_TOO_LARGE ); // Check if build is unique diff --git a/svc/pkg/cdn/ops/site-create/src/lib.rs b/svc/pkg/cdn/ops/site-create/src/lib.rs index ee37bdded9..cc1764e453 100644 --- a/svc/pkg/cdn/ops/site-create/src/lib.rs +++ b/svc/pkg/cdn/ops/site-create/src/lib.rs @@ -12,12 +12,12 @@ async fn handle( util::check::display_name_long(&ctx.display_name), "invalid display name" ); - internal_assert!( + assert_with!( ctx.files .iter() .fold(0, |acc, file| acc + file.content_length) < MAX_UPLOAD_SIZE, - "upload too large" + UPLOAD_TOO_LARGE ); // Validate game exists From d163f98deb3f61ffc98b5879348a99206e47f536 Mon Sep 17 00:00:00 2001 From: MasterPtato Date: Sat, 17 Jun 2023 21:27:10 +0000 Subject: [PATCH 2/3] Format --- lib/bolt/core/src/tasks/check.rs | 2 +- lib/convert/src/impls/cloud/mod.rs | 26 ++- .../cloud/version/matchmaker/game_mode.rs | 168 +++++++++++------- .../cloud/version/matchmaker/lobby_group.rs | 55 ++++-- .../src/impls/cloud/version/matchmaker/mod.rs | 28 ++- lib/convert/src/impls/mod.rs | 10 +- 6 files changed, 190 insertions(+), 99 deletions(-) diff --git a/lib/bolt/core/src/tasks/check.rs b/lib/bolt/core/src/tasks/check.rs index e21ec2e999..8b1f610688 100644 --- a/lib/bolt/core/src/tasks/check.rs +++ b/lib/bolt/core/src/tasks/check.rs @@ -158,7 +158,7 @@ async fn check_svcs( .arg("clippy::wildcard_imports") .arg("-A") .arg("clippy::module_name_repetitions") - .arg("-A") + .arg("-W") .arg("clippy::cast_possible_truncation") .arg("-A") .arg("clippy::missing_errors_doc") diff --git a/lib/convert/src/impls/cloud/mod.rs b/lib/convert/src/impls/cloud/mod.rs index 067e53473d..682a963e5d 100644 --- a/lib/convert/src/impls/cloud/mod.rs +++ b/lib/convert/src/impls/cloud/mod.rs @@ -100,13 +100,23 @@ impl ApiTryFrom for models::SvcPerf { impl ApiTryFrom for models::SvcMetrics { type Error = GlobalError; - - fn try_from(value: job_run::metrics_log::response::Metrics) -> GlobalResult { + + fn try_from( + value: job_run::metrics_log::response::Metrics, + ) -> GlobalResult { Ok(models::SvcMetrics { job: value.job, cpu: value.cpu, - memory: value.memory.into_iter().map(|v| v.try_into()).collect::, _>>()?, - memory_max: value.memory_max.into_iter().map(|v| v.try_into()).collect::, _>>()?, + memory: value + .memory + .into_iter() + .map(|v| v.try_into()) + .collect::, _>>()?, + memory_max: value + .memory_max + .into_iter() + .map(|v| v.try_into()) + .collect::, _>>()?, allocated_memory: value.allocated_memory.try_into()?, }) } @@ -145,7 +155,11 @@ impl ApiTryFrom for backend::upload::PrepareFile { type Error = GlobalError; fn try_from(value: models::UploadPrepareFile) -> GlobalResult { - assert_with!(value.content_length >= 0, MATCHMAKER_INVALID_VERSION_CONFIG, error = "`file.content_length` out of bounds"); + assert_with!( + value.content_length >= 0, + MATCHMAKER_INVALID_VERSION_CONFIG, + error = "`file.content_length` out of bounds" + ); Ok(backend::upload::PrepareFile { path: value.path, @@ -223,7 +237,7 @@ mod openapi { impl ApiTryFrom for models::RegionTier { type Error = GlobalError; - + fn try_from(value: backend::region::Tier) -> GlobalResult { Ok(models::RegionTier { tier_name_id: value.tier_name_id.to_owned(), diff --git a/lib/convert/src/impls/cloud/version/matchmaker/game_mode.rs b/lib/convert/src/impls/cloud/version/matchmaker/game_mode.rs index edd2366713..9d1c45412f 100644 --- a/lib/convert/src/impls/cloud/version/matchmaker/game_mode.rs +++ b/lib/convert/src/impls/cloud/version/matchmaker/game_mode.rs @@ -34,76 +34,100 @@ pub fn game_mode_to_proto( .unwrap_or(max_players_normal); // TODO: Make this return a 400 error instead - assert_with!(max_players_normal >= 0, MATCHMAKER_INVALID_VERSION_CONFIG, error = "`max_players` out of bounds"); - assert_with!(max_players_direct >= 0, MATCHMAKER_INVALID_VERSION_CONFIG, error = "`max_players_direct` out of bounds"); - assert_with!(max_players_party >= 0, MATCHMAKER_INVALID_VERSION_CONFIG, error = "`max_players_party` out of bounds"); + assert_with!( + max_players_normal >= 0, + MATCHMAKER_INVALID_VERSION_CONFIG, + error = "`max_players` out of bounds" + ); + assert_with!( + max_players_direct >= 0, + MATCHMAKER_INVALID_VERSION_CONFIG, + error = "`max_players_direct` out of bounds" + ); + assert_with!( + max_players_party >= 0, + MATCHMAKER_INVALID_VERSION_CONFIG, + error = "`max_players_party` out of bounds" + ); // Derive runtime - let runtime = if let Some(either_runtime) = game_mode.docker.as_ref().or(matchmaker.docker.as_ref()) { - let args = either_runtime.args.clone().unwrap_or_default(); + let runtime = + if let Some(either_runtime) = game_mode.docker.as_ref().or(matchmaker.docker.as_ref()) { + let args = either_runtime.args.clone().unwrap_or_default(); - let mut env_vars = HashMap::::new(); - if let Some(env) = matchmaker.docker.as_ref().and_then(|x| x.env.clone()) { - env_vars.extend(env); - } - if let Some(env) = game_mode.docker.as_ref().and_then(|x| x.env.clone()) { - env_vars.extend(env); - } + let mut env_vars = HashMap::::new(); + if let Some(env) = matchmaker.docker.as_ref().and_then(|x| x.env.clone()) { + env_vars.extend(env); + } + if let Some(env) = game_mode.docker.as_ref().and_then(|x| x.env.clone()) { + env_vars.extend(env); + } - let network_mode = either_runtime.network_mode - .unwrap_or(models::CloudVersionMatchmakerNetworkMode::Bridge); - - let ports = either_runtime.ports.clone().unwrap_or_default(); - - Some(backend::matchmaker::LobbyRuntime { - runtime: Some(backend::matchmaker::lobby_runtime::Runtime::Docker( - backend::matchmaker::lobby_runtime::Docker { - build_id: either_runtime.image_id.map(Into::into), - args, - env_vars: env_vars - .into_iter() - .map(|(key, value)| backend::matchmaker::lobby_runtime::EnvVar { - key, - value, - }) - .collect(), - network_mode: - ApiInto::::api_into( - network_mode, - ) as i32, - ports: ports - .into_iter() - .map(|(label, value)| { - let proxy_protocol = value - .protocol - .unwrap_or(models::CloudVersionMatchmakerPortProtocol::Https); - let proxy_kind = value - .proxy - .unwrap_or(models::CloudVersionMatchmakerProxyKind::GameGuard); - - GlobalResult::Ok(backend::matchmaker::lobby_runtime::Port { - label, - target_port: value.port.map(|x| { - assert_with!(x >= 0, MATCHMAKER_INVALID_VERSION_CONFIG, error = "`port` out of bounds"); - - Ok(x.try_into()?) - }).transpose()?, - port_range: value.port_range.map(|x| (*x).try_into()).transpose()?, - proxy_protocol: ApiInto::< - backend::matchmaker::lobby_runtime::ProxyProtocol, - >::api_into(proxy_protocol) as i32, - proxy_kind: ApiInto::< - backend::matchmaker::lobby_runtime::ProxyKind, - >::api_into(proxy_kind) as i32, + let network_mode = either_runtime + .network_mode + .unwrap_or(models::CloudVersionMatchmakerNetworkMode::Bridge); + + let ports = either_runtime.ports.clone().unwrap_or_default(); + + Some(backend::matchmaker::LobbyRuntime { + runtime: Some(backend::matchmaker::lobby_runtime::Runtime::Docker( + backend::matchmaker::lobby_runtime::Docker { + build_id: either_runtime.image_id.map(Into::into), + args, + env_vars: env_vars + .into_iter() + .map(|(key, value)| backend::matchmaker::lobby_runtime::EnvVar { + key, + value, }) - }) - .collect::>()?, - }, - )), - }) - } else { - None - }; + .collect(), + network_mode: + ApiInto::::api_into( + network_mode, + ) as i32, + ports: ports + .into_iter() + .map(|(label, value)| { + let proxy_protocol = value + .protocol + .unwrap_or(models::CloudVersionMatchmakerPortProtocol::Https); + let proxy_kind = value + .proxy + .unwrap_or(models::CloudVersionMatchmakerProxyKind::GameGuard); + + GlobalResult::Ok(backend::matchmaker::lobby_runtime::Port { + label, + target_port: value + .port + .map(|x| { + assert_with!( + x >= 0, + MATCHMAKER_INVALID_VERSION_CONFIG, + error = "`port` out of bounds" + ); + + Ok(x.try_into()?) + }) + .transpose()?, + port_range: value + .port_range + .map(|x| (*x).try_into()) + .transpose()?, + proxy_protocol: ApiInto::< + backend::matchmaker::lobby_runtime::ProxyProtocol, + >::api_into(proxy_protocol) as i32, + proxy_kind: ApiInto::< + backend::matchmaker::lobby_runtime::ProxyKind, + >::api_into(proxy_kind) as i32, + }) + }) + .collect::>()?, + }, + )), + }) + } else { + None + }; Ok(backend::matchmaker::LobbyGroup { name_id, @@ -290,9 +314,17 @@ impl ApiTryFrom fn try_from( value: models::CloudVersionMatchmakerGameModeIdleLobbiesConfig, ) -> GlobalResult { - assert_with!(value.min >= 0, MATCHMAKER_INVALID_VERSION_CONFIG, error = "`idle_lobbies.min` out of bounds"); - assert_with!(value.max >= 0, MATCHMAKER_INVALID_VERSION_CONFIG, error = "`idle_lobbies.max` out of bounds"); - + assert_with!( + value.min >= 0, + MATCHMAKER_INVALID_VERSION_CONFIG, + error = "`idle_lobbies.min` out of bounds" + ); + assert_with!( + value.max >= 0, + MATCHMAKER_INVALID_VERSION_CONFIG, + error = "`idle_lobbies.max` out of bounds" + ); + Ok(backend::matchmaker::lobby_group::IdleLobbies { min_idle_lobbies: value.min.try_into()?, max_idle_lobbies: value.max.try_into()?, diff --git a/lib/convert/src/impls/cloud/version/matchmaker/lobby_group.rs b/lib/convert/src/impls/cloud/version/matchmaker/lobby_group.rs index d4104a2a05..6c30784ae0 100644 --- a/lib/convert/src/impls/cloud/version/matchmaker/lobby_group.rs +++ b/lib/convert/src/impls/cloud/version/matchmaker/lobby_group.rs @@ -8,10 +8,22 @@ impl ApiTryFrom for backend::matchmake type Error = GlobalError; fn try_from(value: models::CloudVersionMatchmakerLobbyGroup) -> GlobalResult { - assert_with!(value.max_players_normal >= 0, MATCHMAKER_INVALID_VERSION_CONFIG, error = "`max_players` out of bounds"); - assert_with!(value.max_players_direct >= 0, MATCHMAKER_INVALID_VERSION_CONFIG, error = "`max_players_direct` out of bounds"); - assert_with!(value.max_players_party >= 0, MATCHMAKER_INVALID_VERSION_CONFIG, error = "`max_players_party` out of bounds"); - + assert_with!( + value.max_players_normal >= 0, + MATCHMAKER_INVALID_VERSION_CONFIG, + error = "`max_players` out of bounds" + ); + assert_with!( + value.max_players_direct >= 0, + MATCHMAKER_INVALID_VERSION_CONFIG, + error = "`max_players_direct` out of bounds" + ); + assert_with!( + value.max_players_party >= 0, + MATCHMAKER_INVALID_VERSION_CONFIG, + error = "`max_players_party` out of bounds" + ); + Ok(backend::matchmaker::LobbyGroup { name_id: value.name_id, @@ -90,8 +102,16 @@ impl ApiTryFrom fn try_from( value: models::CloudVersionMatchmakerLobbyGroupIdleLobbiesConfig, ) -> GlobalResult { - assert_with!(value.min_idle_lobbies >= 0, MATCHMAKER_INVALID_VERSION_CONFIG, error = "`min_idle_lobbies` out of bounds"); - assert_with!(value.max_idle_lobbies >= 0, MATCHMAKER_INVALID_VERSION_CONFIG, error = "`max_idle_lobbies` out of bounds"); + assert_with!( + value.min_idle_lobbies >= 0, + MATCHMAKER_INVALID_VERSION_CONFIG, + error = "`min_idle_lobbies` out of bounds" + ); + assert_with!( + value.max_idle_lobbies >= 0, + MATCHMAKER_INVALID_VERSION_CONFIG, + error = "`max_idle_lobbies` out of bounds" + ); Ok(backend::matchmaker::lobby_group::IdleLobbies { min_idle_lobbies: value.min_idle_lobbies.try_into()?, @@ -212,11 +232,18 @@ impl ApiTryFrom Ok(backend::matchmaker::lobby_runtime::Port { label: value.label, - target_port: value.target_port.map(|x| { - assert_with!(x >= 0, MATCHMAKER_INVALID_VERSION_CONFIG, error = "`target_port` out of bounds"); - - Ok(x.try_into()?) - }).transpose()?, + target_port: value + .target_port + .map(|x| { + assert_with!( + x >= 0, + MATCHMAKER_INVALID_VERSION_CONFIG, + error = "`target_port` out of bounds" + ); + + Ok(x.try_into()?) + }) + .transpose()?, port_range: value.port_range.map(|x| (*x).try_into()).transpose()?, proxy_protocol: (ApiInto::::api_into( value.proxy_protocol, @@ -235,7 +262,11 @@ impl ApiTryFrom Ok(models::CloudVersionMatchmakerLobbyGroupRuntimeDockerPort { label: value.label, target_port: value.target_port.map(|x| x.try_into()).transpose()?, - port_range: value.port_range.map(ApiTryInto::try_into).transpose()?.map(Box::new), + port_range: value + .port_range + .map(ApiTryInto::try_into) + .transpose()? + .map(Box::new), proxy_protocol: internal_unwrap_owned!( backend::matchmaker::lobby_runtime::ProxyProtocol::from_i32(value.proxy_protocol) ) diff --git a/lib/convert/src/impls/cloud/version/matchmaker/mod.rs b/lib/convert/src/impls/cloud/version/matchmaker/mod.rs index cbefa9eefa..f9ea93a8eb 100644 --- a/lib/convert/src/impls/cloud/version/matchmaker/mod.rs +++ b/lib/convert/src/impls/cloud/version/matchmaker/mod.rs @@ -111,10 +111,17 @@ impl ApiTryFrom { type Error = GlobalError; - fn try_from(value: models::CloudVersionMatchmakerPortRange, - ) -> GlobalResult { - assert_with!(value.min >= 0, MATCHMAKER_INVALID_VERSION_CONFIG, error = "`port_range.min` out of bounds"); - assert_with!(value.max >= 0, MATCHMAKER_INVALID_VERSION_CONFIG, error = "`port_range.max` out of bounds"); + fn try_from(value: models::CloudVersionMatchmakerPortRange) -> GlobalResult { + assert_with!( + value.min >= 0, + MATCHMAKER_INVALID_VERSION_CONFIG, + error = "`port_range.min` out of bounds" + ); + assert_with!( + value.max >= 0, + MATCHMAKER_INVALID_VERSION_CONFIG, + error = "`port_range.max` out of bounds" + ); Ok(backend::matchmaker::lobby_runtime::PortRange { min: value.min.try_into()?, @@ -254,9 +261,16 @@ impl ApiTryFrom for backend::captcha::Cap type Error = GlobalError; fn try_from(value: models::CloudVersionMatchmakerCaptcha) -> GlobalResult { - assert_with!(value.requests_before_reverify >= 0, MATCHMAKER_INVALID_VERSION_CONFIG, error = "`requests_before_reverify` out of bounds"); - assert_with!(value.verification_ttl >= 0, MATCHMAKER_INVALID_VERSION_CONFIG, error = "`verification_ttl` out of bounds"); - + assert_with!( + value.requests_before_reverify >= 0, + MATCHMAKER_INVALID_VERSION_CONFIG, + error = "`requests_before_reverify` out of bounds" + ); + assert_with!( + value.verification_ttl >= 0, + MATCHMAKER_INVALID_VERSION_CONFIG, + error = "`verification_ttl` out of bounds" + ); Ok(backend::captcha::CaptchaConfig { requests_before_reverify: value.requests_before_reverify.try_into()?, diff --git a/lib/convert/src/impls/mod.rs b/lib/convert/src/impls/mod.rs index 8004caa7d3..f20d4362a8 100644 --- a/lib/convert/src/impls/mod.rs +++ b/lib/convert/src/impls/mod.rs @@ -12,10 +12,10 @@ pub mod user; // Reimplement conversions for ease of use in this module mod num { use crate::ApiTryFrom; - + impl ApiTryFrom for u32 { type Error = std::num::TryFromIntError; - + fn try_from(v: i32) -> Result { std::convert::TryInto::try_into(v) } @@ -23,7 +23,7 @@ mod num { impl ApiTryFrom for i32 { type Error = std::num::TryFromIntError; - + fn try_from(v: u32) -> Result { std::convert::TryInto::try_into(v) } @@ -31,7 +31,7 @@ mod num { impl ApiTryFrom for i64 { type Error = std::num::TryFromIntError; - + fn try_from(v: u64) -> Result { std::convert::TryInto::try_into(v) } @@ -39,7 +39,7 @@ mod num { impl ApiTryFrom for u64 { type Error = std::num::TryFromIntError; - + fn try_from(v: i64) -> Result { std::convert::TryInto::try_into(v) } From 6d75b60ea4ca622b42ccc5f75f675e07fad9b101 Mon Sep 17 00:00:00 2001 From: MasterPtato Date: Mon, 19 Jun 2023 17:55:10 +0000 Subject: [PATCH 3/3] Fix error title --- errors/cloud/invalid-config.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/errors/cloud/invalid-config.md b/errors/cloud/invalid-config.md index 143dcc4cba..c421dbcb40 100644 --- a/errors/cloud/invalid-config.md +++ b/errors/cloud/invalid-config.md @@ -4,6 +4,6 @@ description = "The given config was invalid: {error}" http_status = 400 --- -# Matchmaker Invalid Config +# Cloud Invalid Config The given config was invalid.