diff --git a/.gitignore b/.gitignore index aef926e8..ce32d891 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,14 @@ /.venv *.pyc __pycache__ + +# +# Artifacts from the Rust client generation process +# +/crates/tower-api/.openapi-generator-ignore +/crates/tower-api/.openapi-generator/ +/crates/tower-api/.travis.yml +/crates/tower-api/git_push.sh +/crates/tower-api/.gitignore +/scripts/openapi.json +/scripts/openapi-generator-cli-*.jar diff --git a/Cargo.lock b/Cargo.lock index 78b1714c..39684c9e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -310,7 +310,7 @@ dependencies = [ [[package]] name = "config" -version = "0.3.10" +version = "0.3.11" dependencies = [ "chrono", "clap", @@ -382,7 +382,7 @@ dependencies = [ [[package]] name = "crypto" -version = "0.3.10" +version = "0.3.11" dependencies = [ "base64", "pem", @@ -2425,7 +2425,7 @@ dependencies = [ [[package]] name = "testutils" -version = "0.3.10" +version = "0.3.11" dependencies = [ "pem", "rsa", @@ -2629,7 +2629,7 @@ dependencies = [ [[package]] name = "tower" -version = "0.3.10" +version = "0.3.11" dependencies = [ "tokio", "tower-api", @@ -2655,6 +2655,7 @@ dependencies = [ name = "tower-api" version = "1.0.0" dependencies = [ + "log", "reqwest", "serde", "serde_json", @@ -2665,7 +2666,7 @@ dependencies = [ [[package]] name = "tower-cmd" -version = "0.3.10" +version = "0.3.11" dependencies = [ "anyhow", "bytes", @@ -2704,7 +2705,7 @@ checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" [[package]] name = "tower-package" -version = "0.3.10" +version = "0.3.11" dependencies = [ "async-compression", "config", @@ -2718,11 +2719,12 @@ dependencies = [ "tokio", "tokio-stream", "tokio-tar", + "tokio-util", ] [[package]] name = "tower-runtime" -version = "0.3.10" +version = "0.3.11" dependencies = [ "chrono", "log", @@ -2739,7 +2741,7 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tower-version" -version = "0.3.10" +version = "0.3.11" dependencies = [ "anyhow", "chrono", diff --git a/Cargo.toml b/Cargo.toml index 7cf975dc..d65364ab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ resolver = "2" [workspace.package] edition = "2021" -version = "0.3.10" +version = "0.3.11" description = "Tower is the best way to host Python data apps in production" rust-version = "1.77" authors = ["Brad Heller "] diff --git a/crates/tower-api/Cargo.toml b/crates/tower-api/Cargo.toml index 67ebecab..eae002a1 100644 --- a/crates/tower-api/Cargo.toml +++ b/crates/tower-api/Cargo.toml @@ -8,9 +8,10 @@ license = "Unlicense" edition = "2021" [dependencies] +log = { workspace = true } serde = { version = "^1.0", features = ["derive"] } serde_with = { version = "^3.8", default-features = false, features = ["base64", "std", "macros"] } serde_json = "^1.0" serde_repr = "^0.1" url = "^2.5" -reqwest = { version = "^0.12", features = ["json", "multipart"] } +reqwest = { version = "^0.12", default-features = false, features = ["json", "multipart"] } diff --git a/crates/tower-api/README.md b/crates/tower-api/README.md index 31c1bb1f..0f6326b2 100644 --- a/crates/tower-api/README.md +++ b/crates/tower-api/README.md @@ -8,9 +8,9 @@ For more information, please visit [https://tower.dev](https://tower.dev) This API client was generated by the [OpenAPI Generator](https://openapi-generator.tech) project. By using the [openapi-spec](https://openapis.org) from a remote server, you can easily generate an API client. -- API version: v0.5.1 +- API version: v0.5.9 - Package version: 1.0.0 -- Generator version: 7.13.0-SNAPSHOT +- Generator version: 7.13.0 - Build package: `org.openapitools.codegen.languages.RustClientCodegen` ## Installation @@ -28,6 +28,7 @@ All URIs are relative to *https://api.tower.dev/v1* Class | Method | HTTP request | Description ------------ | ------------- | ------------- | ------------- *DefaultApi* | [**accept_invitation**](docs/DefaultApi.md#accept_invitation) | **POST** /accounts/invite | Accept an invitation code +*DefaultApi* | [**acknowledge_alert**](docs/DefaultApi.md#acknowledge_alert) | **POST** /alerts/{alert_id}/acknowledge | Acknowledge alert *DefaultApi* | [**cancel_run**](docs/DefaultApi.md#cancel_run) | **POST** /apps/{slug}/runs/{seq} | Cancel run *DefaultApi* | [**claim_device_login_ticket**](docs/DefaultApi.md#claim_device_login_ticket) | **POST** /login/device/claim | Claim a device login ticket *DefaultApi* | [**create_account**](docs/DefaultApi.md#create_account) | **POST** /accounts | Create account @@ -38,6 +39,7 @@ Class | Method | HTTP request | Description *DefaultApi* | [**create_secret**](docs/DefaultApi.md#create_secret) | **POST** /secrets | Create secret *DefaultApi* | [**create_session**](docs/DefaultApi.md#create_session) | **POST** /session | Create session *DefaultApi* | [**create_team**](docs/DefaultApi.md#create_team) | **POST** /teams | Create team +*DefaultApi* | [**delete_alert**](docs/DefaultApi.md#delete_alert) | **DELETE** /alerts/{alert_id} | Delete alert *DefaultApi* | [**delete_api_key**](docs/DefaultApi.md#delete_api_key) | **DELETE** /api-keys | Delete API key *DefaultApi* | [**delete_app**](docs/DefaultApi.md#delete_app) | **DELETE** /apps/{slug} | Delete app *DefaultApi* | [**delete_catalog**](docs/DefaultApi.md#delete_catalog) | **DELETE** /catalogs/{slug} | Delete catalog @@ -57,6 +59,7 @@ Class | Method | HTTP request | Description *DefaultApi* | [**generate_run_statistics**](docs/DefaultApi.md#generate_run_statistics) | **GET** /stats/runs | Generate run statistics *DefaultApi* | [**invite_team_member**](docs/DefaultApi.md#invite_team_member) | **POST** /teams/{slug}/invites | Invite team member *DefaultApi* | [**leave_team**](docs/DefaultApi.md#leave_team) | **POST** /teams/{slug}/leave | Leave team +*DefaultApi* | [**list_alerts**](docs/DefaultApi.md#list_alerts) | **GET** /alerts | List alerts *DefaultApi* | [**list_api_keys**](docs/DefaultApi.md#list_api_keys) | **GET** /api-keys | List API keys *DefaultApi* | [**list_app_environments**](docs/DefaultApi.md#list_app_environments) | **GET** /apps/{slug}/environments | List app environments *DefaultApi* | [**list_app_versions**](docs/DefaultApi.md#list_app_versions) | **GET** /apps/{name}/versions | List app versions @@ -88,6 +91,9 @@ Class | Method | HTTP request | Description - [AcceptInvitationParams](docs/AcceptInvitationParams.md) - [AcceptInvitationResponse](docs/AcceptInvitationResponse.md) - [Account](docs/Account.md) + - [AcknowledgeAlertResponse](docs/AcknowledgeAlertResponse.md) + - [Alert](docs/Alert.md) + - [AlertDetail](docs/AlertDetail.md) - [ApiKey](docs/ApiKey.md) - [App](docs/App.md) - [AppStatistics](docs/AppStatistics.md) @@ -141,6 +147,7 @@ Class | Method | HTTP request | Description - [InviteTeamMemberParams](docs/InviteTeamMemberParams.md) - [InviteTeamMemberResponse](docs/InviteTeamMemberResponse.md) - [LeaveTeamResponse](docs/LeaveTeamResponse.md) + - [ListAlertsResponse](docs/ListAlertsResponse.md) - [ListApiKeysResponse](docs/ListApiKeysResponse.md) - [ListAppEnvironmentsResponse](docs/ListAppEnvironmentsResponse.md) - [ListAppVersionsResponse](docs/ListAppVersionsResponse.md) @@ -167,6 +174,7 @@ Class | Method | HTTP request | Description - [RunAppParams](docs/RunAppParams.md) - [RunAppResponse](docs/RunAppResponse.md) - [RunLogLine](docs/RunLogLine.md) + - [RunParameter](docs/RunParameter.md) - [RunResults](docs/RunResults.md) - [RunStatistics](docs/RunStatistics.md) - [Secret](docs/Secret.md) diff --git a/crates/tower-api/src/apis/configuration.rs b/crates/tower-api/src/apis/configuration.rs index 4a9ee3b7..60dcd450 100644 --- a/crates/tower-api/src/apis/configuration.rs +++ b/crates/tower-api/src/apis/configuration.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ @@ -40,7 +40,7 @@ impl Default for Configuration { fn default() -> Self { Configuration { base_path: "https://api.tower.dev/v1".to_owned(), - user_agent: Some("tower_cli_client".to_owned()), + user_agent: Some("Tower CLI".to_owned()), client: reqwest::Client::new(), basic_auth: None, oauth_access_token: None, diff --git a/crates/tower-api/src/apis/default_api.rs b/crates/tower-api/src/apis/default_api.rs index 826778a8..b3c86e06 100644 --- a/crates/tower-api/src/apis/default_api.rs +++ b/crates/tower-api/src/apis/default_api.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ @@ -14,12 +14,21 @@ use serde::{Deserialize, Serialize, de::Error as _}; use crate::{apis::ResponseContent, models}; use super::{Error, configuration, ContentType}; +const DEFAULT_TOWER_TRACE_ID: &str = "(none)"; + /// struct for passing parameters to the method [`accept_invitation`] #[derive(Clone, Debug)] pub struct AcceptInvitationParams { pub accept_invitation_params: models::AcceptInvitationParams } +/// struct for passing parameters to the method [`acknowledge_alert`] +#[derive(Clone, Debug)] +pub struct AcknowledgeAlertParams { + /// ID of the alert to acknowledge + pub alert_id: String +} + /// struct for passing parameters to the method [`cancel_run`] #[derive(Clone, Debug)] pub struct CancelRunParams { @@ -77,6 +86,13 @@ pub struct CreateTeamParams { pub create_team_params: models::CreateTeamParams } +/// struct for passing parameters to the method [`delete_alert`] +#[derive(Clone, Debug)] +pub struct DeleteAlertParams { + /// ID of the alert to delete + pub alert_id: String +} + /// struct for passing parameters to the method [`delete_api_key`] #[derive(Clone, Debug)] pub struct DeleteApiKeyParams { @@ -215,6 +231,23 @@ pub struct LeaveTeamParams { pub slug: String } +/// struct for passing parameters to the method [`list_alerts`] +#[derive(Clone, Debug)] +pub struct ListAlertsParams { + /// Filter alerts by alert type + pub alert_type: Option, + /// Filter by acknowledgement status (true=acknowledged, false=unacknowledged) + pub acked: Option, + /// Filter alerts created after or at this datetime (inclusive) + pub start_at: Option, + /// Filter alerts created before or at this datetime (inclusive) + pub end_at: Option, + /// The page number to fetch. + pub page: Option, + /// The number of records to fetch on each page. + pub page_size: Option +} + /// struct for passing parameters to the method [`list_app_environments`] #[derive(Clone, Debug)] pub struct ListAppEnvironmentsParams { @@ -402,6 +435,14 @@ pub enum AcceptInvitationSuccess { UnknownValue(serde_json::Value), } +/// struct for typed successes of method [`acknowledge_alert`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum AcknowledgeAlertSuccess { + Status200(models::AcknowledgeAlertResponse), + UnknownValue(serde_json::Value), +} + /// struct for typed successes of method [`cancel_run`] #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(untagged)] @@ -482,6 +523,14 @@ pub enum CreateTeamSuccess { UnknownValue(serde_json::Value), } +/// struct for typed successes of method [`delete_alert`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum DeleteAlertSuccess { + Status204(), + UnknownValue(serde_json::Value), +} + /// struct for typed successes of method [`delete_api_key`] #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(untagged)] @@ -634,6 +683,14 @@ pub enum LeaveTeamSuccess { UnknownValue(serde_json::Value), } +/// struct for typed successes of method [`list_alerts`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum ListAlertsSuccess { + Status200(models::ListAlertsResponse), + UnknownValue(serde_json::Value), +} + /// struct for typed successes of method [`list_api_keys`] #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(untagged)] @@ -835,6 +892,14 @@ pub enum AcceptInvitationError { UnknownValue(serde_json::Value), } +/// struct for typed errors of method [`acknowledge_alert`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum AcknowledgeAlertError { + DefaultResponse(models::ErrorModel), + UnknownValue(serde_json::Value), +} + /// struct for typed errors of method [`cancel_run`] #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(untagged)] @@ -915,6 +980,14 @@ pub enum CreateTeamError { UnknownValue(serde_json::Value), } +/// struct for typed errors of method [`delete_alert`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum DeleteAlertError { + DefaultResponse(models::ErrorModel), + UnknownValue(serde_json::Value), +} + /// struct for typed errors of method [`delete_api_key`] #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(untagged)] @@ -1070,6 +1143,14 @@ pub enum LeaveTeamError { UnknownValue(serde_json::Value), } +/// struct for typed errors of method [`list_alerts`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum ListAlertsError { + DefaultResponse(models::ErrorModel), + UnknownValue(serde_json::Value), +} + /// struct for typed errors of method [`list_api_keys`] #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(untagged)] @@ -1282,14 +1363,55 @@ pub async fn accept_invitation(configuration: &configuration::Configuration, par let status = resp.status(); + let tower_trace_id = resp + .headers() + .get("x-tower-trace-id") + .and_then(|v| v.to_str().ok()) + .map_or(String::from(DEFAULT_TOWER_TRACE_ID), String::from); + if !status.is_client_error() && !status.is_server_error() { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Ok(ResponseContent { status, content, entity }) + Ok(ResponseContent { tower_trace_id, status, content, entity }) } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { tower_trace_id, status, content, entity })) + } +} + +/// Mark an alert as acknowledged +pub async fn acknowledge_alert(configuration: &configuration::Configuration, params: AcknowledgeAlertParams) -> Result, Error> { + + let uri_str = format!("{}/alerts/{alert_id}/acknowledge", configuration.base_path, alert_id=crate::apis::urlencode(params.alert_id)); + let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str); + + if let Some(ref user_agent) = configuration.user_agent { + req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone()); + } + if let Some(ref token) = configuration.bearer_access_token { + req_builder = req_builder.bearer_auth(token.to_owned()); + }; + + let req = req_builder.build()?; + let resp = configuration.client.execute(req).await?; + + let status = resp.status(); + + let tower_trace_id = resp + .headers() + .get("x-tower-trace-id") + .and_then(|v| v.to_str().ok()) + .map_or(String::from(DEFAULT_TOWER_TRACE_ID), String::from); + + if !status.is_client_error() && !status.is_server_error() { + let content = resp.text().await?; + let entity: Option = serde_json::from_str(&content).ok(); + Ok(ResponseContent { tower_trace_id, status, content, entity }) + } else { + let content = resp.text().await?; + let entity: Option = serde_json::from_str(&content).ok(); + Err(Error::ResponseError(ResponseContent { tower_trace_id, status, content, entity })) } } @@ -1311,14 +1433,20 @@ pub async fn cancel_run(configuration: &configuration::Configuration, params: Ca let status = resp.status(); + let tower_trace_id = resp + .headers() + .get("x-tower-trace-id") + .and_then(|v| v.to_str().ok()) + .map_or(String::from(DEFAULT_TOWER_TRACE_ID), String::from); + if !status.is_client_error() && !status.is_server_error() { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Ok(ResponseContent { status, content, entity }) + Ok(ResponseContent { tower_trace_id, status, content, entity }) } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { tower_trace_id, status, content, entity })) } } @@ -1341,14 +1469,20 @@ pub async fn claim_device_login_ticket(configuration: &configuration::Configurat let status = resp.status(); + let tower_trace_id = resp + .headers() + .get("x-tower-trace-id") + .and_then(|v| v.to_str().ok()) + .map_or(String::from(DEFAULT_TOWER_TRACE_ID), String::from); + if !status.is_client_error() && !status.is_server_error() { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Ok(ResponseContent { status, content, entity }) + Ok(ResponseContent { tower_trace_id, status, content, entity }) } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { tower_trace_id, status, content, entity })) } } @@ -1368,14 +1502,20 @@ pub async fn create_account(configuration: &configuration::Configuration, params let status = resp.status(); + let tower_trace_id = resp + .headers() + .get("x-tower-trace-id") + .and_then(|v| v.to_str().ok()) + .map_or(String::from(DEFAULT_TOWER_TRACE_ID), String::from); + if !status.is_client_error() && !status.is_server_error() { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Ok(ResponseContent { status, content, entity }) + Ok(ResponseContent { tower_trace_id, status, content, entity }) } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { tower_trace_id, status, content, entity })) } } @@ -1397,14 +1537,20 @@ pub async fn create_api_key(configuration: &configuration::Configuration, params let status = resp.status(); + let tower_trace_id = resp + .headers() + .get("x-tower-trace-id") + .and_then(|v| v.to_str().ok()) + .map_or(String::from(DEFAULT_TOWER_TRACE_ID), String::from); + if !status.is_client_error() && !status.is_server_error() { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Ok(ResponseContent { status, content, entity }) + Ok(ResponseContent { tower_trace_id, status, content, entity }) } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { tower_trace_id, status, content, entity })) } } @@ -1427,14 +1573,20 @@ pub async fn create_app(configuration: &configuration::Configuration, params: Cr let status = resp.status(); + let tower_trace_id = resp + .headers() + .get("x-tower-trace-id") + .and_then(|v| v.to_str().ok()) + .map_or(String::from(DEFAULT_TOWER_TRACE_ID), String::from); + if !status.is_client_error() && !status.is_server_error() { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Ok(ResponseContent { status, content, entity }) + Ok(ResponseContent { tower_trace_id, status, content, entity }) } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { tower_trace_id, status, content, entity })) } } @@ -1457,14 +1609,20 @@ pub async fn create_catalog(configuration: &configuration::Configuration, params let status = resp.status(); + let tower_trace_id = resp + .headers() + .get("x-tower-trace-id") + .and_then(|v| v.to_str().ok()) + .map_or(String::from(DEFAULT_TOWER_TRACE_ID), String::from); + if !status.is_client_error() && !status.is_server_error() { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Ok(ResponseContent { status, content, entity }) + Ok(ResponseContent { tower_trace_id, status, content, entity }) } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { tower_trace_id, status, content, entity })) } } @@ -1483,14 +1641,20 @@ pub async fn create_device_login_ticket(configuration: &configuration::Configura let status = resp.status(); + let tower_trace_id = resp + .headers() + .get("x-tower-trace-id") + .and_then(|v| v.to_str().ok()) + .map_or(String::from(DEFAULT_TOWER_TRACE_ID), String::from); + if !status.is_client_error() && !status.is_server_error() { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Ok(ResponseContent { status, content, entity }) + Ok(ResponseContent { tower_trace_id, status, content, entity }) } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { tower_trace_id, status, content, entity })) } } @@ -1513,14 +1677,20 @@ pub async fn create_secret(configuration: &configuration::Configuration, params: let status = resp.status(); + let tower_trace_id = resp + .headers() + .get("x-tower-trace-id") + .and_then(|v| v.to_str().ok()) + .map_or(String::from(DEFAULT_TOWER_TRACE_ID), String::from); + if !status.is_client_error() && !status.is_server_error() { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Ok(ResponseContent { status, content, entity }) + Ok(ResponseContent { tower_trace_id, status, content, entity }) } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { tower_trace_id, status, content, entity })) } } @@ -1540,14 +1710,20 @@ pub async fn create_session(configuration: &configuration::Configuration, params let status = resp.status(); + let tower_trace_id = resp + .headers() + .get("x-tower-trace-id") + .and_then(|v| v.to_str().ok()) + .map_or(String::from(DEFAULT_TOWER_TRACE_ID), String::from); + if !status.is_client_error() && !status.is_server_error() { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Ok(ResponseContent { status, content, entity }) + Ok(ResponseContent { tower_trace_id, status, content, entity }) } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { tower_trace_id, status, content, entity })) } } @@ -1570,14 +1746,55 @@ pub async fn create_team(configuration: &configuration::Configuration, params: C let status = resp.status(); + let tower_trace_id = resp + .headers() + .get("x-tower-trace-id") + .and_then(|v| v.to_str().ok()) + .map_or(String::from(DEFAULT_TOWER_TRACE_ID), String::from); + if !status.is_client_error() && !status.is_server_error() { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Ok(ResponseContent { status, content, entity }) + Ok(ResponseContent { tower_trace_id, status, content, entity }) } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { tower_trace_id, status, content, entity })) + } +} + +/// Permanently delete an alert +pub async fn delete_alert(configuration: &configuration::Configuration, params: DeleteAlertParams) -> Result, Error> { + + let uri_str = format!("{}/alerts/{alert_id}", configuration.base_path, alert_id=crate::apis::urlencode(params.alert_id)); + let mut req_builder = configuration.client.request(reqwest::Method::DELETE, &uri_str); + + if let Some(ref user_agent) = configuration.user_agent { + req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone()); + } + if let Some(ref token) = configuration.bearer_access_token { + req_builder = req_builder.bearer_auth(token.to_owned()); + }; + + let req = req_builder.build()?; + let resp = configuration.client.execute(req).await?; + + let status = resp.status(); + + let tower_trace_id = resp + .headers() + .get("x-tower-trace-id") + .and_then(|v| v.to_str().ok()) + .map_or(String::from(DEFAULT_TOWER_TRACE_ID), String::from); + + if !status.is_client_error() && !status.is_server_error() { + let content = resp.text().await?; + let entity: Option = serde_json::from_str(&content).ok(); + Ok(ResponseContent { tower_trace_id, status, content, entity }) + } else { + let content = resp.text().await?; + let entity: Option = serde_json::from_str(&content).ok(); + Err(Error::ResponseError(ResponseContent { tower_trace_id, status, content, entity })) } } @@ -1599,14 +1816,20 @@ pub async fn delete_api_key(configuration: &configuration::Configuration, params let status = resp.status(); + let tower_trace_id = resp + .headers() + .get("x-tower-trace-id") + .and_then(|v| v.to_str().ok()) + .map_or(String::from(DEFAULT_TOWER_TRACE_ID), String::from); + if !status.is_client_error() && !status.is_server_error() { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Ok(ResponseContent { status, content, entity }) + Ok(ResponseContent { tower_trace_id, status, content, entity }) } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { tower_trace_id, status, content, entity })) } } @@ -1628,14 +1851,20 @@ pub async fn delete_app(configuration: &configuration::Configuration, params: De let status = resp.status(); + let tower_trace_id = resp + .headers() + .get("x-tower-trace-id") + .and_then(|v| v.to_str().ok()) + .map_or(String::from(DEFAULT_TOWER_TRACE_ID), String::from); + if !status.is_client_error() && !status.is_server_error() { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Ok(ResponseContent { status, content, entity }) + Ok(ResponseContent { tower_trace_id, status, content, entity }) } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { tower_trace_id, status, content, entity })) } } @@ -1657,14 +1886,20 @@ pub async fn delete_catalog(configuration: &configuration::Configuration, params let status = resp.status(); + let tower_trace_id = resp + .headers() + .get("x-tower-trace-id") + .and_then(|v| v.to_str().ok()) + .map_or(String::from(DEFAULT_TOWER_TRACE_ID), String::from); + if !status.is_client_error() && !status.is_server_error() { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Ok(ResponseContent { status, content, entity }) + Ok(ResponseContent { tower_trace_id, status, content, entity }) } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { tower_trace_id, status, content, entity })) } } @@ -1689,14 +1924,20 @@ pub async fn delete_secret(configuration: &configuration::Configuration, params: let status = resp.status(); + let tower_trace_id = resp + .headers() + .get("x-tower-trace-id") + .and_then(|v| v.to_str().ok()) + .map_or(String::from(DEFAULT_TOWER_TRACE_ID), String::from); + if !status.is_client_error() && !status.is_server_error() { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Ok(ResponseContent { status, content, entity }) + Ok(ResponseContent { tower_trace_id, status, content, entity }) } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { tower_trace_id, status, content, entity })) } } @@ -1719,14 +1960,20 @@ pub async fn delete_team(configuration: &configuration::Configuration, params: D let status = resp.status(); + let tower_trace_id = resp + .headers() + .get("x-tower-trace-id") + .and_then(|v| v.to_str().ok()) + .map_or(String::from(DEFAULT_TOWER_TRACE_ID), String::from); + if !status.is_client_error() && !status.is_server_error() { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Ok(ResponseContent { status, content, entity }) + Ok(ResponseContent { tower_trace_id, status, content, entity }) } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { tower_trace_id, status, content, entity })) } } @@ -1749,14 +1996,20 @@ pub async fn delete_team_invitation(configuration: &configuration::Configuration let status = resp.status(); + let tower_trace_id = resp + .headers() + .get("x-tower-trace-id") + .and_then(|v| v.to_str().ok()) + .map_or(String::from(DEFAULT_TOWER_TRACE_ID), String::from); + if !status.is_client_error() && !status.is_server_error() { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Ok(ResponseContent { status, content, entity }) + Ok(ResponseContent { tower_trace_id, status, content, entity }) } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { tower_trace_id, status, content, entity })) } } @@ -1781,14 +2034,20 @@ pub async fn deploy_app(configuration: &configuration::Configuration, params: De let status = resp.status(); + let tower_trace_id = resp + .headers() + .get("x-tower-trace-id") + .and_then(|v| v.to_str().ok()) + .map_or(String::from(DEFAULT_TOWER_TRACE_ID), String::from); + if !status.is_client_error() && !status.is_server_error() { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Ok(ResponseContent { status, content, entity }) + Ok(ResponseContent { tower_trace_id, status, content, entity }) } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { tower_trace_id, status, content, entity })) } } @@ -1813,14 +2072,20 @@ pub async fn describe_app(configuration: &configuration::Configuration, params: let status = resp.status(); + let tower_trace_id = resp + .headers() + .get("x-tower-trace-id") + .and_then(|v| v.to_str().ok()) + .map_or(String::from(DEFAULT_TOWER_TRACE_ID), String::from); + if !status.is_client_error() && !status.is_server_error() { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Ok(ResponseContent { status, content, entity }) + Ok(ResponseContent { tower_trace_id, status, content, entity }) } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { tower_trace_id, status, content, entity })) } } @@ -1842,14 +2107,20 @@ pub async fn describe_app_version(configuration: &configuration::Configuration, let status = resp.status(); + let tower_trace_id = resp + .headers() + .get("x-tower-trace-id") + .and_then(|v| v.to_str().ok()) + .map_or(String::from(DEFAULT_TOWER_TRACE_ID), String::from); + if !status.is_client_error() && !status.is_server_error() { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Ok(ResponseContent { status, content, entity }) + Ok(ResponseContent { tower_trace_id, status, content, entity }) } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { tower_trace_id, status, content, entity })) } } @@ -1868,14 +2139,20 @@ pub async fn describe_device_login_session(configuration: &configuration::Config let status = resp.status(); + let tower_trace_id = resp + .headers() + .get("x-tower-trace-id") + .and_then(|v| v.to_str().ok()) + .map_or(String::from(DEFAULT_TOWER_TRACE_ID), String::from); + if !status.is_client_error() && !status.is_server_error() { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Ok(ResponseContent { status, content, entity }) + Ok(ResponseContent { tower_trace_id, status, content, entity }) } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { tower_trace_id, status, content, entity })) } } @@ -1905,14 +2182,20 @@ pub async fn describe_run(configuration: &configuration::Configuration, params: let status = resp.status(); + let tower_trace_id = resp + .headers() + .get("x-tower-trace-id") + .and_then(|v| v.to_str().ok()) + .map_or(String::from(DEFAULT_TOWER_TRACE_ID), String::from); + if !status.is_client_error() && !status.is_server_error() { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Ok(ResponseContent { status, content, entity }) + Ok(ResponseContent { tower_trace_id, status, content, entity }) } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { tower_trace_id, status, content, entity })) } } @@ -1934,14 +2217,20 @@ pub async fn describe_run_logs(configuration: &configuration::Configuration, par let status = resp.status(); + let tower_trace_id = resp + .headers() + .get("x-tower-trace-id") + .and_then(|v| v.to_str().ok()) + .map_or(String::from(DEFAULT_TOWER_TRACE_ID), String::from); + if !status.is_client_error() && !status.is_server_error() { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Ok(ResponseContent { status, content, entity }) + Ok(ResponseContent { tower_trace_id, status, content, entity }) } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { tower_trace_id, status, content, entity })) } } @@ -1966,14 +2255,20 @@ pub async fn describe_secrets_key(configuration: &configuration::Configuration, let status = resp.status(); + let tower_trace_id = resp + .headers() + .get("x-tower-trace-id") + .and_then(|v| v.to_str().ok()) + .map_or(String::from(DEFAULT_TOWER_TRACE_ID), String::from); + if !status.is_client_error() && !status.is_server_error() { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Ok(ResponseContent { status, content, entity }) + Ok(ResponseContent { tower_trace_id, status, content, entity }) } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { tower_trace_id, status, content, entity })) } } @@ -1995,14 +2290,20 @@ pub async fn describe_session(configuration: &configuration::Configuration) -> R let status = resp.status(); + let tower_trace_id = resp + .headers() + .get("x-tower-trace-id") + .and_then(|v| v.to_str().ok()) + .map_or(String::from(DEFAULT_TOWER_TRACE_ID), String::from); + if !status.is_client_error() && !status.is_server_error() { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Ok(ResponseContent { status, content, entity }) + Ok(ResponseContent { tower_trace_id, status, content, entity }) } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { tower_trace_id, status, content, entity })) } } @@ -2037,14 +2338,20 @@ pub async fn export_secrets(configuration: &configuration::Configuration, params let status = resp.status(); + let tower_trace_id = resp + .headers() + .get("x-tower-trace-id") + .and_then(|v| v.to_str().ok()) + .map_or(String::from(DEFAULT_TOWER_TRACE_ID), String::from); + if !status.is_client_error() && !status.is_server_error() { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Ok(ResponseContent { status, content, entity }) + Ok(ResponseContent { tower_trace_id, status, content, entity }) } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { tower_trace_id, status, content, entity })) } } @@ -2066,14 +2373,20 @@ pub async fn generate_app_statistics(configuration: &configuration::Configuratio let status = resp.status(); + let tower_trace_id = resp + .headers() + .get("x-tower-trace-id") + .and_then(|v| v.to_str().ok()) + .map_or(String::from(DEFAULT_TOWER_TRACE_ID), String::from); + if !status.is_client_error() && !status.is_server_error() { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Ok(ResponseContent { status, content, entity }) + Ok(ResponseContent { tower_trace_id, status, content, entity }) } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { tower_trace_id, status, content, entity })) } } @@ -2098,14 +2411,20 @@ pub async fn generate_run_statistics(configuration: &configuration::Configuratio let status = resp.status(); + let tower_trace_id = resp + .headers() + .get("x-tower-trace-id") + .and_then(|v| v.to_str().ok()) + .map_or(String::from(DEFAULT_TOWER_TRACE_ID), String::from); + if !status.is_client_error() && !status.is_server_error() { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Ok(ResponseContent { status, content, entity }) + Ok(ResponseContent { tower_trace_id, status, content, entity }) } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { tower_trace_id, status, content, entity })) } } @@ -2128,14 +2447,20 @@ pub async fn invite_team_member(configuration: &configuration::Configuration, pa let status = resp.status(); + let tower_trace_id = resp + .headers() + .get("x-tower-trace-id") + .and_then(|v| v.to_str().ok()) + .map_or(String::from(DEFAULT_TOWER_TRACE_ID), String::from); + if !status.is_client_error() && !status.is_server_error() { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Ok(ResponseContent { status, content, entity }) + Ok(ResponseContent { tower_trace_id, status, content, entity }) } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { tower_trace_id, status, content, entity })) } } @@ -2157,14 +2482,73 @@ pub async fn leave_team(configuration: &configuration::Configuration, params: Le let status = resp.status(); + let tower_trace_id = resp + .headers() + .get("x-tower-trace-id") + .and_then(|v| v.to_str().ok()) + .map_or(String::from(DEFAULT_TOWER_TRACE_ID), String::from); + if !status.is_client_error() && !status.is_server_error() { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Ok(ResponseContent { status, content, entity }) + Ok(ResponseContent { tower_trace_id, status, content, entity }) } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { tower_trace_id, status, content, entity })) + } +} + +/// List alerts for the current account with optional filtering +pub async fn list_alerts(configuration: &configuration::Configuration, params: ListAlertsParams) -> Result, Error> { + + let uri_str = format!("{}/alerts", configuration.base_path); + let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str); + + if let Some(ref param_value) = params.alert_type { + req_builder = req_builder.query(&[("alert_type", ¶m_value.to_string())]); + } + if let Some(ref param_value) = params.acked { + req_builder = req_builder.query(&[("acked", ¶m_value.to_string())]); + } + if let Some(ref param_value) = params.start_at { + req_builder = req_builder.query(&[("start_at", ¶m_value.to_string())]); + } + if let Some(ref param_value) = params.end_at { + req_builder = req_builder.query(&[("end_at", ¶m_value.to_string())]); + } + if let Some(ref param_value) = params.page { + req_builder = req_builder.query(&[("page", ¶m_value.to_string())]); + } + if let Some(ref param_value) = params.page_size { + req_builder = req_builder.query(&[("page_size", ¶m_value.to_string())]); + } + if let Some(ref user_agent) = configuration.user_agent { + req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone()); + } + if let Some(ref token) = configuration.bearer_access_token { + req_builder = req_builder.bearer_auth(token.to_owned()); + }; + + let req = req_builder.build()?; + let resp = configuration.client.execute(req).await?; + + let status = resp.status(); + + let tower_trace_id = resp + .headers() + .get("x-tower-trace-id") + .and_then(|v| v.to_str().ok()) + .map_or(String::from(DEFAULT_TOWER_TRACE_ID), String::from); + + if !status.is_client_error() && !status.is_server_error() { + let content = resp.text().await?; + let entity: Option = serde_json::from_str(&content).ok(); + Ok(ResponseContent { tower_trace_id, status, content, entity }) + } else { + let content = resp.text().await?; + let entity: Option = serde_json::from_str(&content).ok(); + Err(Error::ResponseError(ResponseContent { tower_trace_id, status, content, entity })) } } @@ -2186,14 +2570,20 @@ pub async fn list_api_keys(configuration: &configuration::Configuration) -> Resu let status = resp.status(); + let tower_trace_id = resp + .headers() + .get("x-tower-trace-id") + .and_then(|v| v.to_str().ok()) + .map_or(String::from(DEFAULT_TOWER_TRACE_ID), String::from); + if !status.is_client_error() && !status.is_server_error() { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Ok(ResponseContent { status, content, entity }) + Ok(ResponseContent { tower_trace_id, status, content, entity }) } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { tower_trace_id, status, content, entity })) } } @@ -2215,14 +2605,20 @@ pub async fn list_app_environments(configuration: &configuration::Configuration, let status = resp.status(); + let tower_trace_id = resp + .headers() + .get("x-tower-trace-id") + .and_then(|v| v.to_str().ok()) + .map_or(String::from(DEFAULT_TOWER_TRACE_ID), String::from); + if !status.is_client_error() && !status.is_server_error() { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Ok(ResponseContent { status, content, entity }) + Ok(ResponseContent { tower_trace_id, status, content, entity }) } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { tower_trace_id, status, content, entity })) } } @@ -2244,14 +2640,20 @@ pub async fn list_app_versions(configuration: &configuration::Configuration, par let status = resp.status(); + let tower_trace_id = resp + .headers() + .get("x-tower-trace-id") + .and_then(|v| v.to_str().ok()) + .map_or(String::from(DEFAULT_TOWER_TRACE_ID), String::from); + if !status.is_client_error() && !status.is_server_error() { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Ok(ResponseContent { status, content, entity }) + Ok(ResponseContent { tower_trace_id, status, content, entity }) } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { tower_trace_id, status, content, entity })) } } @@ -2302,14 +2704,20 @@ pub async fn list_apps(configuration: &configuration::Configuration, params: Lis let status = resp.status(); + let tower_trace_id = resp + .headers() + .get("x-tower-trace-id") + .and_then(|v| v.to_str().ok()) + .map_or(String::from(DEFAULT_TOWER_TRACE_ID), String::from); + if !status.is_client_error() && !status.is_server_error() { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Ok(ResponseContent { status, content, entity }) + Ok(ResponseContent { tower_trace_id, status, content, entity }) } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { tower_trace_id, status, content, entity })) } } @@ -2343,14 +2751,20 @@ pub async fn list_catalogs(configuration: &configuration::Configuration, params: let status = resp.status(); + let tower_trace_id = resp + .headers() + .get("x-tower-trace-id") + .and_then(|v| v.to_str().ok()) + .map_or(String::from(DEFAULT_TOWER_TRACE_ID), String::from); + if !status.is_client_error() && !status.is_server_error() { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Ok(ResponseContent { status, content, entity }) + Ok(ResponseContent { tower_trace_id, status, content, entity }) } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { tower_trace_id, status, content, entity })) } } @@ -2372,14 +2786,20 @@ pub async fn list_my_team_invitations(configuration: &configuration::Configurati let status = resp.status(); + let tower_trace_id = resp + .headers() + .get("x-tower-trace-id") + .and_then(|v| v.to_str().ok()) + .map_or(String::from(DEFAULT_TOWER_TRACE_ID), String::from); + if !status.is_client_error() && !status.is_server_error() { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Ok(ResponseContent { status, content, entity }) + Ok(ResponseContent { tower_trace_id, status, content, entity }) } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { tower_trace_id, status, content, entity })) } } @@ -2419,14 +2839,20 @@ pub async fn list_runs(configuration: &configuration::Configuration, params: Lis let status = resp.status(); + let tower_trace_id = resp + .headers() + .get("x-tower-trace-id") + .and_then(|v| v.to_str().ok()) + .map_or(String::from(DEFAULT_TOWER_TRACE_ID), String::from); + if !status.is_client_error() && !status.is_server_error() { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Ok(ResponseContent { status, content, entity }) + Ok(ResponseContent { tower_trace_id, status, content, entity }) } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { tower_trace_id, status, content, entity })) } } @@ -2448,14 +2874,20 @@ pub async fn list_secret_environments(configuration: &configuration::Configurati let status = resp.status(); + let tower_trace_id = resp + .headers() + .get("x-tower-trace-id") + .and_then(|v| v.to_str().ok()) + .map_or(String::from(DEFAULT_TOWER_TRACE_ID), String::from); + if !status.is_client_error() && !status.is_server_error() { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Ok(ResponseContent { status, content, entity }) + Ok(ResponseContent { tower_trace_id, status, content, entity }) } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { tower_trace_id, status, content, entity })) } } @@ -2489,14 +2921,20 @@ pub async fn list_secrets(configuration: &configuration::Configuration, params: let status = resp.status(); + let tower_trace_id = resp + .headers() + .get("x-tower-trace-id") + .and_then(|v| v.to_str().ok()) + .map_or(String::from(DEFAULT_TOWER_TRACE_ID), String::from); + if !status.is_client_error() && !status.is_server_error() { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Ok(ResponseContent { status, content, entity }) + Ok(ResponseContent { tower_trace_id, status, content, entity }) } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { tower_trace_id, status, content, entity })) } } @@ -2518,14 +2956,20 @@ pub async fn list_team_invitations(configuration: &configuration::Configuration, let status = resp.status(); + let tower_trace_id = resp + .headers() + .get("x-tower-trace-id") + .and_then(|v| v.to_str().ok()) + .map_or(String::from(DEFAULT_TOWER_TRACE_ID), String::from); + if !status.is_client_error() && !status.is_server_error() { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Ok(ResponseContent { status, content, entity }) + Ok(ResponseContent { tower_trace_id, status, content, entity }) } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { tower_trace_id, status, content, entity })) } } @@ -2547,14 +2991,20 @@ pub async fn list_team_members(configuration: &configuration::Configuration, par let status = resp.status(); + let tower_trace_id = resp + .headers() + .get("x-tower-trace-id") + .and_then(|v| v.to_str().ok()) + .map_or(String::from(DEFAULT_TOWER_TRACE_ID), String::from); + if !status.is_client_error() && !status.is_server_error() { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Ok(ResponseContent { status, content, entity }) + Ok(ResponseContent { tower_trace_id, status, content, entity }) } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { tower_trace_id, status, content, entity })) } } @@ -2576,14 +3026,20 @@ pub async fn list_teams(configuration: &configuration::Configuration) -> Result< let status = resp.status(); + let tower_trace_id = resp + .headers() + .get("x-tower-trace-id") + .and_then(|v| v.to_str().ok()) + .map_or(String::from(DEFAULT_TOWER_TRACE_ID), String::from); + if !status.is_client_error() && !status.is_server_error() { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Ok(ResponseContent { status, content, entity }) + Ok(ResponseContent { tower_trace_id, status, content, entity }) } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { tower_trace_id, status, content, entity })) } } @@ -2606,14 +3062,20 @@ pub async fn refresh_session(configuration: &configuration::Configuration, param let status = resp.status(); + let tower_trace_id = resp + .headers() + .get("x-tower-trace-id") + .and_then(|v| v.to_str().ok()) + .map_or(String::from(DEFAULT_TOWER_TRACE_ID), String::from); + if !status.is_client_error() && !status.is_server_error() { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Ok(ResponseContent { status, content, entity }) + Ok(ResponseContent { tower_trace_id, status, content, entity }) } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { tower_trace_id, status, content, entity })) } } @@ -2636,14 +3098,20 @@ pub async fn remove_team_member(configuration: &configuration::Configuration, pa let status = resp.status(); + let tower_trace_id = resp + .headers() + .get("x-tower-trace-id") + .and_then(|v| v.to_str().ok()) + .map_or(String::from(DEFAULT_TOWER_TRACE_ID), String::from); + if !status.is_client_error() && !status.is_server_error() { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Ok(ResponseContent { status, content, entity }) + Ok(ResponseContent { tower_trace_id, status, content, entity }) } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { tower_trace_id, status, content, entity })) } } @@ -2666,14 +3134,20 @@ pub async fn resend_team_invitation(configuration: &configuration::Configuration let status = resp.status(); + let tower_trace_id = resp + .headers() + .get("x-tower-trace-id") + .and_then(|v| v.to_str().ok()) + .map_or(String::from(DEFAULT_TOWER_TRACE_ID), String::from); + if !status.is_client_error() && !status.is_server_error() { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Ok(ResponseContent { status, content, entity }) + Ok(ResponseContent { tower_trace_id, status, content, entity }) } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { tower_trace_id, status, content, entity })) } } @@ -2704,14 +3178,20 @@ pub async fn run_app(configuration: &configuration::Configuration, params: RunAp let status = resp.status(); + let tower_trace_id = resp + .headers() + .get("x-tower-trace-id") + .and_then(|v| v.to_str().ok()) + .map_or(String::from(DEFAULT_TOWER_TRACE_ID), String::from); + if !status.is_client_error() && !status.is_server_error() { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Ok(ResponseContent { status, content, entity }) + Ok(ResponseContent { tower_trace_id, status, content, entity }) } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { tower_trace_id, status, content, entity })) } } @@ -2733,14 +3213,20 @@ pub async fn stream_run_logs(configuration: &configuration::Configuration, param let status = resp.status(); + let tower_trace_id = resp + .headers() + .get("x-tower-trace-id") + .and_then(|v| v.to_str().ok()) + .map_or(String::from(DEFAULT_TOWER_TRACE_ID), String::from); + if !status.is_client_error() && !status.is_server_error() { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Ok(ResponseContent { status, content, entity }) + Ok(ResponseContent { tower_trace_id, status, content, entity }) } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { tower_trace_id, status, content, entity })) } } @@ -2763,14 +3249,20 @@ pub async fn update_account_slug(configuration: &configuration::Configuration, p let status = resp.status(); + let tower_trace_id = resp + .headers() + .get("x-tower-trace-id") + .and_then(|v| v.to_str().ok()) + .map_or(String::from(DEFAULT_TOWER_TRACE_ID), String::from); + if !status.is_client_error() && !status.is_server_error() { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Ok(ResponseContent { status, content, entity }) + Ok(ResponseContent { tower_trace_id, status, content, entity }) } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { tower_trace_id, status, content, entity })) } } @@ -2793,14 +3285,20 @@ pub async fn update_app(configuration: &configuration::Configuration, params: Up let status = resp.status(); + let tower_trace_id = resp + .headers() + .get("x-tower-trace-id") + .and_then(|v| v.to_str().ok()) + .map_or(String::from(DEFAULT_TOWER_TRACE_ID), String::from); + if !status.is_client_error() && !status.is_server_error() { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Ok(ResponseContent { status, content, entity }) + Ok(ResponseContent { tower_trace_id, status, content, entity }) } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { tower_trace_id, status, content, entity })) } } @@ -2823,14 +3321,20 @@ pub async fn update_catalog(configuration: &configuration::Configuration, params let status = resp.status(); + let tower_trace_id = resp + .headers() + .get("x-tower-trace-id") + .and_then(|v| v.to_str().ok()) + .map_or(String::from(DEFAULT_TOWER_TRACE_ID), String::from); + if !status.is_client_error() && !status.is_server_error() { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Ok(ResponseContent { status, content, entity }) + Ok(ResponseContent { tower_trace_id, status, content, entity }) } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { tower_trace_id, status, content, entity })) } } @@ -2853,14 +3357,20 @@ pub async fn update_my_team_invitation(configuration: &configuration::Configurat let status = resp.status(); + let tower_trace_id = resp + .headers() + .get("x-tower-trace-id") + .and_then(|v| v.to_str().ok()) + .map_or(String::from(DEFAULT_TOWER_TRACE_ID), String::from); + if !status.is_client_error() && !status.is_server_error() { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Ok(ResponseContent { status, content, entity }) + Ok(ResponseContent { tower_trace_id, status, content, entity }) } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { tower_trace_id, status, content, entity })) } } @@ -2883,14 +3393,20 @@ pub async fn update_secret(configuration: &configuration::Configuration, params: let status = resp.status(); + let tower_trace_id = resp + .headers() + .get("x-tower-trace-id") + .and_then(|v| v.to_str().ok()) + .map_or(String::from(DEFAULT_TOWER_TRACE_ID), String::from); + if !status.is_client_error() && !status.is_server_error() { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Ok(ResponseContent { status, content, entity }) + Ok(ResponseContent { tower_trace_id, status, content, entity }) } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { tower_trace_id, status, content, entity })) } } @@ -2913,14 +3429,20 @@ pub async fn update_team(configuration: &configuration::Configuration, params: U let status = resp.status(); + let tower_trace_id = resp + .headers() + .get("x-tower-trace-id") + .and_then(|v| v.to_str().ok()) + .map_or(String::from(DEFAULT_TOWER_TRACE_ID), String::from); + if !status.is_client_error() && !status.is_server_error() { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Ok(ResponseContent { status, content, entity }) + Ok(ResponseContent { tower_trace_id, status, content, entity }) } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { tower_trace_id, status, content, entity })) } } @@ -2943,14 +3465,20 @@ pub async fn update_user(configuration: &configuration::Configuration, params: U let status = resp.status(); + let tower_trace_id = resp + .headers() + .get("x-tower-trace-id") + .and_then(|v| v.to_str().ok()) + .map_or(String::from(DEFAULT_TOWER_TRACE_ID), String::from); + if !status.is_client_error() && !status.is_server_error() { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Ok(ResponseContent { status, content, entity }) + Ok(ResponseContent { tower_trace_id, status, content, entity }) } else { let content = resp.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + Err(Error::ResponseError(ResponseContent { tower_trace_id, status, content, entity })) } } diff --git a/crates/tower-api/src/apis/mod.rs b/crates/tower-api/src/apis/mod.rs index fdcc89b3..54c07687 100644 --- a/crates/tower-api/src/apis/mod.rs +++ b/crates/tower-api/src/apis/mod.rs @@ -3,6 +3,7 @@ use std::fmt; #[derive(Debug, Clone)] pub struct ResponseContent { + pub tower_trace_id: String, pub status: reqwest::StatusCode, pub content: String, pub entity: Option, diff --git a/crates/tower-api/src/models/accept_invitation_params.rs b/crates/tower-api/src/models/accept_invitation_params.rs index 54fae2a7..42c07c12 100644 --- a/crates/tower-api/src/models/accept_invitation_params.rs +++ b/crates/tower-api/src/models/accept_invitation_params.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/accept_invitation_response.rs b/crates/tower-api/src/models/accept_invitation_response.rs index 0bfd8457..c63bfb7b 100644 --- a/crates/tower-api/src/models/accept_invitation_response.rs +++ b/crates/tower-api/src/models/accept_invitation_response.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/account.rs b/crates/tower-api/src/models/account.rs index adead7d3..ca96476e 100644 --- a/crates/tower-api/src/models/account.rs +++ b/crates/tower-api/src/models/account.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/acknowledge_alert_response.rs b/crates/tower-api/src/models/acknowledge_alert_response.rs new file mode 100644 index 00000000..0e6d9c54 --- /dev/null +++ b/crates/tower-api/src/models/acknowledge_alert_response.rs @@ -0,0 +1,31 @@ +/* + * Tower API + * + * REST API to interact with Tower Services. + * + * The version of the OpenAPI document: v0.5.9 + * Contact: hello@tower.dev + * Generated by: https://openapi-generator.tech + */ + +use crate::models; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)] +pub struct AcknowledgeAlertResponse { + /// A URL to the JSON Schema for this object. + #[serde(rename = "$schema", skip_serializing_if = "Option::is_none")] + pub schema: Option, + #[serde(rename = "status")] + pub status: String, +} + +impl AcknowledgeAlertResponse { + pub fn new(status: String) -> AcknowledgeAlertResponse { + AcknowledgeAlertResponse { + schema: None, + status, + } + } +} + diff --git a/crates/tower-api/src/models/alert.rs b/crates/tower-api/src/models/alert.rs new file mode 100644 index 00000000..f7f7dc8d --- /dev/null +++ b/crates/tower-api/src/models/alert.rs @@ -0,0 +1,74 @@ +/* + * Tower API + * + * REST API to interact with Tower Services. + * + * The version of the OpenAPI document: v0.5.9 + * Contact: hello@tower.dev + * Generated by: https://openapi-generator.tech + */ + +use crate::models; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)] +pub struct Alert { + /// Type of the alert + #[serde(rename = "alert_type")] + pub alert_type: AlertType, + /// Time when the alert was created + #[serde(rename = "created_at")] + pub created_at: String, + /// Detailed description of the alert + #[serde(rename = "details")] + pub details: Vec, + /// Unique identifier for the alert + #[serde(rename = "id")] + pub id: String, + /// Status of the alert + #[serde(rename = "status")] + pub status: Status, +} + +impl Alert { + pub fn new(alert_type: AlertType, created_at: String, details: Vec, id: String, status: Status) -> Alert { + Alert { + alert_type, + created_at, + details, + id, + status, + } + } +} +/// Type of the alert +#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)] +pub enum AlertType { + #[serde(rename = "error")] + Error, + #[serde(rename = "success")] + Success, +} + +impl Default for AlertType { + fn default() -> AlertType { + Self::Error + } +} +/// Status of the alert +#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)] +pub enum Status { + #[serde(rename = "pending")] + Pending, + #[serde(rename = "sent")] + Sent, + #[serde(rename = "errored")] + Errored, +} + +impl Default for Status { + fn default() -> Status { + Self::Pending + } +} + diff --git a/crates/tower-api/src/models/alert_detail.rs b/crates/tower-api/src/models/alert_detail.rs new file mode 100644 index 00000000..f3bc5ee1 --- /dev/null +++ b/crates/tower-api/src/models/alert_detail.rs @@ -0,0 +1,30 @@ +/* + * Tower API + * + * REST API to interact with Tower Services. + * + * The version of the OpenAPI document: v0.5.9 + * Contact: hello@tower.dev + * Generated by: https://openapi-generator.tech + */ + +use crate::models; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)] +pub struct AlertDetail { + #[serde(rename = "description")] + pub description: String, + #[serde(rename = "name")] + pub name: String, +} + +impl AlertDetail { + pub fn new(description: String, name: String) -> AlertDetail { + AlertDetail { + description, + name, + } + } +} + diff --git a/crates/tower-api/src/models/api_key.rs b/crates/tower-api/src/models/api_key.rs index 5306df47..f8dd800a 100644 --- a/crates/tower-api/src/models/api_key.rs +++ b/crates/tower-api/src/models/api_key.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/app.rs b/crates/tower-api/src/models/app.rs index ff53c5e4..d70d33b9 100644 --- a/crates/tower-api/src/models/app.rs +++ b/crates/tower-api/src/models/app.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/app_statistics.rs b/crates/tower-api/src/models/app_statistics.rs index c3264f90..7c5b4c3f 100644 --- a/crates/tower-api/src/models/app_statistics.rs +++ b/crates/tower-api/src/models/app_statistics.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/app_summary.rs b/crates/tower-api/src/models/app_summary.rs index 6e8f5c66..204795c7 100644 --- a/crates/tower-api/src/models/app_summary.rs +++ b/crates/tower-api/src/models/app_summary.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/app_version.rs b/crates/tower-api/src/models/app_version.rs index 65d19fdf..8297184b 100644 --- a/crates/tower-api/src/models/app_version.rs +++ b/crates/tower-api/src/models/app_version.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/cancel_run_response.rs b/crates/tower-api/src/models/cancel_run_response.rs index 36409b62..402e43e7 100644 --- a/crates/tower-api/src/models/cancel_run_response.rs +++ b/crates/tower-api/src/models/cancel_run_response.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/catalog.rs b/crates/tower-api/src/models/catalog.rs index 0bb7c948..7be361a5 100644 --- a/crates/tower-api/src/models/catalog.rs +++ b/crates/tower-api/src/models/catalog.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/catalog_property.rs b/crates/tower-api/src/models/catalog_property.rs index 5acceed8..7c3b7e50 100644 --- a/crates/tower-api/src/models/catalog_property.rs +++ b/crates/tower-api/src/models/catalog_property.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/claim_device_login_ticket_params.rs b/crates/tower-api/src/models/claim_device_login_ticket_params.rs index b18dd7d8..dc89f14d 100644 --- a/crates/tower-api/src/models/claim_device_login_ticket_params.rs +++ b/crates/tower-api/src/models/claim_device_login_ticket_params.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/claim_device_login_ticket_response.rs b/crates/tower-api/src/models/claim_device_login_ticket_response.rs index 7600e469..fcee5163 100644 --- a/crates/tower-api/src/models/claim_device_login_ticket_response.rs +++ b/crates/tower-api/src/models/claim_device_login_ticket_response.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/create_account_params.rs b/crates/tower-api/src/models/create_account_params.rs index b4e553de..86981a3c 100644 --- a/crates/tower-api/src/models/create_account_params.rs +++ b/crates/tower-api/src/models/create_account_params.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/create_account_params_flags_struct.rs b/crates/tower-api/src/models/create_account_params_flags_struct.rs index c9ee2007..82ea3d50 100644 --- a/crates/tower-api/src/models/create_account_params_flags_struct.rs +++ b/crates/tower-api/src/models/create_account_params_flags_struct.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/create_account_response.rs b/crates/tower-api/src/models/create_account_response.rs index 9f1f4ace..3245b701 100644 --- a/crates/tower-api/src/models/create_account_response.rs +++ b/crates/tower-api/src/models/create_account_response.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/create_api_key_params.rs b/crates/tower-api/src/models/create_api_key_params.rs index e9328ae1..d7e12be6 100644 --- a/crates/tower-api/src/models/create_api_key_params.rs +++ b/crates/tower-api/src/models/create_api_key_params.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/create_api_key_response.rs b/crates/tower-api/src/models/create_api_key_response.rs index 291752aa..6c9b9184 100644 --- a/crates/tower-api/src/models/create_api_key_response.rs +++ b/crates/tower-api/src/models/create_api_key_response.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/create_app_params.rs b/crates/tower-api/src/models/create_app_params.rs index 117c55bc..3b9e2ca1 100644 --- a/crates/tower-api/src/models/create_app_params.rs +++ b/crates/tower-api/src/models/create_app_params.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/create_app_response.rs b/crates/tower-api/src/models/create_app_response.rs index 0ea6ef3e..0c2e3a19 100644 --- a/crates/tower-api/src/models/create_app_response.rs +++ b/crates/tower-api/src/models/create_app_response.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/create_catalog_params.rs b/crates/tower-api/src/models/create_catalog_params.rs index a7b971c4..bd4bf3fe 100644 --- a/crates/tower-api/src/models/create_catalog_params.rs +++ b/crates/tower-api/src/models/create_catalog_params.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/create_catalog_response.rs b/crates/tower-api/src/models/create_catalog_response.rs index ad1d6c2e..6cbbf74c 100644 --- a/crates/tower-api/src/models/create_catalog_response.rs +++ b/crates/tower-api/src/models/create_catalog_response.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/create_device_login_ticket_response.rs b/crates/tower-api/src/models/create_device_login_ticket_response.rs index 092c7c6f..f70d8d9b 100644 --- a/crates/tower-api/src/models/create_device_login_ticket_response.rs +++ b/crates/tower-api/src/models/create_device_login_ticket_response.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/create_secret_params.rs b/crates/tower-api/src/models/create_secret_params.rs index df5b46d4..c39e5eed 100644 --- a/crates/tower-api/src/models/create_secret_params.rs +++ b/crates/tower-api/src/models/create_secret_params.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/create_secret_response.rs b/crates/tower-api/src/models/create_secret_response.rs index 295b20d3..db08d26f 100644 --- a/crates/tower-api/src/models/create_secret_response.rs +++ b/crates/tower-api/src/models/create_secret_response.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/create_session_params.rs b/crates/tower-api/src/models/create_session_params.rs index 649caf2b..5c0bcf74 100644 --- a/crates/tower-api/src/models/create_session_params.rs +++ b/crates/tower-api/src/models/create_session_params.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/create_session_response.rs b/crates/tower-api/src/models/create_session_response.rs index 4217cf5e..84e288d5 100644 --- a/crates/tower-api/src/models/create_session_response.rs +++ b/crates/tower-api/src/models/create_session_response.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/create_team_params.rs b/crates/tower-api/src/models/create_team_params.rs index 09818998..c4878123 100644 --- a/crates/tower-api/src/models/create_team_params.rs +++ b/crates/tower-api/src/models/create_team_params.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/create_team_response.rs b/crates/tower-api/src/models/create_team_response.rs index ba14251e..9f7d542f 100644 --- a/crates/tower-api/src/models/create_team_response.rs +++ b/crates/tower-api/src/models/create_team_response.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/delete_api_key_params.rs b/crates/tower-api/src/models/delete_api_key_params.rs index dba14f2b..45085ce2 100644 --- a/crates/tower-api/src/models/delete_api_key_params.rs +++ b/crates/tower-api/src/models/delete_api_key_params.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/delete_api_key_response.rs b/crates/tower-api/src/models/delete_api_key_response.rs index de11ed26..fce18fd0 100644 --- a/crates/tower-api/src/models/delete_api_key_response.rs +++ b/crates/tower-api/src/models/delete_api_key_response.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/delete_app_response.rs b/crates/tower-api/src/models/delete_app_response.rs index 361e51cb..bb8882f0 100644 --- a/crates/tower-api/src/models/delete_app_response.rs +++ b/crates/tower-api/src/models/delete_app_response.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/delete_catalog_response.rs b/crates/tower-api/src/models/delete_catalog_response.rs index da44ba06..4c188fb9 100644 --- a/crates/tower-api/src/models/delete_catalog_response.rs +++ b/crates/tower-api/src/models/delete_catalog_response.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/delete_team_invitation_params.rs b/crates/tower-api/src/models/delete_team_invitation_params.rs index 92a6ef7b..bb3a6dfd 100644 --- a/crates/tower-api/src/models/delete_team_invitation_params.rs +++ b/crates/tower-api/src/models/delete_team_invitation_params.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/delete_team_invitation_response.rs b/crates/tower-api/src/models/delete_team_invitation_response.rs index 10ded0a5..e1eee20f 100644 --- a/crates/tower-api/src/models/delete_team_invitation_response.rs +++ b/crates/tower-api/src/models/delete_team_invitation_response.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/delete_team_params.rs b/crates/tower-api/src/models/delete_team_params.rs index e016f5b0..293d6b43 100644 --- a/crates/tower-api/src/models/delete_team_params.rs +++ b/crates/tower-api/src/models/delete_team_params.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/delete_team_response.rs b/crates/tower-api/src/models/delete_team_response.rs index 1b584806..c679e3bd 100644 --- a/crates/tower-api/src/models/delete_team_response.rs +++ b/crates/tower-api/src/models/delete_team_response.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/deploy_app_response.rs b/crates/tower-api/src/models/deploy_app_response.rs index 01e0273b..bbffc569 100644 --- a/crates/tower-api/src/models/deploy_app_response.rs +++ b/crates/tower-api/src/models/deploy_app_response.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/describe_app_response.rs b/crates/tower-api/src/models/describe_app_response.rs index 31934f47..4af26166 100644 --- a/crates/tower-api/src/models/describe_app_response.rs +++ b/crates/tower-api/src/models/describe_app_response.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/describe_app_version_response.rs b/crates/tower-api/src/models/describe_app_version_response.rs index 48b96a96..bdfd604f 100644 --- a/crates/tower-api/src/models/describe_app_version_response.rs +++ b/crates/tower-api/src/models/describe_app_version_response.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/describe_device_login_session_response.rs b/crates/tower-api/src/models/describe_device_login_session_response.rs index 968be731..02132c3f 100644 --- a/crates/tower-api/src/models/describe_device_login_session_response.rs +++ b/crates/tower-api/src/models/describe_device_login_session_response.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/describe_run_logs_response.rs b/crates/tower-api/src/models/describe_run_logs_response.rs index bbc066bf..acaf0e33 100644 --- a/crates/tower-api/src/models/describe_run_logs_response.rs +++ b/crates/tower-api/src/models/describe_run_logs_response.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/describe_run_response.rs b/crates/tower-api/src/models/describe_run_response.rs index ccc57a2f..63c4381e 100644 --- a/crates/tower-api/src/models/describe_run_response.rs +++ b/crates/tower-api/src/models/describe_run_response.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/describe_secrets_key_response.rs b/crates/tower-api/src/models/describe_secrets_key_response.rs index 9f49cd06..7fd64875 100644 --- a/crates/tower-api/src/models/describe_secrets_key_response.rs +++ b/crates/tower-api/src/models/describe_secrets_key_response.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/describe_session_response.rs b/crates/tower-api/src/models/describe_session_response.rs index ecda7907..85a3c0d7 100644 --- a/crates/tower-api/src/models/describe_session_response.rs +++ b/crates/tower-api/src/models/describe_session_response.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/encrypted_catalog_property.rs b/crates/tower-api/src/models/encrypted_catalog_property.rs index 52e55eff..59732b20 100644 --- a/crates/tower-api/src/models/encrypted_catalog_property.rs +++ b/crates/tower-api/src/models/encrypted_catalog_property.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/error_detail.rs b/crates/tower-api/src/models/error_detail.rs index 88db6deb..3732c59f 100644 --- a/crates/tower-api/src/models/error_detail.rs +++ b/crates/tower-api/src/models/error_detail.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/error_model.rs b/crates/tower-api/src/models/error_model.rs index 12c35496..b22e9d4f 100644 --- a/crates/tower-api/src/models/error_model.rs +++ b/crates/tower-api/src/models/error_model.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/export_secrets_response.rs b/crates/tower-api/src/models/export_secrets_response.rs index c1e4f17d..94d29790 100644 --- a/crates/tower-api/src/models/export_secrets_response.rs +++ b/crates/tower-api/src/models/export_secrets_response.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/export_user_secrets_params.rs b/crates/tower-api/src/models/export_user_secrets_params.rs index 9e5cd340..ede929dc 100644 --- a/crates/tower-api/src/models/export_user_secrets_params.rs +++ b/crates/tower-api/src/models/export_user_secrets_params.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/exported_secret.rs b/crates/tower-api/src/models/exported_secret.rs index 6c708ea5..17c70dca 100644 --- a/crates/tower-api/src/models/exported_secret.rs +++ b/crates/tower-api/src/models/exported_secret.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/generate_app_statistics_response.rs b/crates/tower-api/src/models/generate_app_statistics_response.rs index f4f7dd09..caf8a377 100644 --- a/crates/tower-api/src/models/generate_app_statistics_response.rs +++ b/crates/tower-api/src/models/generate_app_statistics_response.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/generate_run_statistics_response.rs b/crates/tower-api/src/models/generate_run_statistics_response.rs index 184c4ae4..157c97a9 100644 --- a/crates/tower-api/src/models/generate_run_statistics_response.rs +++ b/crates/tower-api/src/models/generate_run_statistics_response.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/invite_team_member_params.rs b/crates/tower-api/src/models/invite_team_member_params.rs index 283a96b4..6e9a6195 100644 --- a/crates/tower-api/src/models/invite_team_member_params.rs +++ b/crates/tower-api/src/models/invite_team_member_params.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/invite_team_member_response.rs b/crates/tower-api/src/models/invite_team_member_response.rs index b3e9b421..26a8c9e3 100644 --- a/crates/tower-api/src/models/invite_team_member_response.rs +++ b/crates/tower-api/src/models/invite_team_member_response.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/leave_team_response.rs b/crates/tower-api/src/models/leave_team_response.rs index 5e4faff7..d177eebd 100644 --- a/crates/tower-api/src/models/leave_team_response.rs +++ b/crates/tower-api/src/models/leave_team_response.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/list_alerts_response.rs b/crates/tower-api/src/models/list_alerts_response.rs new file mode 100644 index 00000000..3dddfced --- /dev/null +++ b/crates/tower-api/src/models/list_alerts_response.rs @@ -0,0 +1,36 @@ +/* + * Tower API + * + * REST API to interact with Tower Services. + * + * The version of the OpenAPI document: v0.5.9 + * Contact: hello@tower.dev + * Generated by: https://openapi-generator.tech + */ + +use crate::models; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)] +pub struct ListAlertsResponse { + /// A URL to the JSON Schema for this object. + #[serde(rename = "$schema", skip_serializing_if = "Option::is_none")] + pub schema: Option, + /// List of alerts + #[serde(rename = "alerts")] + pub alerts: Vec, + /// Pagination information + #[serde(rename = "pages")] + pub pages: Box, +} + +impl ListAlertsResponse { + pub fn new(alerts: Vec, pages: models::Pagination) -> ListAlertsResponse { + ListAlertsResponse { + schema: None, + alerts, + pages: Box::new(pages), + } + } +} + diff --git a/crates/tower-api/src/models/list_api_keys_response.rs b/crates/tower-api/src/models/list_api_keys_response.rs index 588e7432..6795b261 100644 --- a/crates/tower-api/src/models/list_api_keys_response.rs +++ b/crates/tower-api/src/models/list_api_keys_response.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/list_app_environments_response.rs b/crates/tower-api/src/models/list_app_environments_response.rs index b33e6f08..78e33fcd 100644 --- a/crates/tower-api/src/models/list_app_environments_response.rs +++ b/crates/tower-api/src/models/list_app_environments_response.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/list_app_versions_response.rs b/crates/tower-api/src/models/list_app_versions_response.rs index d2c3b4d6..d48ce900 100644 --- a/crates/tower-api/src/models/list_app_versions_response.rs +++ b/crates/tower-api/src/models/list_app_versions_response.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/list_apps_response.rs b/crates/tower-api/src/models/list_apps_response.rs index 90d5747f..efabc7de 100644 --- a/crates/tower-api/src/models/list_apps_response.rs +++ b/crates/tower-api/src/models/list_apps_response.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/list_catalogs_response.rs b/crates/tower-api/src/models/list_catalogs_response.rs index fefad471..360ba1ef 100644 --- a/crates/tower-api/src/models/list_catalogs_response.rs +++ b/crates/tower-api/src/models/list_catalogs_response.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/list_my_team_invitations_response.rs b/crates/tower-api/src/models/list_my_team_invitations_response.rs index d1688a52..adc9378f 100644 --- a/crates/tower-api/src/models/list_my_team_invitations_response.rs +++ b/crates/tower-api/src/models/list_my_team_invitations_response.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/list_runs_response.rs b/crates/tower-api/src/models/list_runs_response.rs index d9cb638c..886c4f68 100644 --- a/crates/tower-api/src/models/list_runs_response.rs +++ b/crates/tower-api/src/models/list_runs_response.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/list_secret_environments_response.rs b/crates/tower-api/src/models/list_secret_environments_response.rs index 67babe7f..d930c1ef 100644 --- a/crates/tower-api/src/models/list_secret_environments_response.rs +++ b/crates/tower-api/src/models/list_secret_environments_response.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/list_secrets_response.rs b/crates/tower-api/src/models/list_secrets_response.rs index 4aee4ecd..4b555826 100644 --- a/crates/tower-api/src/models/list_secrets_response.rs +++ b/crates/tower-api/src/models/list_secrets_response.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/list_team_invitations_response.rs b/crates/tower-api/src/models/list_team_invitations_response.rs index 17b3fc76..b1626a7c 100644 --- a/crates/tower-api/src/models/list_team_invitations_response.rs +++ b/crates/tower-api/src/models/list_team_invitations_response.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/list_team_members_response.rs b/crates/tower-api/src/models/list_team_members_response.rs index d50936fe..c9068564 100644 --- a/crates/tower-api/src/models/list_team_members_response.rs +++ b/crates/tower-api/src/models/list_team_members_response.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/list_teams_response.rs b/crates/tower-api/src/models/list_teams_response.rs index f94a5eed..99ecb55f 100644 --- a/crates/tower-api/src/models/list_teams_response.rs +++ b/crates/tower-api/src/models/list_teams_response.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/log_line.rs b/crates/tower-api/src/models/log_line.rs index b9bacde1..07a81fd0 100644 --- a/crates/tower-api/src/models/log_line.rs +++ b/crates/tower-api/src/models/log_line.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/log_line_error.rs b/crates/tower-api/src/models/log_line_error.rs index 7f51ed1f..b685301c 100644 --- a/crates/tower-api/src/models/log_line_error.rs +++ b/crates/tower-api/src/models/log_line_error.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/mod.rs b/crates/tower-api/src/models/mod.rs index 539ff2d9..b70b1f0e 100644 --- a/crates/tower-api/src/models/mod.rs +++ b/crates/tower-api/src/models/mod.rs @@ -4,6 +4,12 @@ pub mod accept_invitation_response; pub use self::accept_invitation_response::AcceptInvitationResponse; pub mod account; pub use self::account::Account; +pub mod acknowledge_alert_response; +pub use self::acknowledge_alert_response::AcknowledgeAlertResponse; +pub mod alert; +pub use self::alert::Alert; +pub mod alert_detail; +pub use self::alert_detail::AlertDetail; pub mod api_key; pub use self::api_key::ApiKey; pub mod app; @@ -110,6 +116,8 @@ pub mod invite_team_member_response; pub use self::invite_team_member_response::InviteTeamMemberResponse; pub mod leave_team_response; pub use self::leave_team_response::LeaveTeamResponse; +pub mod list_alerts_response; +pub use self::list_alerts_response::ListAlertsResponse; pub mod list_api_keys_response; pub use self::list_api_keys_response::ListApiKeysResponse; pub mod list_app_environments_response; @@ -162,6 +170,8 @@ pub mod run_app_response; pub use self::run_app_response::RunAppResponse; pub mod run_log_line; pub use self::run_log_line::RunLogLine; +pub mod run_parameter; +pub use self::run_parameter::RunParameter; pub mod run_results; pub use self::run_results::RunResults; pub mod run_statistics; diff --git a/crates/tower-api/src/models/pagination.rs b/crates/tower-api/src/models/pagination.rs index bff0a467..ec157f79 100644 --- a/crates/tower-api/src/models/pagination.rs +++ b/crates/tower-api/src/models/pagination.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/parameter.rs b/crates/tower-api/src/models/parameter.rs index 2bf53713..1890d83a 100644 --- a/crates/tower-api/src/models/parameter.rs +++ b/crates/tower-api/src/models/parameter.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/refresh_session_params.rs b/crates/tower-api/src/models/refresh_session_params.rs index 1b4a1a84..391ee86d 100644 --- a/crates/tower-api/src/models/refresh_session_params.rs +++ b/crates/tower-api/src/models/refresh_session_params.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/refresh_session_response.rs b/crates/tower-api/src/models/refresh_session_response.rs index ab63796d..011633d4 100644 --- a/crates/tower-api/src/models/refresh_session_response.rs +++ b/crates/tower-api/src/models/refresh_session_response.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/remove_team_member_params.rs b/crates/tower-api/src/models/remove_team_member_params.rs index 4e930dfe..359e806d 100644 --- a/crates/tower-api/src/models/remove_team_member_params.rs +++ b/crates/tower-api/src/models/remove_team_member_params.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/remove_team_member_response.rs b/crates/tower-api/src/models/remove_team_member_response.rs index 66cc0d07..7f0c24d3 100644 --- a/crates/tower-api/src/models/remove_team_member_response.rs +++ b/crates/tower-api/src/models/remove_team_member_response.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/resend_team_invitation_params.rs b/crates/tower-api/src/models/resend_team_invitation_params.rs index 0860feb4..9a37d025 100644 --- a/crates/tower-api/src/models/resend_team_invitation_params.rs +++ b/crates/tower-api/src/models/resend_team_invitation_params.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/resend_team_invitation_response.rs b/crates/tower-api/src/models/resend_team_invitation_response.rs index 5a512fd5..791f8472 100644 --- a/crates/tower-api/src/models/resend_team_invitation_response.rs +++ b/crates/tower-api/src/models/resend_team_invitation_response.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/run.rs b/crates/tower-api/src/models/run.rs index e028b0e9..18f8f183 100644 --- a/crates/tower-api/src/models/run.rs +++ b/crates/tower-api/src/models/run.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ @@ -27,6 +27,9 @@ pub struct Run { pub environment: String, #[serde(rename = "number")] pub number: i64, + /// Parameters used to invoke this run. + #[serde(rename = "parameters")] + pub parameters: Vec, #[serde(rename = "run_id")] pub run_id: String, #[serde(rename = "scheduled_at")] @@ -40,7 +43,7 @@ pub struct Run { } impl Run { - pub fn new(app_slug: String, app_version: String, cancelled_at: Option, created_at: String, ended_at: Option, environment: String, number: i64, run_id: String, scheduled_at: String, started_at: Option, status: Status, status_group: StatusGroup) -> Run { + pub fn new(app_slug: String, app_version: String, cancelled_at: Option, created_at: String, ended_at: Option, environment: String, number: i64, parameters: Vec, run_id: String, scheduled_at: String, started_at: Option, status: Status, status_group: StatusGroup) -> Run { Run { app_slug, app_version, @@ -49,6 +52,7 @@ impl Run { ended_at, environment, number, + parameters, run_id, scheduled_at, started_at, diff --git a/crates/tower-api/src/models/run_app_params.rs b/crates/tower-api/src/models/run_app_params.rs index 1cd45946..60a306ca 100644 --- a/crates/tower-api/src/models/run_app_params.rs +++ b/crates/tower-api/src/models/run_app_params.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/run_app_response.rs b/crates/tower-api/src/models/run_app_response.rs index 30282401..b340dc52 100644 --- a/crates/tower-api/src/models/run_app_response.rs +++ b/crates/tower-api/src/models/run_app_response.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/run_log_line.rs b/crates/tower-api/src/models/run_log_line.rs index 4eb9a215..d8486c35 100644 --- a/crates/tower-api/src/models/run_log_line.rs +++ b/crates/tower-api/src/models/run_log_line.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/run_parameter.rs b/crates/tower-api/src/models/run_parameter.rs new file mode 100644 index 00000000..b342df4b --- /dev/null +++ b/crates/tower-api/src/models/run_parameter.rs @@ -0,0 +1,30 @@ +/* + * Tower API + * + * REST API to interact with Tower Services. + * + * The version of the OpenAPI document: v0.5.9 + * Contact: hello@tower.dev + * Generated by: https://openapi-generator.tech + */ + +use crate::models; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)] +pub struct RunParameter { + #[serde(rename = "name")] + pub name: String, + #[serde(rename = "value")] + pub value: String, +} + +impl RunParameter { + pub fn new(name: String, value: String) -> RunParameter { + RunParameter { + name, + value, + } + } +} + diff --git a/crates/tower-api/src/models/run_results.rs b/crates/tower-api/src/models/run_results.rs index 1f26e70f..e8743378 100644 --- a/crates/tower-api/src/models/run_results.rs +++ b/crates/tower-api/src/models/run_results.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/run_statistics.rs b/crates/tower-api/src/models/run_statistics.rs index 2ff9235d..f20a767c 100644 --- a/crates/tower-api/src/models/run_statistics.rs +++ b/crates/tower-api/src/models/run_statistics.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/secret.rs b/crates/tower-api/src/models/secret.rs index 47da8abe..4d2024bf 100644 --- a/crates/tower-api/src/models/secret.rs +++ b/crates/tower-api/src/models/secret.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/series_point.rs b/crates/tower-api/src/models/series_point.rs index 34ba10e0..2f4b0d8d 100644 --- a/crates/tower-api/src/models/series_point.rs +++ b/crates/tower-api/src/models/series_point.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/session.rs b/crates/tower-api/src/models/session.rs index 316b9da0..8e0d0656 100644 --- a/crates/tower-api/src/models/session.rs +++ b/crates/tower-api/src/models/session.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/statistics_settings.rs b/crates/tower-api/src/models/statistics_settings.rs index 4f6c0e6f..3adfe640 100644 --- a/crates/tower-api/src/models/statistics_settings.rs +++ b/crates/tower-api/src/models/statistics_settings.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/stream_run_logs_200_response_inner.rs b/crates/tower-api/src/models/stream_run_logs_200_response_inner.rs index b2d80172..08d0f1a4 100644 --- a/crates/tower-api/src/models/stream_run_logs_200_response_inner.rs +++ b/crates/tower-api/src/models/stream_run_logs_200_response_inner.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/team.rs b/crates/tower-api/src/models/team.rs index 9581cc86..593b786d 100644 --- a/crates/tower-api/src/models/team.rs +++ b/crates/tower-api/src/models/team.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/team_invitation.rs b/crates/tower-api/src/models/team_invitation.rs index c1c7060e..e466fc10 100644 --- a/crates/tower-api/src/models/team_invitation.rs +++ b/crates/tower-api/src/models/team_invitation.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/token.rs b/crates/tower-api/src/models/token.rs index 972f2ae4..7f2c947b 100644 --- a/crates/tower-api/src/models/token.rs +++ b/crates/tower-api/src/models/token.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/update_account_slug_params.rs b/crates/tower-api/src/models/update_account_slug_params.rs index 8ed95e37..b5192976 100644 --- a/crates/tower-api/src/models/update_account_slug_params.rs +++ b/crates/tower-api/src/models/update_account_slug_params.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/update_account_slug_response.rs b/crates/tower-api/src/models/update_account_slug_response.rs index 58de0705..58dda7bd 100644 --- a/crates/tower-api/src/models/update_account_slug_response.rs +++ b/crates/tower-api/src/models/update_account_slug_response.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/update_app_params.rs b/crates/tower-api/src/models/update_app_params.rs index d9ebc179..e340d33c 100644 --- a/crates/tower-api/src/models/update_app_params.rs +++ b/crates/tower-api/src/models/update_app_params.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/update_app_response.rs b/crates/tower-api/src/models/update_app_response.rs index fb83250e..0afde58f 100644 --- a/crates/tower-api/src/models/update_app_response.rs +++ b/crates/tower-api/src/models/update_app_response.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/update_catalog_params.rs b/crates/tower-api/src/models/update_catalog_params.rs index 21173fdb..8fe5d7d0 100644 --- a/crates/tower-api/src/models/update_catalog_params.rs +++ b/crates/tower-api/src/models/update_catalog_params.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/update_catalog_response.rs b/crates/tower-api/src/models/update_catalog_response.rs index 2e69f8dd..95ae3b79 100644 --- a/crates/tower-api/src/models/update_catalog_response.rs +++ b/crates/tower-api/src/models/update_catalog_response.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/update_my_team_invitation_params.rs b/crates/tower-api/src/models/update_my_team_invitation_params.rs index dabbf925..efb56e9d 100644 --- a/crates/tower-api/src/models/update_my_team_invitation_params.rs +++ b/crates/tower-api/src/models/update_my_team_invitation_params.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/update_my_team_invitation_response.rs b/crates/tower-api/src/models/update_my_team_invitation_response.rs index 4400c5c7..1e33da85 100644 --- a/crates/tower-api/src/models/update_my_team_invitation_response.rs +++ b/crates/tower-api/src/models/update_my_team_invitation_response.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/update_secret_params.rs b/crates/tower-api/src/models/update_secret_params.rs index 8fe5678f..7dcb3ec2 100644 --- a/crates/tower-api/src/models/update_secret_params.rs +++ b/crates/tower-api/src/models/update_secret_params.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/update_secret_response.rs b/crates/tower-api/src/models/update_secret_response.rs index 40513c03..7806d835 100644 --- a/crates/tower-api/src/models/update_secret_response.rs +++ b/crates/tower-api/src/models/update_secret_response.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/update_team_params.rs b/crates/tower-api/src/models/update_team_params.rs index e0bb3dd8..873ac06f 100644 --- a/crates/tower-api/src/models/update_team_params.rs +++ b/crates/tower-api/src/models/update_team_params.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/update_team_response.rs b/crates/tower-api/src/models/update_team_response.rs index 5c790303..8cdc9df2 100644 --- a/crates/tower-api/src/models/update_team_response.rs +++ b/crates/tower-api/src/models/update_team_response.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/update_user_params.rs b/crates/tower-api/src/models/update_user_params.rs index 9c642fe1..a68b6fed 100644 --- a/crates/tower-api/src/models/update_user_params.rs +++ b/crates/tower-api/src/models/update_user_params.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/update_user_response.rs b/crates/tower-api/src/models/update_user_response.rs index d8540a66..122dca0e 100644 --- a/crates/tower-api/src/models/update_user_response.rs +++ b/crates/tower-api/src/models/update_user_response.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-api/src/models/user.rs b/crates/tower-api/src/models/user.rs index 99d269bf..b94f84c0 100644 --- a/crates/tower-api/src/models/user.rs +++ b/crates/tower-api/src/models/user.rs @@ -3,7 +3,7 @@ * * REST API to interact with Tower Services. * - * The version of the OpenAPI document: v0.5.1 + * The version of the OpenAPI document: v0.5.9 * Contact: hello@tower.dev * Generated by: https://openapi-generator.tech */ diff --git a/crates/tower-cmd/src/api.rs b/crates/tower-cmd/src/api.rs index 3815481a..2240e0e4 100644 --- a/crates/tower-cmd/src/api.rs +++ b/crates/tower-cmd/src/api.rs @@ -32,7 +32,7 @@ pub async fn list_apps(config: &Config) -> Result { + log::debug!("tower trace ID: {}", response.tower_trace_id); log::debug!("Response from server: {}", response.content); if let Some(entity) = response.entity { @@ -199,6 +200,7 @@ where } else { let err = Error::ResponseError( tower_api::apis::ResponseContent { + tower_trace_id: "".to_string(), status: StatusCode::NO_CONTENT, content: "Received a response from the server that the CLI wasn't able to understand".to_string(), entity: None, @@ -209,6 +211,7 @@ where } else { let err = Error::ResponseError( tower_api::apis::ResponseContent { + tower_trace_id: "".to_string(), status: StatusCode::NO_CONTENT, content: "Empty response from server".to_string(), entity: None, diff --git a/crates/tower-cmd/src/apps.rs b/crates/tower-cmd/src/apps.rs index f2072388..bf2de986 100644 --- a/crates/tower-cmd/src/apps.rs +++ b/crates/tower-cmd/src/apps.rs @@ -11,20 +11,12 @@ use crate::{ pub fn apps_cmd() -> Command { Command::new("apps") - .about("Interact with the apps that you own") + .about("Manage the apps in your current Tower account") .arg_required_else_help(true) .subcommand(Command::new("list").about("List all of your apps`")) .subcommand( Command::new("show") .allow_external_subcommands(true) - .arg( - Arg::new("name") - .short('n') - .long("name") - .value_parser(value_parser!(String)) - .required(true) - .action(clap::ArgAction::Set), - ) .about("Show the details about an app in Tower"), ) .subcommand( @@ -62,20 +54,12 @@ pub fn apps_cmd() -> Command { .subcommand( Command::new("delete") .allow_external_subcommands(true) - .arg( - Arg::new("slug") - .short('n') - .long("slug") - .value_parser(value_parser!(String)) - .required(true) - .action(clap::ArgAction::Set), - ) .about("Delete an app in Tower"), ) } -pub async fn do_logs_app(config: Config, cmd: Option<(&str, &ArgMatches)>) { - let (slug, seq) = extract_app_run(cmd); +pub async fn do_logs(config: Config, cmd: &ArgMatches) { + let (slug, seq) = extract_app_slug_and_run("logs", cmd.subcommand()); if let Ok(resp) = api::describe_run_logs(&config, &slug, seq).await { for line in resp.log_lines { @@ -84,10 +68,10 @@ pub async fn do_logs_app(config: Config, cmd: Option<(&str, &ArgMatches)>) { } } -pub async fn do_show_app(config: Config, args: &ArgMatches) { - let slug = args.get_one::("slug").unwrap(); +pub async fn do_show(config: Config, cmd: &ArgMatches) { + let slug = extract_app_slug("show", cmd.subcommand()); - match api::describe_app(&config, slug).await { + match api::describe_app(&config, &slug).await { Ok(app_response) => { let app = app_response.app; let runs = app_response.runs; @@ -201,7 +185,7 @@ pub async fn do_list_apps(config: Config) { } } -pub async fn do_create_app(config: Config, args: &ArgMatches) { +pub async fn do_create(config: Config, args: &ArgMatches) { let name = args.get_one::("name").unwrap_or_else(|| { output::die("App name (--name) is required"); }); @@ -221,11 +205,11 @@ pub async fn do_create_app(config: Config, args: &ArgMatches) { } -pub async fn do_delete_app(config: Config, args: &ArgMatches) { - let slug = args.get_one::("slug").unwrap(); +pub async fn do_delete(config: Config, cmd: &ArgMatches) { + let slug = extract_app_slug("delete", cmd.subcommand()); let mut spinner = output::spinner("Deleting app"); - if let Err(err) = api::delete_app(&config, slug).await { + if let Err(err) = api::delete_app(&config, &slug).await { spinner.failure(); output::tower_error(err); } else { @@ -234,17 +218,29 @@ pub async fn do_delete_app(config: Config, args: &ArgMatches) { } /// Extract app name and run number from command -fn extract_app_run(cmd: Option<(&str, &ArgMatches)>) -> (String, i64) { +fn extract_app_slug_and_run(subcmd: &str, cmd: Option<(&str, &ArgMatches)>) -> (String, i64) { if let Some((slug, _)) = cmd { if let Some((slug, num)) = slug.split_once('#') { return ( slug.to_string(), num.parse::().unwrap_or_else(|_| { - output::die("Run number must be a valid number"); + output::die("Run number must be an actual number"); }), ); } - output::die("Run number is required (e.g. tower apps logs #)"); + + let line = format!("Run number is required. Example: tower apps {} #", subcmd); + output::die(&line); + } + let line = format!("App slug is required. Example: tower apps {} #", subcmd); + output::die(&line) +} + +fn extract_app_slug(subcmd: &str, cmd: Option<(&str, &ArgMatches)>) -> String { + if let Some((slug, _)) = cmd { + return slug.to_string(); } - output::die("App name (e.g. tower apps logs #) is required"); + + let line = format!("App slug is required. Example: tower apps {} ", subcmd); + output::die(&line); } diff --git a/crates/tower-cmd/src/lib.rs b/crates/tower-cmd/src/lib.rs index e2b1d27d..3fe628c9 100644 --- a/crates/tower-cmd/src/lib.rs +++ b/crates/tower-cmd/src/lib.rs @@ -25,7 +25,6 @@ impl App { // environment variable. This is for programmatic use cases where we want to test the CLI // in automated environments, for instance. let session = if let Ok(token) = std::env::var("TOWER_JWT") { - // let's exchange the token for a session, what we'll load. Session::from_jwt(&token).ok() } else { Session::from_config_dir().ok() @@ -91,12 +90,10 @@ impl App { match apps_command { Some(("list", _)) => apps::do_list_apps(sessionized_config).await, - Some(("show", args)) => apps::do_show_app(sessionized_config, args).await, - Some(("logs", args)) => { - apps::do_logs_app(sessionized_config, args.subcommand()).await - } - Some(("create", args)) => apps::do_create_app(sessionized_config, args).await, - Some(("delete", args)) => apps::do_delete_app(sessionized_config, args).await, + Some(("create", args)) => apps::do_create(sessionized_config, args).await, + Some(("show", args)) => apps::do_show(sessionized_config, args).await, + Some(("logs", args)) => apps::do_logs(sessionized_config, args).await, + Some(("delete", args)) => apps::do_delete(sessionized_config, args).await, _ => { apps::apps_cmd().print_help().unwrap(); std::process::exit(2); @@ -107,15 +104,9 @@ impl App { let secrets_command = sub_matches.subcommand(); match secrets_command { - Some(("list", args)) => { - secrets::do_list_secrets(sessionized_config, args).await - } - Some(("create", args)) => { - secrets::do_create_secret(sessionized_config, args).await - } - Some(("delete", args)) => { - secrets::do_delete_secret(sessionized_config, args).await - } + Some(("list", args)) => secrets::do_list(sessionized_config, args).await, + Some(("create", args)) => secrets::do_create(sessionized_config, args).await, + Some(("delete", args)) => secrets::do_delete(sessionized_config, args).await, _ => { secrets::secrets_cmd().print_help().unwrap(); std::process::exit(2); @@ -128,8 +119,8 @@ impl App { let teams_command = sub_matches.subcommand(); match teams_command { - Some(("list", _)) => teams::do_list_teams(sessionized_config).await, - Some(("switch", args)) => teams::do_switch_team(sessionized_config, args).await, + Some(("list", _)) => teams::do_list(sessionized_config).await, + Some(("switch", args)) => teams::do_switch(sessionized_config, args).await, _ => { teams::teams_cmd().print_help().unwrap(); std::process::exit(2); diff --git a/crates/tower-cmd/src/secrets.rs b/crates/tower-cmd/src/secrets.rs index 60b26e9e..614a8595 100644 --- a/crates/tower-cmd/src/secrets.rs +++ b/crates/tower-cmd/src/secrets.rs @@ -15,6 +15,7 @@ use tower_api::{ use crate::{ output, api, + util::cmd, }; pub fn secrets_cmd() -> Command { @@ -76,37 +77,21 @@ pub fn secrets_cmd() -> Command { .subcommand( Command::new("delete") .allow_external_subcommands(true) - .arg( - Arg::new("name") - .short('n') - .long("name") - .value_parser(value_parser!(String)) - .required(true) - .help("Name of the secret to delete") - .action(clap::ArgAction::Set), - ) - .arg( - Arg::new("environment") - .short('e') - .long("environment") - .default_value("default") - .value_parser(value_parser!(String)) - .action(clap::ArgAction::Set) - .help("Environment the secret belongs to"), - ) .about("Delete a secret in Tower"), ) } -pub async fn do_list_secrets(config: Config, args: &ArgMatches) { - let show = args.get_one::("show").unwrap_or(&false); - let env = args.get_one::("environment").unwrap(); - let all = args.get_one::("all").unwrap_or(&false); +pub async fn do_list(config: Config, args: &ArgMatches) { + let all = cmd::get_bool_flag(args, "all"); + let show = cmd::get_bool_flag(args, "show"); + let env = cmd::get_string_flag(args, "environment"); + + log::debug!("listing secrets, environment={} all={} show={}", env, all, show); - if *show { + if show { let (private_key, public_key) = crypto::generate_key_pair(); - match api::export_secrets(&config, env, *all, public_key).await { + match api::export_secrets(&config, &env, all, public_key).await { Ok(list_response) => { let headers = vec![ "Secret".bold().yellow().to_string(), @@ -131,7 +116,7 @@ pub async fn do_list_secrets(config: Config, args: &ArgMatches) { Err(err) => output::tower_error(err), } } else { - match api::list_secrets(&config, env, *all).await { + match api::list_secrets(&config, &env, all).await { Ok(list_response) => { let headers = vec![ "Secret".bold().yellow().to_string(), @@ -152,14 +137,14 @@ pub async fn do_list_secrets(config: Config, args: &ArgMatches) { } } -pub async fn do_create_secret(config: Config, args: &ArgMatches) { - let name = args.get_one::("name").unwrap(); - let environment = args.get_one::("environment").unwrap(); - let value = args.get_one::("value").unwrap(); +pub async fn do_create(config: Config, args: &ArgMatches) { + let name = cmd::get_string_flag(args, "name"); + let environment = cmd::get_string_flag(args, "environment"); + let value = cmd::get_string_flag(args, "value"); let mut spinner = output::spinner("Creating secret..."); - match encrypt_and_create_secret(&config, name, value, environment).await { + match encrypt_and_create_secret(&config, &name, &value, &environment).await { Ok(_) => { spinner.success(); @@ -178,16 +163,13 @@ pub async fn do_create_secret(config: Config, args: &ArgMatches) { } } -pub async fn do_delete_secret(config: Config, args: &ArgMatches) { - let env_default = "default".to_string(); - let name = args.get_one::("name").unwrap(); - let environment = args - .get_one::("environment") - .unwrap_or(&env_default); +pub async fn do_delete(config: Config, args: &ArgMatches) { + let (environment, name) = extract_secret_environment_and_name("delete", args.subcommand()); + log::debug!("deleting secret, environment={} name={}", environment, name); let mut spinner = output::spinner("Deleting secret..."); - if let Ok(_) = api::delete_secret(&config, name, environment).await { + if let Ok(_) = api::delete_secret(&config, &name, &environment).await { spinner.success(); } else { spinner.failure(); @@ -232,3 +214,17 @@ async fn encrypt_and_create_secret( } } } + +fn extract_secret_environment_and_name(subcmd: &str, cmd: Option<(&str, &ArgMatches)>) -> (String, String) { + if let Some((slug, _)) = cmd { + if let Some((env, name)) = slug.split_once('/') { + return (env.to_string(), name.to_string()); + } + + let line = format!("Secret name is required. Example: tower secrets {} /", subcmd); + output::die(&line); + } + + let line = format!("Secret name and environment is required. Example: tower secrets {} /", subcmd); + output::die(&line); +} diff --git a/crates/tower-cmd/src/teams.rs b/crates/tower-cmd/src/teams.rs index 22844d94..cabdae3b 100644 --- a/crates/tower-cmd/src/teams.rs +++ b/crates/tower-cmd/src/teams.rs @@ -1,4 +1,4 @@ -use clap::{value_parser, Arg, ArgMatches, Command}; +use clap::{ArgMatches, Command}; use colored::*; use config::Config; @@ -14,12 +14,8 @@ pub fn teams_cmd() -> Command { .subcommand(Command::new("list").about("List teams you belong to")) .subcommand( Command::new("switch") + .allow_external_subcommands(true) .about("Switch context to a different team") - .arg( - Arg::new("team_slug") - .value_parser(value_parser!(String)) - .action(clap::ArgAction::Set), - ), ) } @@ -60,7 +56,7 @@ async fn refresh_session(config: &Config) -> config::Session { } } -pub async fn do_list_teams(config: Config) { +pub async fn do_list(config: Config) { // Refresh the session and get the updated data let session = refresh_session(&config).await; @@ -121,24 +117,19 @@ pub async fn do_list_teams(config: Config) { output::newline(); } -pub async fn do_switch_team(config: Config, args: &ArgMatches) { - let team_slug = args - .get_one::("team_slug") - .map(|s| s.as_str()) - .unwrap_or_else(|| { - output::die("Team Slug (e.g. tower teams switch ) is required"); - }); +pub async fn do_switch(config: Config, args: &ArgMatches) { + let slug = extract_team_slug("switch", args.subcommand()); // Refresh the session first to ensure we have the latest teams data let session = refresh_session(&config).await; // Check if the provided team slug exists in the refreshed session - let team = session.teams.iter().find(|team| team.slug == team_slug); + let team = session.teams.iter().find(|team| team.slug == slug); match team { Some(team) => { // Team found, set it as active - match config.set_active_team_by_slug(team_slug) { + match config.set_active_team_by_slug(&slug) { Ok(_) => { output::success(&format!("Switched to team: {}", team.name)); } @@ -152,9 +143,18 @@ pub async fn do_switch_team(config: Config, args: &ArgMatches) { // Team not found output::failure(&format!( "Team '{}' not found. Use 'tower teams list' to see all your teams.", - team_slug + slug )); std::process::exit(1); } } } + +fn extract_team_slug(subcmd: &str, cmd: Option<(&str, &ArgMatches)>) -> String { + if let Some((slug, _)) = cmd { + return slug.to_string(); + } + + let line = format!("Team slug is required. Example: tower teams {} ", subcmd); + output::die(&line); +} diff --git a/crates/tower-cmd/src/util/apps.rs b/crates/tower-cmd/src/util/apps.rs index 4ab99a8e..2090f536 100644 --- a/crates/tower-cmd/src/util/apps.rs +++ b/crates/tower-cmd/src/util/apps.rs @@ -83,6 +83,7 @@ pub async fn ensure_app_exists( // Convert any creation error to a response error Err(tower_api::apis::Error::ResponseError( tower_api::apis::ResponseContent { + tower_trace_id: "".to_string(), status: match &create_err { tower_api::apis::Error::ResponseError(resp) => resp.status, _ => StatusCode::INTERNAL_SERVER_ERROR, diff --git a/crates/tower-cmd/src/util/cmd.rs b/crates/tower-cmd/src/util/cmd.rs new file mode 100644 index 00000000..49148410 --- /dev/null +++ b/crates/tower-cmd/src/util/cmd.rs @@ -0,0 +1,18 @@ +use crate::output; +use clap::ArgMatches; + +pub fn get_string_flag(args: &ArgMatches, name: &str) -> String { + args.get_one::(name) + .unwrap_or_else(|| { + output::die(&format!("{} is required", name)); + }) + .to_string() +} + +pub fn get_bool_flag(args: &ArgMatches, name: &str) -> bool { + args.get_one::(name) + .unwrap_or_else(|| { + output::die(&format!("{} is required", name)); + }) + .to_owned() +} diff --git a/crates/tower-cmd/src/util/deploy.rs b/crates/tower-cmd/src/util/deploy.rs index 073da529..bd5a5353 100644 --- a/crates/tower-cmd/src/util/deploy.rs +++ b/crates/tower-cmd/src/util/deploy.rs @@ -53,10 +53,18 @@ pub async fn upload_file_with_progress( // NOTE: I just kind of lifted this out of the generated client to figure out how to // deserialize this type into something that is useful and resembles the original/generated // stuff. + let tower_trace_id = response + .headers() + .get("x-tower-trace-id") + .and_then(|v| v.to_str().ok()) + .unwrap_or("") + .to_string(); + let status = response.status(); let content = response.text().await?; let entity: Option = serde_json::from_str(&content).ok(); - Err(Error::ResponseError(ResponseContent { status, content, entity })) + + Err(Error::ResponseError(ResponseContent { tower_trace_id, status, content, entity })) } } diff --git a/crates/tower-cmd/src/util/mod.rs b/crates/tower-cmd/src/util/mod.rs index 02520da4..23a6bb3f 100644 --- a/crates/tower-cmd/src/util/mod.rs +++ b/crates/tower-cmd/src/util/mod.rs @@ -1,3 +1,4 @@ pub mod apps; pub mod deploy; pub mod progress; +pub mod cmd; diff --git a/crates/tower-package/Cargo.toml b/crates/tower-package/Cargo.toml index 9b0f1d21..e052d468 100644 --- a/crates/tower-package/Cargo.toml +++ b/crates/tower-package/Cargo.toml @@ -16,8 +16,9 @@ serde_json = { workspace = true } snafu = { workspace = true } tmpdir = { workspace = true } tokio = { workspace = true } -tokio-tar = { workspace = true } tokio-stream = { workspace = true } +tokio-tar = { workspace = true } +tokio-util = { workspace = true } [dev-dependencies] env_logger = { workspace = true } diff --git a/crates/tower-package/src/lib.rs b/crates/tower-package/src/lib.rs index 540a0609..ee4c7513 100644 --- a/crates/tower-package/src/lib.rs +++ b/crates/tower-package/src/lib.rs @@ -1,8 +1,9 @@ use serde::{Deserialize, Serialize}; use std::path::{Path, PathBuf}; +use std::pin::Pin; use tokio::{ fs::File, - io::{AsyncReadExt, AsyncWriteExt}, + io::{AsyncRead, BufReader, AsyncReadExt, AsyncWriteExt}, }; use config::Towerfile; use tokio_tar::{Archive, Builder}; @@ -10,6 +11,7 @@ use glob::glob; use tmpdir::TmpDir; use async_compression::tokio::write::GzipEncoder; +use async_compression::tokio::bufread::GzipDecoder; mod error; pub use error::Error; @@ -242,10 +244,9 @@ impl Package { path }; - let file = File::open(self.package_file_path.clone().unwrap()).await?; - let mut archive = Archive::new(file); - - archive.unpack(path.clone()).await?; + // self.package_file_path should be set otherwise this is a bug. + let package_path = self.package_file_path.clone().unwrap(); + unpack_archive(&package_path, &path).await?; self.unpacked_path = Some(path); Ok(()) } @@ -280,3 +281,42 @@ fn extract_glob_path(path: PathBuf) -> String { str.to_string() } } + +/// Check if a file is a valid gzip file by attempting to decompress it +async fn is_valid_gzip>(path: P) -> bool { + let file = match File::open(&path).await { + Ok(file) => file, + Err(_) => return false, + }; + + let reader = BufReader::new(file); + let mut decoder = GzipDecoder::new(reader); + + // Try to read a small amount of data. If we can, then we assume that it's a valid gzip file. + // Othwewise, it's not gzipped I suppose? + let mut buffer = [0u8; 1024]; + match decoder.read(&mut buffer).await { + Ok(_) => true, + Err(_) => false, + } +} + +async fn unpack_archive>(package_path: P, output_path: P) -> Result<(), std::io::Error> { + let reader: Pin> = if is_valid_gzip(&package_path).await { + // gor gzipped files + let file = File::open(&package_path).await?; + let buf_reader = BufReader::new(file); + let decoder = GzipDecoder::new(buf_reader); + Box::pin(decoder) + } else { + // For regular files + let file = File::open(&package_path).await?; + Box::pin(file) + }; + + // Create and unpack the archive + let mut archive = Archive::new(reader); + archive.unpack(output_path).await?; + + Ok(()) +} diff --git a/openapi/generate_tower_client.sh b/openapi/generate_tower_client.sh deleted file mode 100755 index 3cb54260..00000000 --- a/openapi/generate_tower_client.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash - -echo "Downloading OpenAPI specification..." && \ -curl -s -O https://api.tower.dev/v1/openapi.json && \ - -echo "Generating API code..." && \ -podman run --rm \ --v ${PWD}:/local openapitools/openapi-generator-cli generate \ --c /local/generator_config.yaml && \ - -echo "Copying generated files to target directory..." && \ -rsync -avm --delete --exclude={'.gitignore','.openapi*','.travis.yml','*.sh'} tower-api/ ../crates/tower-api/ && \ - -echo "Cleaning up..." && \ -rm -r openapi.json tower-api diff --git a/pyproject.toml b/pyproject.toml index a76d8a0b..90b38580 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "maturin" [project] name = "tower" -version = "0.3.10" +version = "0.3.11" description = "Tower CLI and runtime environment for Tower." authors = [{ name = "Tower Computing Inc.", email = "brad@tower.dev" }] readme = "README.md" diff --git a/scripts/generate-rust-api-client.sh b/scripts/generate-rust-api-client.sh new file mode 100755 index 00000000..c1c196ac --- /dev/null +++ b/scripts/generate-rust-api-client.sh @@ -0,0 +1,112 @@ +#!/bin/bash +#### +# +# generate-rust-api-client.sh +# +# This script uses openapi-generator-cli to generate a Rust API client from an +# OpenAPI specification. The unfortunate truth is that this project is authored +# in Java, so to run this script you need all the Java infrastructure on your +# development machine. There's instructions in README.md on how to set this up. +# We've lifted this script from the openapi-generator project's repository. +# +# The below variables are used in small modifications that we've made from the +# source script to make this work for our specific usecase. +# +### + +# TOWER_URL is the URL of the API that we want to generate the client from. +TOWER_URL="https://api.tower.dev" + +# BASEDIR is the directory to change into before running the generator. +BASEDIR=$(dirname "$0") + +# CONFIG_FILE is the config file for the generator. Should be in the same directory as BASEDIR. +CONFIG_FILE="rust-api-client-generator-config.yaml" + +# ARGS are the args sent to the generator. +ARGS="generate --config ${CONFIG_FILE}" + +# OPENAPI_GENERATOR_VERSION is the version of the openapi-generator-cli to use. +OPENAPI_GENERATOR_VERSION="7.13.0" + +# We need to get the OpenAPI spec file into scope in the first place. +curl -sL ${TOWER_URL}/v1/openapi.json -o ${BASEDIR}/openapi.json + +### +# +# Start of the opernapi-generator-cli script +# +# Source: https://raw.githubusercontent.com/OpenAPITools/openapi-generator/master/bin/utils/openapi-generator-cli.sh +# +### + +#### +# Save as openapi-generator-cli on your PATH. chmod u+x. Enjoy. +# +# This script will query github on every invocation to pull the latest released version +# of openapi-generator. +# +# If you want repeatable executions, you can explicitly set a version via +# OPENAPI_GENERATOR_VERSION +# e.g. (in Bash) +# export OPENAPI_GENERATOR_VERSION=3.1.0 +# openapi-generator-cli.sh +# or +# OPENAPI_GENERATOR_VERSION=3.1.0 openapi-generator-cli.sh +# +# This is also helpful, for example, if you want to evaluate a SNAPSHOT version. +# +# NOTE: Jars are downloaded on demand from maven into the same directory as this script +# for every 'latest' version pulled from github. Consider putting this under its own directory. +#### +set -o pipefail + +for cmd in {mvn,jq,curl}; do + if ! command -v ${cmd} > /dev/null; then + >&2 echo "This script requires '${cmd}' to be installed." + exit 1 + fi +done + +function latest.tag { + local uri="https://api.github.com/repos/${1}/releases" + local ver=$(curl -s ${uri} | jq -r 'first(.[]|select(.prerelease==false)).tag_name') + if [[ $ver == v* ]]; then + ver=${ver:1} + fi + echo $ver +} + +ghrepo=openapitools/openapi-generator +groupid=org.openapitools +artifactid=openapi-generator-cli +ver=${OPENAPI_GENERATOR_VERSION:-$(latest.tag $ghrepo)} + +jar=${artifactid}-${ver}.jar +cachedir=${OPENAPI_GENERATOR_DOWNLOAD_CACHE_DIR} + +DIR=${cachedir:-"$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"} + +if [ ! -d "${DIR}" ]; then + mkdir -p "${DIR}" +fi + +if [ ! -f ${DIR}/${jar} ]; then + repo="central::default::https://repo1.maven.org/maven2/" + if [[ ${ver} =~ ^.*-SNAPSHOT$ ]]; then + repo="central::default::https://oss.sonatype.org/content/repositories/snapshots" + fi + mvn org.apache.maven.plugins:maven-dependency-plugin:2.9:get \ + -DremoteRepositories=${repo} \ + -Dartifact=${groupid}:${artifactid}:${ver} \ + -Dtransitive=false \ + -Ddest=${DIR}/${jar} +fi + +cd $BASEDIR + +java -ea \ + ${JAVA_OPTS} \ + -Xms512M \ + -Xmx1024M \ + -jar ${DIR}/${jar} ${ARGS} diff --git a/openapi/generator_config.yaml b/scripts/rust-api-client-generator-config.yaml similarity index 79% rename from openapi/generator_config.yaml rename to scripts/rust-api-client-generator-config.yaml index a521c945..703d3f05 100644 --- a/openapi/generator_config.yaml +++ b/scripts/rust-api-client-generator-config.yaml @@ -1,8 +1,9 @@ # Generator Configuration generatorName: rust -inputSpec: /local/openapi.json -outputDir: /local/tower-api -httpUserAgent: "tower_cli_client" +inputSpec: openapi.json +outputDir: ../crates/tower-api +httpUserAgent: "Tower CLI" +templateDir: rust-client-templates nameMappings: $schema: schema diff --git a/scripts/rust-client-templates/.travis.yml b/scripts/rust-client-templates/.travis.yml new file mode 100644 index 00000000..22761ba7 --- /dev/null +++ b/scripts/rust-client-templates/.travis.yml @@ -0,0 +1 @@ +language: rust diff --git a/scripts/rust-client-templates/Cargo.mustache b/scripts/rust-client-templates/Cargo.mustache new file mode 100644 index 00000000..2e21ce53 --- /dev/null +++ b/scripts/rust-client-templates/Cargo.mustache @@ -0,0 +1,110 @@ +[package] +name = "{{{packageName}}}" +version = "{{#lambdaVersion}}{{{packageVersion}}}{{/lambdaVersion}}" +{{#infoEmail}} +authors = ["{{{.}}}"] +{{/infoEmail}} +{{^infoEmail}} +authors = ["OpenAPI Generator team and contributors"] +{{/infoEmail}} +{{#appDescription}} +description = "{{{.}}}" +{{/appDescription}} +{{#licenseInfo}} +license = "{{.}}" +{{/licenseInfo}} +{{^licenseInfo}} +# Override this license by providing a License Object in the OpenAPI. +license = "Unlicense" +{{/licenseInfo}} +edition = "2021" +{{#publishRustRegistry}} +publish = ["{{.}}"] +{{/publishRustRegistry}} +{{#repositoryUrl}} +repository = "{{.}}" +{{/repositoryUrl}} +{{#documentationUrl}} +documentation = "{{.}}" +{{/documentationUrl}} +{{#homePageUrl}} +homepage = "{{.}}" +{{/homePageUrl}} + +[dependencies] +log = { workspace = true } +serde = { version = "^1.0", features = ["derive"] } +{{#serdeWith}} +serde_with = { version = "^3.8", default-features = false, features = ["base64", "std", "macros"] } +{{/serdeWith}} +serde_json = "^1.0" +serde_repr = "^0.1" +url = "^2.5" +{{#hasUUIDs}} +uuid = { version = "^1.8", features = ["serde", "v4"] } +{{/hasUUIDs}} +{{#hyper}} +{{#hyper0x}} +hyper = { version = "~0.14", features = ["full"] } +hyper-tls = "~0.5" +{{/hyper0x}} +{{^hyper0x}} +hyper = { version = "^1.3.1", features = ["full"] } +hyper-util = { version = "0.1.5", features = ["client", "client-legacy", "http1", "http2"] } +http-body-util = { version = "0.1.2" } +{{/hyper0x}} +http = "~0.2" +base64 = "~0.7.0" +futures = "^0.3" +{{/hyper}} +{{#withAWSV4Signature}} +aws-sigv4 = "0.3.0" +http = "0.2.5" +secrecy = "0.8.0" +{{/withAWSV4Signature}} +{{#reqwest}} +{{^supportAsync}} +reqwest = { version = "^0.12", default-features = false, features = ["json", "blocking", "multipart"] } +{{#supportMiddleware}} +reqwest-middleware = { version = "^0.4", features = ["json", "blocking", "multipart"] } +{{/supportMiddleware}} +{{/supportAsync}} +{{#supportAsync}} +reqwest = { version = "^0.12", default-features = false, features = ["json", "multipart"] } +{{#supportMiddleware}} +reqwest-middleware = { version = "^0.4", features = ["json", "multipart"] } +{{/supportMiddleware}} +{{#supportTokenSource}} +async-trait = "^0.1" +# TODO: propose to Yoshidan to externalize this as non google related crate, so that it can easily be extended for other cloud providers. +google-cloud-token = "^0.1" +{{/supportTokenSource}} +{{/supportAsync}} +{{/reqwest}} +{{#reqwestTrait}} +async-trait = "^0.1" +reqwest = { version = "^0.12", default-features = false, features = ["json", "multipart"] } +{{#supportMiddleware}} +reqwest-middleware = { version = "^0.4", features = ["json", "multipart"] } +{{/supportMiddleware}} +{{#supportTokenSource}} +# TODO: propose to Yoshidan to externalize this as non google related crate, so that it can easily be extended for other cloud providers. +google-cloud-token = "^0.1" +{{/supportTokenSource}} +{{#mockall}} +mockall = { version = "^0.13", optional = true} +{{/mockall}} +{{#useBonBuilder}} +bon = { version = "2.3", optional = true } +{{/useBonBuilder}} +[features] +default = ["native-tls"] +native-tls = ["reqwest/native-tls"] +rustls = ["reqwest/rustls-tls"] +{{#mockall}} +mockall = ["dep:mockall"] +{{/mockall}} +{{#useBonBuilder}} +bon = ["dep:bon"] +{{/useBonBuilder}} +{{/reqwestTrait}} diff --git a/scripts/rust-client-templates/README.mustache b/scripts/rust-client-templates/README.mustache new file mode 100644 index 00000000..0e8bfb37 --- /dev/null +++ b/scripts/rust-client-templates/README.mustache @@ -0,0 +1,54 @@ +# Rust API client for {{{packageName}}} + +{{#appDescriptionWithNewLines}} +{{{.}}} +{{/appDescriptionWithNewLines}} + +{{#infoUrl}} +For more information, please visit [{{{infoUrl}}}]({{{infoUrl}}}) +{{/infoUrl}} + +## Overview + +This API client was generated by the [OpenAPI Generator](https://openapi-generator.tech) project. By using the [openapi-spec](https://openapis.org) from a remote server, you can easily generate an API client. + +- API version: {{{appVersion}}} +- Package version: {{{packageVersion}}} +{{^hideGenerationTimestamp}} +- Build date: {{{generatedDate}}} +{{/hideGenerationTimestamp}} +- Generator version: {{generatorVersion}} +- Build package: `{{{generatorClass}}}` + +## Installation + +Put the package under your project folder in a directory named `{{packageName}}` and add the following to `Cargo.toml` under `[dependencies]`: + +``` +{{{packageName}}} = { path = "./{{{packageName}}}" } +``` + +## Documentation for API Endpoints + +All URIs are relative to *{{{basePath}}}* + +Class | Method | HTTP request | Description +------------ | ------------- | ------------- | ------------- +{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}*{{{classname}}}* | [**{{{operationId}}}**]({{{apiDocPath}}}{{classname}}.md#{{{operationIdLowerCase}}}) | **{{{httpMethod}}}** {{{path}}} | {{{summary}}} +{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}} + +## Documentation For Models + +{{#models}}{{#model}} - [{{{classname}}}]({{{modelDocPath}}}{{{classname}}}.md) +{{/model}}{{/models}} + +To get access to the crate's generated documentation, use: + +``` +cargo doc --open +``` + +## Author + +{{#apiInfo}}{{#apis}}{{#-last}}{{{infoEmail}}} +{{/-last}}{{/apis}}{{/apiInfo}} diff --git a/scripts/rust-client-templates/api_doc.mustache b/scripts/rust-client-templates/api_doc.mustache new file mode 100644 index 00000000..f8ea5203 --- /dev/null +++ b/scripts/rust-client-templates/api_doc.mustache @@ -0,0 +1,47 @@ +# {{{invokerPackage}}}\{{{classname}}}{{#description}} + +{{{.}}}{{/description}} + +All URIs are relative to *{{{basePath}}}* + +Method | HTTP request | Description +------------- | ------------- | ------------- +{{#operations}}{{#operation}}[**{{{operationId}}}**]({{{classname}}}.md#{{{operationId}}}) | **{{{httpMethod}}}** {{{path}}} | {{{summary}}} +{{/operation}}{{/operations}} + +{{#operations}} +{{#operation}} + +## {{{operationId}}} + +> {{#returnType}}{{{.}}} {{/returnType}}{{{operationId}}}({{#allParams}}{{{paramName}}}{{^-last}}, {{/-last}}{{/allParams}}) +{{{summary}}}{{#notes}} + +{{{.}}}{{/notes}} + +### Parameters + +{{^allParams}}This endpoint does not need any parameter.{{/allParams}}{{#allParams}}{{#-last}} +Name | Type | Description | Required | Notes +------------- | ------------- | ------------- | ------------- | -------------{{/-last}}{{/allParams}} +{{#allParams}} +**{{{paramName}}}** | {{^required}}Option<{{/required}}{{#required}}{{#isNullable}}Option<{{/isNullable}}{{/required}}{{#isPrimitiveType}}**{{{dataType}}}**{{/isPrimitiveType}}{{^isPrimitiveType}}[**{{{dataType}}}**]({{{baseType}}}.md){{/isPrimitiveType}}{{^required}}>{{/required}}{{#required}}{{#isNullable}}>{{/isNullable}}{{/required}} | {{{description}}} | {{#required}}[required]{{/required}} |{{#defaultValue}}[default to {{{.}}}]{{/defaultValue}} +{{/allParams}} + +### Return type + +{{#returnType}}{{#returnTypeIsPrimitive}}**{{{returnType}}}**{{/returnTypeIsPrimitive}}{{^returnTypeIsPrimitive}}[**{{{returnType}}}**]({{{returnBaseType}}}.md){{/returnTypeIsPrimitive}}{{/returnType}}{{^returnType}} (empty response body){{/returnType}} + +### Authorization + +{{^authMethods}}No authorization required{{/authMethods}}{{#authMethods}}[{{{name}}}](../README.md#{{{name}}}){{^-last}}, {{/-last}}{{/authMethods}} + +### HTTP request headers + +- **Content-Type**: {{#consumes}}{{{mediaType}}}{{^-last}}, {{/-last}}{{/consumes}}{{^consumes}}Not defined{{/consumes}} +- **Accept**: {{#produces}}{{{mediaType}}}{{^-last}}, {{/-last}}{{/produces}}{{^produces}}Not defined{{/produces}} + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +{{/operation}} +{{/operations}} diff --git a/scripts/rust-client-templates/git_push.sh.mustache b/scripts/rust-client-templates/git_push.sh.mustache new file mode 100644 index 00000000..0e3776ae --- /dev/null +++ b/scripts/rust-client-templates/git_push.sh.mustache @@ -0,0 +1,57 @@ +#!/bin/sh +# ref: https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/ +# +# Usage example: /bin/sh ./git_push.sh wing328 openapi-petstore-perl "minor update" "gitlab.com" + +git_user_id=$1 +git_repo_id=$2 +release_note=$3 +git_host=$4 + +if [ "$git_host" = "" ]; then + git_host="{{{gitHost}}}" + echo "[INFO] No command line input provided. Set \$git_host to $git_host" +fi + +if [ "$git_user_id" = "" ]; then + git_user_id="{{{gitUserId}}}" + echo "[INFO] No command line input provided. Set \$git_user_id to $git_user_id" +fi + +if [ "$git_repo_id" = "" ]; then + git_repo_id="{{{gitRepoId}}}" + echo "[INFO] No command line input provided. Set \$git_repo_id to $git_repo_id" +fi + +if [ "$release_note" = "" ]; then + release_note="{{{releaseNote}}}" + echo "[INFO] No command line input provided. Set \$release_note to $release_note" +fi + +# Initialize the local directory as a Git repository +git init + +# Adds the files in the local repository and stages them for commit. +git add . + +# Commits the tracked changes and prepares them to be pushed to a remote repository. +git commit -m "$release_note" + +# Sets the new remote +git_remote=$(git remote) +if [ "$git_remote" = "" ]; then # git remote not defined + + if [ "$GIT_TOKEN" = "" ]; then + echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." + git remote add origin https://${git_host}/${git_user_id}/${git_repo_id}.git + else + git remote add origin https://${git_user_id}:"${GIT_TOKEN}"@${git_host}/${git_user_id}/${git_repo_id}.git + fi + +fi + +git pull origin master + +# Pushes (Forces) the changes in the local repository up to the remote repository +echo "Git pushing to https://${git_host}/${git_user_id}/${git_repo_id}.git" +git push origin master 2>&1 | grep -v 'To https' diff --git a/scripts/rust-client-templates/gitignore.mustache b/scripts/rust-client-templates/gitignore.mustache new file mode 100644 index 00000000..6aa10640 --- /dev/null +++ b/scripts/rust-client-templates/gitignore.mustache @@ -0,0 +1,3 @@ +/target/ +**/*.rs.bk +Cargo.lock diff --git a/scripts/rust-client-templates/hyper/api.mustache b/scripts/rust-client-templates/hyper/api.mustache new file mode 100644 index 00000000..ad0410bc --- /dev/null +++ b/scripts/rust-client-templates/hyper/api.mustache @@ -0,0 +1,174 @@ +{{>partial_header}} +use std::sync::Arc; +use std::borrow::Borrow; +use std::pin::Pin; +#[allow(unused_imports)] +use std::option::Option; + +use hyper; +use hyper_util::client::legacy::connect::Connect; +use futures::Future; + +use crate::models; +use super::{Error, configuration}; +use super::request as __internal_request; + +pub struct {{{classname}}}Client + where C: Clone + std::marker::Send + Sync + 'static { + configuration: Arc>, +} + +impl {{{classname}}}Client + where C: Clone + std::marker::Send + Sync { + pub fn new(configuration: Arc>) -> {{{classname}}}Client { + {{{classname}}}Client { + configuration, + } + } +} + +pub trait {{{classname}}}: Send + Sync { +{{#operations}} +{{#operation}} + fn {{{operationId}}}(&self, {{#allParams}}{{{paramName}}}: {{^required}}Option<{{/required}}{{#required}}{{#isNullable}}Option<{{/isNullable}}{{/required}}{{#isString}}{{^isUuid}}&str{{/isUuid}}{{/isString}}{{#isUuid}}&str{{/isUuid}}{{^isString}}{{^isUuid}}{{^isPrimitiveType}}{{^isContainer}}models::{{/isContainer}}{{/isPrimitiveType}}{{{dataType}}}{{/isUuid}}{{/isString}}{{^required}}>{{/required}}{{#required}}{{#isNullable}}>{{/isNullable}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}}) -> Pin> + Send>>; +{{/operation}} +{{/operations}} +} + +impl{{{classname}}} for {{{classname}}}Client + where C: Clone + std::marker::Send + Sync { + {{#operations}} + {{#operation}} + #[allow(unused_mut)] + fn {{{operationId}}}(&self, {{#allParams}}{{{paramName}}}: {{^required}}Option<{{/required}}{{#required}}{{#isNullable}}Option<{{/isNullable}}{{/required}}{{#isString}}{{^isUuid}}&str{{/isUuid}}{{/isString}}{{#isUuid}}&str{{/isUuid}}{{^isString}}{{^isUuid}}{{^isPrimitiveType}}{{^isContainer}}models::{{/isContainer}}{{/isPrimitiveType}}{{{dataType}}}{{/isUuid}}{{/isString}}{{^required}}>{{/required}}{{#required}}{{#isNullable}}>{{/isNullable}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}}) -> Pin> + Send>> { + let mut req = __internal_request::Request::new(hyper::Method::{{{httpMethod.toUpperCase}}}, "{{{path}}}".to_string()) + {{#hasAuthMethods}} + {{#authMethods}} + {{#isApiKey}} + .with_auth(__internal_request::Auth::ApiKey(__internal_request::ApiKey{ + in_header: {{#isKeyInHeader}}true{{/isKeyInHeader}}{{^isKeyInHeader}}false{{/isKeyInHeader}}, + in_query: {{#isKeyInQuery}}true{{/isKeyInQuery}}{{^isKeyInQuery}}false{{/isKeyInQuery}}, + param_name: "{{{keyParamName}}}".to_owned(), + })) + {{/isApiKey}} + {{#isBasicBasic}} + .with_auth(__internal_request::Auth::Basic) + {{/isBasicBasic}} + {{#isOAuth}} + .with_auth(__internal_request::Auth::Oauth) + {{/isOAuth}} + {{/authMethods}} + {{/hasAuthMethods}} + ; + {{#queryParams}} + {{#required}} + {{^isNullable}} + req = req.with_query_param("{{{baseName}}}".to_string(), {{{paramName}}}{{#isArray}}.join(","){{/isArray}}.to_string()); + {{/isNullable}} + {{#isNullable}} + match {{{paramName}}} { + Some(param_value) => { req = req.with_query_param("{{{baseName}}}".to_string(), param_value{{#isArray}}.join(","){{/isArray}}.to_string()); }, + None => { req = req.with_query_param("{{{baseName}}}".to_string(), "".to_string()); }, + } + {{/isNullable}} + {{/required}} + {{^required}} + if let Some(ref s) = {{{paramName}}} { + let query_value = {{#isArray}}s.iter().map(|s| s.to_string()).collect::>().join(","){{/isArray}}{{^isArray}}s.to_string(){{/isArray}}; + req = req.with_query_param("{{{baseName}}}".to_string(), query_value); + } + {{/required}} + {{/queryParams}} + {{#pathParams}} + {{#required}} + {{^isNullable}} + req = req.with_path_param("{{{baseName}}}".to_string(), {{{paramName}}}{{#isArray}}.join(","){{/isArray}}.to_string()); + {{/isNullable}} + {{#isNullable}} + match {{{paramName}}} { + Some(param_value) => { req = req.with_path_param("{{{baseName}}}".to_string(), param_value{{#isArray}}.join(","){{/isArray}}.to_string()); }, + None => { req = req.with_path_param("{{{baseName}}}".to_string(), "".to_string()); }, + } + {{/isNullable}} + {{/required}} + {{^required}} + if let Some(param_value) = {{{paramName}}} { + req = req.with_path_param("{{{baseName}}}".to_string(), param_value{{#isArray}}.join(","){{/isArray}}.to_string()); + } + {{/required}} + {{/pathParams}} + {{#hasHeaderParams}} + {{#headerParams}} + {{#required}} + {{^isNullable}} + req = req.with_header_param("{{{baseName}}}".to_string(), {{{paramName}}}{{#isArray}}.join(","){{/isArray}}.to_string()); + {{/isNullable}} + {{#isNullable}} + match {{{paramName}}} { + Some(param_value) => { req = req.with_header_param("{{{baseName}}}".to_string(), param_value{{#isArray}}.join(","){{/isArray}}.to_string()); }, + None => { req = req.with_header_param("{{{baseName}}}".to_string(), "".to_string()); }, + } + {{/isNullable}} + {{/required}} + {{^required}} + if let Some(param_value) = {{{paramName}}} { + req = req.with_header_param("{{{baseName}}}".to_string(), param_value{{#isArray}}.join(","){{/isArray}}.to_string()); + } + {{/required}} + {{/headerParams}} + {{/hasHeaderParams}} + {{#hasFormParams}} + {{#formParams}} + {{#isFile}} + {{#required}} + {{^isNullable}} + req = req.with_form_param("{{{baseName}}}".to_string(), unimplemented!()); + {{/isNullable}} + {{#isNullable}} + match {{{paramName}}} { + Some(param_value) => { req = req.with_form_param("{{{baseName}}}".to_string(), unimplemented!()); }, + None => { req = req.with_form_param("{{{baseName}}}".to_string(), unimplemented!()); }, + } + {{/isNullable}} + {{/required}} + {{^required}} + if let Some(param_value) = {{{paramName}}} { + req = req.with_form_param("{{{baseName}}}".to_string(), unimplemented!()); + } + {{/required}} + {{/isFile}} + {{^isFile}} + {{#required}} + {{^isNullable}} + req = req.with_form_param("{{{baseName}}}".to_string(), {{{paramName}}}{{#isArray}}.join(","){{/isArray}}.to_string()); + {{/isNullable}} + {{#isNullable}} + match {{{paramName}}} { + Some(param_value) => { req = req.with_form_param("{{{baseName}}}".to_string(), param_value{{#isArray}}.join(","){{/isArray}}.to_string()); }, + None => { req = req.with_form_param("{{{baseName}}}".to_string(), "".to_string()); }, + } + {{/isNullable}} + {{/required}} + {{^required}} + if let Some(param_value) = {{{paramName}}} { + req = req.with_form_param("{{{baseName}}}".to_string(), param_value{{#isArray}}.join(","){{/isArray}}.to_string()); + } + {{/required}} + {{/isFile}} + {{/formParams}} + {{/hasFormParams}} + {{#hasBodyParam}} + {{#bodyParams}} + req = req.with_body_param({{{paramName}}}); + {{/bodyParams}} + {{/hasBodyParam}} + {{^returnType}} + req = req.returns_nothing(); + {{/returnType}} + + req.execute(self.configuration.borrow()) + } + +{{/operation}} +{{/operations}} +} diff --git a/scripts/rust-client-templates/hyper/api_mod.mustache b/scripts/rust-client-templates/hyper/api_mod.mustache new file mode 100644 index 00000000..67baa441 --- /dev/null +++ b/scripts/rust-client-templates/hyper/api_mod.mustache @@ -0,0 +1,83 @@ +use std::fmt; +use std::fmt::Debug; + +use hyper; +use hyper::http; +use hyper_util::client::legacy::connect::Connect; +use serde_json; + +#[derive(Debug)] +pub enum Error { + Api(ApiError), + Header(http::header::InvalidHeaderValue), + Http(http::Error), + Hyper(hyper::Error), + HyperClient(hyper_util::client::legacy::Error), + Serde(serde_json::Error), + UriError(http::uri::InvalidUri), +} + +pub struct ApiError { + pub code: hyper::StatusCode, + pub body: hyper::body::Incoming, +} + +impl Debug for ApiError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ApiError") + .field("code", &self.code) + .field("body", &"hyper::body::Incoming") + .finish() + } +} + +impl From<(hyper::StatusCode, hyper::body::Incoming)> for Error { + fn from(e: (hyper::StatusCode, hyper::body::Incoming)) -> Self { + Error::Api(ApiError { + code: e.0, + body: e.1, + }) + } +} + +impl From for Error { + fn from(e: http::Error) -> Self { + Error::Http(e) + } +} + +impl From for Error { + fn from(e: hyper_util::client::legacy::Error) -> Self { + Error::HyperClient(e) + } +} + +impl From for Error { + fn from(e: hyper::Error) -> Self { + Error::Hyper(e) + } +} + +impl From for Error { + fn from(e: serde_json::Error) -> Self { + Error::Serde(e) + } +} + +mod request; + +{{#apiInfo}} +{{#apis}} +mod {{{classFilename}}}; +{{#operations}} +{{#operation}} +{{#-last}} +pub use self::{{{classFilename}}}::{ {{{classname}}}, {{{classname}}}Client }; +{{/-last}} +{{/operation}} +{{/operations}} +{{/apis}} +{{/apiInfo}} + +pub mod configuration; +pub mod client; diff --git a/scripts/rust-client-templates/hyper/client.mustache b/scripts/rust-client-templates/hyper/client.mustache new file mode 100644 index 00000000..d54e3348 --- /dev/null +++ b/scripts/rust-client-templates/hyper/client.mustache @@ -0,0 +1,55 @@ +use std::sync::Arc; + +use hyper; +use hyper_util::client::legacy::connect::Connect; +use super::configuration::Configuration; + +pub struct APIClient { +{{#apiInfo}} +{{#apis}} +{{#operations}} +{{#operation}} + {{#-last}} + {{{classFilename}}}: Box, + {{/-last}} +{{/operation}} +{{/operations}} +{{/apis}} +{{/apiInfo}} +} + +impl APIClient { + pub fn new(configuration: Configuration) -> APIClient + where C: Clone + std::marker::Send + Sync + 'static { + let rc = Arc::new(configuration); + + APIClient { +{{#apiInfo}} +{{#apis}} +{{#operations}} +{{#operation}} + {{#-last}} + {{{classFilename}}}: Box::new(crate::apis::{{{classname}}}Client::new(rc.clone())), + {{/-last}} +{{/operation}} +{{/operations}} +{{/apis}} +{{/apiInfo}} + } + } + +{{#apiInfo}} +{{#apis}} +{{#operations}} +{{#operation}} +{{#-last}} + pub fn {{{classFilename}}}(&self) -> &dyn crate::apis::{{{classname}}}{ + self.{{{classFilename}}}.as_ref() + } + +{{/-last}} +{{/operation}} +{{/operations}} +{{/apis}} +{{/apiInfo}} +} diff --git a/scripts/rust-client-templates/hyper/configuration.mustache b/scripts/rust-client-templates/hyper/configuration.mustache new file mode 100644 index 00000000..43a48ff2 --- /dev/null +++ b/scripts/rust-client-templates/hyper/configuration.mustache @@ -0,0 +1,83 @@ +{{>partial_header}} +use hyper; +use hyper_util::client::legacy::Client; +use hyper_util::client::legacy::connect::Connect; +use hyper_util::client::legacy::connect::HttpConnector; +use hyper_util::rt::TokioExecutor; + +pub struct Configuration + where C: Clone + std::marker::Send + Sync + 'static { + pub base_path: String, + pub user_agent: Option, + pub client: Client, + pub basic_auth: Option, + pub oauth_access_token: Option, + pub api_key: Option, + // TODO: take an oauth2 token source, similar to the go one +} + +pub type BasicAuth = (String, Option); + +pub struct ApiKey { + pub prefix: Option, + pub key: String, +} + +impl Configuration { + /// Construct a default [`Configuration`](Self) with a hyper client using a default + /// [`HttpConnector`](hyper_util::client::legacy::connect::HttpConnector). + /// + /// Use [`with_client`](Configuration::with_client) to construct a Configuration with a + /// custom hyper client. + /// + /// # Example + /// + /// ``` + /// # use {{externCrateName}}::apis::configuration::Configuration; + /// let api_config = Configuration { + /// basic_auth: Some(("user".into(), None)), + /// ..Configuration::new() + /// }; + /// ``` + pub fn new() -> Configuration { + Configuration::default() + } +} + +impl Configuration + where C: Clone + std::marker::Send + Sync { + + /// Construct a new Configuration with a custom hyper client. + /// + /// # Example + /// + /// ``` + /// # use core::time::Duration; + /// # use {{externCrateName}}::apis::configuration::Configuration; + /// use hyper_util::client::legacy::Client; + /// use hyper_util::rt::TokioExecutor; + /// + /// let client = Client::builder(TokioExecutor::new()) + /// .pool_idle_timeout(Duration::from_secs(30)) + /// .build_http(); + /// + /// let api_config = Configuration::with_client(client); + /// ``` + pub fn with_client(client: Client) -> Configuration { + Configuration { + base_path: "{{{basePath}}}".to_owned(), + user_agent: {{#httpUserAgent}}Some("{{{.}}}".to_owned()){{/httpUserAgent}}{{^httpUserAgent}}Some("OpenAPI-Generator/{{{version}}}/rust".to_owned()){{/httpUserAgent}}, + client, + basic_auth: None, + oauth_access_token: None, + api_key: None, + } + } +} + +impl Default for Configuration { + fn default() -> Self { + let client = Client::builder(TokioExecutor::new()).build_http(); + Configuration::with_client(client) + } +} diff --git a/scripts/rust-client-templates/hyper0x/api.mustache b/scripts/rust-client-templates/hyper0x/api.mustache new file mode 100644 index 00000000..f59705ea --- /dev/null +++ b/scripts/rust-client-templates/hyper0x/api.mustache @@ -0,0 +1,173 @@ +{{>partial_header}} +use std::rc::Rc; +use std::borrow::Borrow; +use std::pin::Pin; +#[allow(unused_imports)] +use std::option::Option; + +use hyper; +use futures::Future; + +use crate::models; +use super::{Error, configuration}; +use super::request as __internal_request; + +pub struct {{{classname}}}Client + where C: Clone + std::marker::Send + Sync + 'static { + configuration: Rc>, +} + +impl {{{classname}}}Client + where C: Clone + std::marker::Send + Sync { + pub fn new(configuration: Rc>) -> {{{classname}}}Client { + {{{classname}}}Client { + configuration, + } + } +} + +pub trait {{{classname}}} { +{{#operations}} +{{#operation}} + fn {{{operationId}}}(&self, {{#allParams}}{{{paramName}}}: {{^required}}Option<{{/required}}{{#required}}{{#isNullable}}Option<{{/isNullable}}{{/required}}{{#isString}}{{^isUuid}}&str{{/isUuid}}{{/isString}}{{#isUuid}}&str{{/isUuid}}{{^isString}}{{^isUuid}}{{^isPrimitiveType}}{{^isContainer}}models::{{/isContainer}}{{/isPrimitiveType}}{{{dataType}}}{{/isUuid}}{{/isString}}{{^required}}>{{/required}}{{#required}}{{#isNullable}}>{{/isNullable}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}}) -> Pin>>>; +{{/operation}} +{{/operations}} +} + +impl{{{classname}}} for {{{classname}}}Client + where C: Clone + std::marker::Send + Sync { + {{#operations}} + {{#operation}} + #[allow(unused_mut)] + fn {{{operationId}}}(&self, {{#allParams}}{{{paramName}}}: {{^required}}Option<{{/required}}{{#required}}{{#isNullable}}Option<{{/isNullable}}{{/required}}{{#isString}}{{^isUuid}}&str{{/isUuid}}{{/isString}}{{#isUuid}}&str{{/isUuid}}{{^isString}}{{^isUuid}}{{^isPrimitiveType}}{{^isContainer}}models::{{/isContainer}}{{/isPrimitiveType}}{{{dataType}}}{{/isUuid}}{{/isString}}{{^required}}>{{/required}}{{#required}}{{#isNullable}}>{{/isNullable}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}}) -> Pin>>> { + let mut req = __internal_request::Request::new(hyper::Method::{{{httpMethod.toUpperCase}}}, "{{{path}}}".to_string()) + {{#hasAuthMethods}} + {{#authMethods}} + {{#isApiKey}} + .with_auth(__internal_request::Auth::ApiKey(__internal_request::ApiKey{ + in_header: {{#isKeyInHeader}}true{{/isKeyInHeader}}{{^isKeyInHeader}}false{{/isKeyInHeader}}, + in_query: {{#isKeyInQuery}}true{{/isKeyInQuery}}{{^isKeyInQuery}}false{{/isKeyInQuery}}, + param_name: "{{{keyParamName}}}".to_owned(), + })) + {{/isApiKey}} + {{#isBasicBasic}} + .with_auth(__internal_request::Auth::Basic) + {{/isBasicBasic}} + {{#isOAuth}} + .with_auth(__internal_request::Auth::Oauth) + {{/isOAuth}} + {{/authMethods}} + {{/hasAuthMethods}} + ; + {{#queryParams}} + {{#required}} + {{^isNullable}} + req = req.with_query_param("{{{baseName}}}".to_string(), {{{paramName}}}{{#isArray}}.join(","){{/isArray}}.to_string()); + {{/isNullable}} + {{#isNullable}} + match {{{paramName}}} { + Some(param_value) => { req = req.with_query_param("{{{baseName}}}".to_string(), param_value{{#isArray}}.join(","){{/isArray}}.to_string()); }, + None => { req = req.with_query_param("{{{baseName}}}".to_string(), "".to_string()); }, + } + {{/isNullable}} + {{/required}} + {{^required}} + if let Some(ref s) = {{{paramName}}} { + let query_value = {{#isArray}}s.iter().map(|s| s.to_string()).collect::>().join(","){{/isArray}}{{^isArray}}s.to_string(){{/isArray}}; + req = req.with_query_param("{{{baseName}}}".to_string(), query_value); + } + {{/required}} + {{/queryParams}} + {{#pathParams}} + {{#required}} + {{^isNullable}} + req = req.with_path_param("{{{baseName}}}".to_string(), {{{paramName}}}{{#isArray}}.join(","){{/isArray}}.to_string()); + {{/isNullable}} + {{#isNullable}} + match {{{paramName}}} { + Some(param_value) => { req = req.with_path_param("{{{baseName}}}".to_string(), param_value{{#isArray}}.join(","){{/isArray}}.to_string()); }, + None => { req = req.with_path_param("{{{baseName}}}".to_string(), "".to_string()); }, + } + {{/isNullable}} + {{/required}} + {{^required}} + if let Some(param_value) = {{{paramName}}} { + req = req.with_path_param("{{{baseName}}}".to_string(), param_value{{#isArray}}.join(","){{/isArray}}.to_string()); + } + {{/required}} + {{/pathParams}} + {{#hasHeaderParams}} + {{#headerParams}} + {{#required}} + {{^isNullable}} + req = req.with_header_param("{{{baseName}}}".to_string(), {{{paramName}}}{{#isArray}}.join(","){{/isArray}}.to_string()); + {{/isNullable}} + {{#isNullable}} + match {{{paramName}}} { + Some(param_value) => { req = req.with_header_param("{{{baseName}}}".to_string(), param_value{{#isArray}}.join(","){{/isArray}}.to_string()); }, + None => { req = req.with_header_param("{{{baseName}}}".to_string(), "".to_string()); }, + } + {{/isNullable}} + {{/required}} + {{^required}} + if let Some(param_value) = {{{paramName}}} { + req = req.with_header_param("{{{baseName}}}".to_string(), param_value{{#isArray}}.join(","){{/isArray}}.to_string()); + } + {{/required}} + {{/headerParams}} + {{/hasHeaderParams}} + {{#hasFormParams}} + {{#formParams}} + {{#isFile}} + {{#required}} + {{^isNullable}} + req = req.with_form_param("{{{baseName}}}".to_string(), unimplemented!()); + {{/isNullable}} + {{#isNullable}} + match {{{paramName}}} { + Some(param_value) => { req = req.with_form_param("{{{baseName}}}".to_string(), unimplemented!()); }, + None => { req = req.with_form_param("{{{baseName}}}".to_string(), unimplemented!()); }, + } + {{/isNullable}} + {{/required}} + {{^required}} + if let Some(param_value) = {{{paramName}}} { + req = req.with_form_param("{{{baseName}}}".to_string(), unimplemented!()); + } + {{/required}} + {{/isFile}} + {{^isFile}} + {{#required}} + {{^isNullable}} + req = req.with_form_param("{{{baseName}}}".to_string(), {{{paramName}}}{{#isArray}}.join(","){{/isArray}}.to_string()); + {{/isNullable}} + {{#isNullable}} + match {{{paramName}}} { + Some(param_value) => { req = req.with_form_param("{{{baseName}}}".to_string(), param_value{{#isArray}}.join(","){{/isArray}}.to_string()); }, + None => { req = req.with_form_param("{{{baseName}}}".to_string(), "".to_string()); }, + } + {{/isNullable}} + {{/required}} + {{^required}} + if let Some(param_value) = {{{paramName}}} { + req = req.with_form_param("{{{baseName}}}".to_string(), param_value{{#isArray}}.join(","){{/isArray}}.to_string()); + } + {{/required}} + {{/isFile}} + {{/formParams}} + {{/hasFormParams}} + {{#hasBodyParam}} + {{#bodyParams}} + req = req.with_body_param({{{paramName}}}); + {{/bodyParams}} + {{/hasBodyParam}} + {{^returnType}} + req = req.returns_nothing(); + {{/returnType}} + + req.execute(self.configuration.borrow()) + } + +{{/operation}} +{{/operations}} +} diff --git a/scripts/rust-client-templates/hyper0x/api_mod.mustache b/scripts/rust-client-templates/hyper0x/api_mod.mustache new file mode 100644 index 00000000..51a42ef3 --- /dev/null +++ b/scripts/rust-client-templates/hyper0x/api_mod.mustache @@ -0,0 +1,64 @@ +use http; +use hyper; +use serde_json; + +#[derive(Debug)] +pub enum Error { + Api(ApiError), + Header(hyper::http::header::InvalidHeaderValue), + Http(http::Error), + Hyper(hyper::Error), + Serde(serde_json::Error), + UriError(http::uri::InvalidUri), +} + +#[derive(Debug)] +pub struct ApiError { + pub code: hyper::StatusCode, + pub body: hyper::body::Body, +} + +impl From<(hyper::StatusCode, hyper::body::Body)> for Error { + fn from(e: (hyper::StatusCode, hyper::body::Body)) -> Self { + Error::Api(ApiError { + code: e.0, + body: e.1, + }) + } +} + +impl From for Error { + fn from(e: http::Error) -> Self { + Error::Http(e) + } +} + +impl From for Error { + fn from(e: hyper::Error) -> Self { + Error::Hyper(e) + } +} + +impl From for Error { + fn from(e: serde_json::Error) -> Self { + Error::Serde(e) + } +} + +mod request; + +{{#apiInfo}} +{{#apis}} +mod {{{classFilename}}}; +{{#operations}} +{{#operation}} +{{#-last}} +pub use self::{{{classFilename}}}::{ {{{classname}}}, {{{classname}}}Client }; +{{/-last}} +{{/operation}} +{{/operations}} +{{/apis}} +{{/apiInfo}} + +pub mod configuration; +pub mod client; diff --git a/scripts/rust-client-templates/hyper0x/client.mustache b/scripts/rust-client-templates/hyper0x/client.mustache new file mode 100644 index 00000000..25124d94 --- /dev/null +++ b/scripts/rust-client-templates/hyper0x/client.mustache @@ -0,0 +1,54 @@ +use std::rc::Rc; + +use hyper; +use super::configuration::Configuration; + +pub struct APIClient { +{{#apiInfo}} +{{#apis}} +{{#operations}} +{{#operation}} + {{#-last}} + {{{classFilename}}}: Box, + {{/-last}} +{{/operation}} +{{/operations}} +{{/apis}} +{{/apiInfo}} +} + +impl APIClient { + pub fn new(configuration: Configuration) -> APIClient + where C: Clone + std::marker::Send + Sync + 'static { + let rc = Rc::new(configuration); + + APIClient { +{{#apiInfo}} +{{#apis}} +{{#operations}} +{{#operation}} + {{#-last}} + {{{classFilename}}}: Box::new(crate::apis::{{{classname}}}Client::new(rc.clone())), + {{/-last}} +{{/operation}} +{{/operations}} +{{/apis}} +{{/apiInfo}} + } + } + +{{#apiInfo}} +{{#apis}} +{{#operations}} +{{#operation}} +{{#-last}} + pub fn {{{classFilename}}}(&self) -> &dyn crate::apis::{{{classname}}}{ + self.{{{classFilename}}}.as_ref() + } + +{{/-last}} +{{/operation}} +{{/operations}} +{{/apis}} +{{/apiInfo}} +} diff --git a/scripts/rust-client-templates/hyper0x/configuration.mustache b/scripts/rust-client-templates/hyper0x/configuration.mustache new file mode 100644 index 00000000..ee6f407d --- /dev/null +++ b/scripts/rust-client-templates/hyper0x/configuration.mustache @@ -0,0 +1,34 @@ +{{>partial_header}} +use hyper; + +pub struct Configuration + where C: Clone + std::marker::Send + Sync + 'static { + pub base_path: String, + pub user_agent: Option, + pub client: hyper::client::Client, + pub basic_auth: Option, + pub oauth_access_token: Option, + pub api_key: Option, + // TODO: take an oauth2 token source, similar to the go one +} + +pub type BasicAuth = (String, Option); + +pub struct ApiKey { + pub prefix: Option, + pub key: String, +} + +impl Configuration + where C: Clone + std::marker::Send + Sync { + pub fn new(client: hyper::client::Client) -> Configuration { + Configuration { + base_path: "{{{basePath}}}".to_owned(), + user_agent: {{#httpUserAgent}}Some("{{{.}}}".to_owned()){{/httpUserAgent}}{{^httpUserAgent}}Some("OpenAPI-Generator/{{{version}}}/rust".to_owned()){{/httpUserAgent}}, + client, + basic_auth: None, + oauth_access_token: None, + api_key: None, + } + } +} diff --git a/scripts/rust-client-templates/lib.mustache b/scripts/rust-client-templates/lib.mustache new file mode 100644 index 00000000..b51df2b1 --- /dev/null +++ b/scripts/rust-client-templates/lib.mustache @@ -0,0 +1,17 @@ +#![allow(unused_imports)] +#![allow(clippy::too_many_arguments)] + +extern crate serde_repr; +extern crate serde; +extern crate serde_json; +extern crate url; +{{#hyper}} +extern crate hyper; +extern crate futures; +{{/hyper}} +{{#reqwest}} +extern crate reqwest; +{{/reqwest}} + +pub mod apis; +pub mod models; diff --git a/scripts/rust-client-templates/model.mustache b/scripts/rust-client-templates/model.mustache new file mode 100644 index 00000000..a4970abf --- /dev/null +++ b/scripts/rust-client-templates/model.mustache @@ -0,0 +1,225 @@ +{{>partial_header}} +use crate::models; +use serde::{Deserialize, Serialize}; +{{#models}} +{{#model}} +{{^isEnum}}{{#vendorExtensions.x-rust-has-byte-array}} +use serde_with::serde_as; +{{/vendorExtensions.x-rust-has-byte-array}}{{/isEnum}} +{{#isEnum}} +{{#isInteger}} +use serde_repr::{Serialize_repr,Deserialize_repr}; +{{/isInteger}} +{{/isEnum}} +{{#description}} +/// {{{classname}}} : {{{description}}} +{{/description}} +{{!-- for repr(int) enum schemas --}} +{{#isEnum}} +{{#isInteger}} +/// {{{description}}} +#[repr(i64)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize_repr, Deserialize_repr)] +pub enum {{{classname}}} { +{{#allowableValues}} +{{#enumVars}} + {{{name}}} = {{{value}}}, +{{/enumVars}}{{/allowableValues}} +} + +impl std::fmt::Display for {{{classname}}} { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", match self { + {{#allowableValues}} + {{#enumVars}} + Self::{{{name}}} => "{{{value}}}", + {{/enumVars}} + {{/allowableValues}} + }) + } +} +{{/isInteger}} +{{/isEnum}} +{{!-- for enum schemas --}} +{{#isEnum}} +{{^isInteger}} +/// {{{description}}} +#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)] +pub enum {{{classname}}} { +{{#allowableValues}} +{{#enumVars}} + #[serde(rename = "{{{value}}}")] + {{{name}}}, +{{/enumVars}}{{/allowableValues}} +} + +impl std::fmt::Display for {{{classname}}} { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + {{#allowableValues}} + {{#enumVars}} + Self::{{{name}}} => write!(f, "{{{value}}}"), + {{/enumVars}} + {{/allowableValues}} + } + } +} + +{{/isInteger}} +impl Default for {{{classname}}} { + fn default() -> {{{classname}}} { + {{#allowableValues}} + Self::{{ enumVars.0.name }} + {{/allowableValues}} + } +} +{{/isEnum}} +{{!-- for schemas that have a discriminator --}} +{{#discriminator}} +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(tag = "{{{propertyBaseName}}}")] +pub enum {{{classname}}} { + {{^oneOf}} + {{#mappedModels}} + #[serde(rename="{{mappingName}}")] + {{{modelName}}} { + {{#vars}} + {{#description}} + /// {{{.}}} + {{/description}} + #[serde(rename = "{{{baseName}}}"{{^required}}, skip_serializing_if = "Option::is_none"{{/required}})] + {{{name}}}: {{#required}}{{#isNullable}}Option<{{/isNullable}}{{/required}}{{^required}}Option<{{/required}}{{#isEnum}}{{{enumName}}}{{/isEnum}}{{^isEnum}}{{#isModel}}{{^avoidBoxedModels}}Box<{{/avoidBoxedModels}}{{{dataType}}}{{^avoidBoxedModels}}>{{/avoidBoxedModels}}{{/isModel}}{{^isModel}}{{{dataType}}}{{/isModel}}{{/isEnum}}{{#required}}{{#isNullable}}>{{/isNullable}}{{/required}}{{^required}}>{{/required}}, + {{/vars}} + }, + {{/mappedModels}} + {{/oneOf}} + {{^oneOf.isEmpty}} + {{#composedSchemas.oneOf}} + {{#description}} + /// {{{.}}} + {{/description}} + {{#baseName}} + #[serde(rename="{{{.}}}")] + {{/baseName}} + {{{name}}}({{#isModel}}{{^avoidBoxedModels}}Box<{{/avoidBoxedModels}}{{/isModel}}{{{dataType}}}{{#isModel}}{{^avoidBoxedModels}}>{{/avoidBoxedModels}}{{/isModel}}), + {{/composedSchemas.oneOf}} + {{/oneOf.isEmpty}} +} + +impl Default for {{classname}} { + fn default() -> Self { + {{^oneOf}}{{#mappedModels}}{{#-first}}Self::{{modelName}} { + {{#vars}} + {{{name}}}: Default::default(), + {{/vars}} + }{{/-first}}{{/mappedModels}} + {{/oneOf}}{{^oneOf.isEmpty}}{{#composedSchemas.oneOf}}{{#-first}}Self::{{{name}}}(Default::default()){{/-first}}{{/composedSchemas.oneOf}}{{/oneOf.isEmpty}} + } +} + +{{/discriminator}} +{{!-- for non-enum schemas --}} +{{^isEnum}} +{{^discriminator}} +{{#vendorExtensions.x-rust-has-byte-array}}#[serde_as] +{{/vendorExtensions.x-rust-has-byte-array}}{{#oneOf.isEmpty}}#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)] +pub struct {{{classname}}} { +{{#vars}} + {{#description}} + /// {{{.}}} + {{/description}} + {{#isByteArray}} + {{#vendorExtensions.isMandatory}}#[serde_as(as = "serde_with::base64::Base64")]{{/vendorExtensions.isMandatory}}{{^vendorExtensions.isMandatory}}#[serde_as(as = "{{^serdeAsDoubleOption}}Option{{/serdeAsDoubleOption}}{{#serdeAsDoubleOption}}super::DoubleOption{{/serdeAsDoubleOption}}")]{{/vendorExtensions.isMandatory}} + {{/isByteArray}} + #[serde(rename = "{{{baseName}}}"{{^required}}{{#isNullable}}, default{{^isByteArray}}, with = "::serde_with::rust::double_option"{{/isByteArray}}{{/isNullable}}{{/required}}{{^required}}, skip_serializing_if = "Option::is_none"{{/required}}{{#required}}{{#isNullable}}, deserialize_with = "Option::deserialize"{{/isNullable}}{{/required}})] + pub {{{name}}}: {{! + ### Option Start + }}{{#isNullable}}Option<{{/isNullable}}{{^required}}Option<{{/required}}{{! + ### Enums + }}{{#isEnum}}{{#isArray}}{{#uniqueItems}}std::collections::HashSet<{{/uniqueItems}}{{^uniqueItems}}Vec<{{/uniqueItems}}{{/isArray}}{{{enumName}}}{{#isArray}}>{{/isArray}}{{/isEnum}}{{! + ### Non-Enums Start + }}{{^isEnum}}{{! + ### Models + }}{{#isModel}}{{^avoidBoxedModels}}Box<{{/avoidBoxedModels}}{{{dataType}}}{{^avoidBoxedModels}}>{{/avoidBoxedModels}}{{/isModel}}{{! + ### Primative datatypes + }}{{^isModel}}{{#isByteArray}}Vec{{/isByteArray}}{{^isByteArray}}{{{dataType}}}{{/isByteArray}}{{/isModel}}{{! + ### Non-Enums End + }}{{/isEnum}}{{! + ### Option End (and trailing comma) + }}{{#isNullable}}>{{/isNullable}}{{^required}}>{{/required}}, +{{/vars}} +} + +impl {{{classname}}} { + {{#description}} + /// {{{.}}} + {{/description}} + pub fn new({{#requiredVars}}{{{name}}}: {{! + ### Option Start + }}{{#isNullable}}Option<{{/isNullable}}{{! + ### Enums + }}{{#isEnum}}{{#isArray}}{{#uniqueItems}}std::collections::HashSet<{{/uniqueItems}}{{^uniqueItems}}Vec<{{/uniqueItems}}{{/isArray}}{{{enumName}}}{{#isArray}}>{{/isArray}}{{/isEnum}}{{! + ### Non-Enums + }}{{^isEnum}}{{#isByteArray}}Vec{{/isByteArray}}{{^isByteArray}}{{{dataType}}}{{/isByteArray}}{{/isEnum}}{{! + ### Option End + }}{{#isNullable}}>{{/isNullable}}{{! + ### Comma for next arguement + }}{{^-last}}, {{/-last}}{{/requiredVars}}) -> {{{classname}}} { + {{{classname}}} { + {{#vars}} + {{{name}}}{{^required}}: None{{/required}}{{#required}}{{#isModel}}{{^avoidBoxedModels}}: {{^isNullable}}Box::new({{{name}}}){{/isNullable}}{{#isNullable}}if let Some(x) = {{{name}}} {Some(Box::new(x))} else {None}{{/isNullable}}{{/avoidBoxedModels}}{{/isModel}}{{/required}}, + {{/vars}} + } + } +} +{{/oneOf.isEmpty}} +{{^oneOf.isEmpty}} +{{! TODO: add other vars that are not part of the oneOf}} +{{#description}} +/// {{{.}}} +{{/description}} +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(untagged)] +pub enum {{classname}} { +{{#composedSchemas.oneOf}} + {{#description}} + /// {{{.}}} + {{/description}} + {{{name}}}({{#isModel}}{{^avoidBoxedModels}}Box<{{/avoidBoxedModels}}{{/isModel}}{{{dataType}}}{{#isModel}}{{^avoidBoxedModels}}>{{/avoidBoxedModels}}{{/isModel}}), +{{/composedSchemas.oneOf}} +} + +impl Default for {{classname}} { + fn default() -> Self { + {{#composedSchemas.oneOf}}{{#-first}}Self::{{{name}}}(Default::default()){{/-first}}{{/composedSchemas.oneOf}} + } +} +{{/oneOf.isEmpty}} +{{/discriminator}} +{{/isEnum}} +{{!-- for properties that are of enum type --}} +{{#vars}} +{{#isEnum}} +/// {{{description}}} +#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)] +pub enum {{{enumName}}} { +{{#allowableValues}} +{{#enumVars}} + #[serde(rename = "{{{value}}}")] + {{{name}}}, +{{/enumVars}} +{{/allowableValues}} +} + +impl Default for {{{enumName}}} { + fn default() -> {{{enumName}}} { + {{#allowableValues}} + Self::{{ enumVars.0.name }} + {{/allowableValues}} + } +} +{{/isEnum}} +{{/vars}} + +{{/model}} +{{/models}} diff --git a/scripts/rust-client-templates/model_doc.mustache b/scripts/rust-client-templates/model_doc.mustache new file mode 100644 index 00000000..2aba7596 --- /dev/null +++ b/scripts/rust-client-templates/model_doc.mustache @@ -0,0 +1,54 @@ +{{#models}}{{#model}}# {{{classname}}} +{{#isEnum}} + +## Enum Variants + +| Name | Value | +|---- | -----|{{#allowableValues}}{{#enumVars}} +| {{name}} | {{value}} |{{/enumVars}}{{/allowableValues}} + +{{/isEnum}} +{{#discriminator}} + +## Enum Variants + +| Name | Value | +|---- | -----|{{#vendorExtensions}}{{#x-mapped-models}} +| {{modelName}} | {{mappingName}} |{{/x-mapped-models}}{{/vendorExtensions}} +{{#vendorExtensions}} +{{#x-mapped-models}} + +## {{modelName}} + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +{{#vars}}**{{{name}}}** | {{^required}}Option<{{/required}}{{#required}}{{#isNullable}}Option<{{/isNullable}}{{/required}}{{#isPrimitiveType}}**{{{dataType}}}**{{/isPrimitiveType}}{{^isPrimitiveType}}[**{{{dataType}}}**]({{{complexType}}}.md){{/isPrimitiveType}}{{^required}}>{{/required}}{{#required}}{{#isNullable}}>{{/isNullable}}{{/required}} | {{{description}}} | {{^required}}[optional]{{/required}}{{#isReadOnly}}[readonly]{{/isReadOnly}}{{#defaultValue}}[default to {{{.}}}]{{/defaultValue}} +{{/vars}} +{{/x-mapped-models}} +{{/vendorExtensions}} +{{/discriminator}} +{{^discriminator}} +{{^isEnum}} +{{#oneOf.isEmpty}} + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +{{#vars}}**{{{name}}}** | {{^required}}Option<{{/required}}{{#required}}{{#isNullable}}Option<{{/isNullable}}{{/required}}{{#isPrimitiveType}}**{{{dataType}}}**{{/isPrimitiveType}}{{^isPrimitiveType}}[**{{{dataType}}}**]({{{complexType}}}.md){{/isPrimitiveType}}{{^required}}>{{/required}}{{#required}}{{#isNullable}}>{{/isNullable}}{{/required}} | {{{description}}} | {{^required}}[optional]{{/required}}{{#isReadOnly}}[readonly]{{/isReadOnly}}{{#defaultValue}}[default to {{{.}}}]{{/defaultValue}} +{{/vars}} +{{/oneOf.isEmpty}} +{{^oneOf.isEmpty}} + +## Enum Variants + +| Name | Description | +|---- | -----|{{#oneOf}} +| {{{.}}} | {{description}} |{{/oneOf}} +{{/oneOf.isEmpty}} +{{/isEnum}} +{{/discriminator}} + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + +{{/model}}{{/models}} diff --git a/scripts/rust-client-templates/model_mod.mustache b/scripts/rust-client-templates/model_mod.mustache new file mode 100644 index 00000000..bdb080f6 --- /dev/null +++ b/scripts/rust-client-templates/model_mod.mustache @@ -0,0 +1,45 @@ +{{#models}} +{{#model}} +pub mod {{{classFilename}}}; +pub use self::{{{classFilename}}}::{{{classname}}}; +{{/model}} +{{/models}} +{{#serdeAsDoubleOption}} +use serde::{Deserialize, Deserializer, Serializer}; +use serde_with::{de::DeserializeAsWrap, ser::SerializeAsWrap, DeserializeAs, SerializeAs}; +use std::marker::PhantomData; + +pub(crate) struct DoubleOption(PhantomData); + +impl SerializeAs>> for DoubleOption +where + TAs: SerializeAs, +{ + fn serialize_as(values: &Option>, serializer: S) -> Result + where + S: Serializer, + { + match values { + None => serializer.serialize_unit(), + Some(None) => serializer.serialize_none(), + Some(Some(v)) => serializer.serialize_some(&SerializeAsWrap::::new(v)), + } + } +} + +impl<'de, T, TAs> DeserializeAs<'de, Option>> for DoubleOption +where + TAs: DeserializeAs<'de, T>, + T: std::fmt::Debug, +{ + fn deserialize_as(deserializer: D) -> Result>, D::Error> + where + D: Deserializer<'de>, + { + Ok(Some( + DeserializeAsWrap::, Option>::deserialize(deserializer)? + .into_inner(), + )) + } +} +{{/serdeAsDoubleOption}} \ No newline at end of file diff --git a/scripts/rust-client-templates/partial_header.mustache b/scripts/rust-client-templates/partial_header.mustache new file mode 100644 index 00000000..408d841d --- /dev/null +++ b/scripts/rust-client-templates/partial_header.mustache @@ -0,0 +1,13 @@ +/* + {{#appName}} + * {{{.}}} + * + {{/appName}} + {{#appDescription}} + * {{{.}}} + * + {{/appDescription}} + * {{#version}}The version of the OpenAPI document: {{{.}}}{{/version}} + * {{#infoEmail}}Contact: {{{.}}}{{/infoEmail}} + * Generated by: https://openapi-generator.tech + */ diff --git a/scripts/rust-client-templates/request.rs b/scripts/rust-client-templates/request.rs new file mode 100644 index 00000000..a6f7b74c --- /dev/null +++ b/scripts/rust-client-templates/request.rs @@ -0,0 +1,247 @@ +use std::collections::HashMap; +use std::pin::Pin; + +use futures; +use futures::Future; +use futures::future::*; +use http_body_util::BodyExt; +use hyper; +use hyper_util::client::legacy::connect::Connect; +use hyper::header::{AUTHORIZATION, CONTENT_LENGTH, CONTENT_TYPE, HeaderValue, USER_AGENT}; +use serde; +use serde_json; + +use super::{configuration, Error}; + +pub(crate) struct ApiKey { + pub in_header: bool, + pub in_query: bool, + pub param_name: String, +} + +impl ApiKey { + fn key(&self, prefix: &Option, key: &str) -> String { + match prefix { + None => key.to_owned(), + Some(ref prefix) => format!("{} {}", prefix, key), + } + } +} + +#[allow(dead_code)] +pub(crate) enum Auth { + None, + ApiKey(ApiKey), + Basic, + Oauth, +} + +/// If the authorization type is unspecified then it will be automatically detected based +/// on the configuration. This functionality is useful when the OpenAPI definition does not +/// include an authorization scheme. +pub(crate) struct Request { + auth: Option, + method: hyper::Method, + path: String, + query_params: HashMap, + no_return_type: bool, + path_params: HashMap, + form_params: HashMap, + header_params: HashMap, + // TODO: multiple body params are possible technically, but not supported here. + serialized_body: Option, +} + +#[allow(dead_code)] +impl Request { + pub fn new(method: hyper::Method, path: String) -> Self { + Request { + auth: None, + method, + path, + query_params: HashMap::new(), + path_params: HashMap::new(), + form_params: HashMap::new(), + header_params: HashMap::new(), + serialized_body: None, + no_return_type: false, + } + } + + pub fn with_body_param(mut self, param: T) -> Self { + self.serialized_body = Some(serde_json::to_string(¶m).unwrap()); + self + } + + pub fn with_header_param(mut self, basename: String, param: String) -> Self { + self.header_params.insert(basename, param); + self + } + + #[allow(unused)] + pub fn with_query_param(mut self, basename: String, param: String) -> Self { + self.query_params.insert(basename, param); + self + } + + #[allow(unused)] + pub fn with_path_param(mut self, basename: String, param: String) -> Self { + self.path_params.insert(basename, param); + self + } + + #[allow(unused)] + pub fn with_form_param(mut self, basename: String, param: String) -> Self { + self.form_params.insert(basename, param); + self + } + + pub fn returns_nothing(mut self) -> Self { + self.no_return_type = true; + self + } + + pub fn with_auth(mut self, auth: Auth) -> Self { + self.auth = Some(auth); + self + } + + pub fn execute<'a, C, U>( + self, + conf: &configuration::Configuration, + ) -> Pin> + 'a + Send>> + where + C: Connect + Clone + std::marker::Send + Sync, + U: Sized + std::marker::Send + 'a, + for<'de> U: serde::Deserialize<'de>, + { + let mut query_string = ::url::form_urlencoded::Serializer::new("".to_owned()); + + let mut path = self.path; + for (k, v) in self.path_params { + // replace {id} with the value of the id path param + path = path.replace(&format!("{{{}}}", k), &v); + } + + for (key, val) in self.query_params { + query_string.append_pair(&key, &val); + } + + let mut uri_str = format!("{}{}", conf.base_path, path); + + let query_string_str = query_string.finish(); + if !query_string_str.is_empty() { + uri_str += "?"; + uri_str += &query_string_str; + } + let uri: hyper::Uri = match uri_str.parse() { + Err(e) => return Box::pin(futures::future::err(Error::UriError(e))), + Ok(u) => u, + }; + + let mut req_builder = hyper::Request::builder() + .uri(uri) + .method(self.method); + + // Detect the authorization type if it hasn't been set. + let auth = self.auth.unwrap_or_else(|| + if conf.api_key.is_some() { + panic!("Cannot automatically set the API key from the configuration, it must be specified in the OpenAPI definition") + } else if conf.oauth_access_token.is_some() { + Auth::Oauth + } else if conf.basic_auth.is_some() { + Auth::Basic + } else { + Auth::None + } + ); + match auth { + Auth::ApiKey(apikey) => { + if let Some(ref key) = conf.api_key { + let val = apikey.key(&key.prefix, &key.key); + if apikey.in_query { + query_string.append_pair(&apikey.param_name, &val); + } + if apikey.in_header { + req_builder = req_builder.header(&apikey.param_name, val); + } + } + } + Auth::Basic => { + if let Some(ref auth_conf) = conf.basic_auth { + let mut text = auth_conf.0.clone(); + text.push(':'); + if let Some(ref pass) = auth_conf.1 { + text.push_str(&pass[..]); + } + let encoded = base64::encode(&text); + req_builder = req_builder.header(AUTHORIZATION, encoded); + } + } + Auth::Oauth => { + if let Some(ref token) = conf.oauth_access_token { + let text = "Bearer ".to_owned() + token; + req_builder = req_builder.header(AUTHORIZATION, text); + } + } + Auth::None => {} + } + + if let Some(ref user_agent) = conf.user_agent { + req_builder = req_builder.header(USER_AGENT, match HeaderValue::from_str(user_agent) { + Ok(header_value) => header_value, + Err(e) => return Box::pin(futures::future::err(super::Error::Header(e))) + }); + } + + for (k, v) in self.header_params { + req_builder = req_builder.header(&k, v); + } + + let req_headers = req_builder.headers_mut().unwrap(); + let request_result = if self.form_params.len() > 0 { + req_headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/x-www-form-urlencoded")); + let mut enc = ::url::form_urlencoded::Serializer::new("".to_owned()); + for (k, v) in self.form_params { + enc.append_pair(&k, &v); + } + req_builder.body(enc.finish()) + } else if let Some(body) = self.serialized_body { + req_headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json")); + req_headers.insert(CONTENT_LENGTH, body.len().into()); + req_builder.body(body) + } else { + req_builder.body(String::new()) + }; + let request = match request_result { + Ok(request) => request, + Err(e) => return Box::pin(futures::future::err(Error::from(e))) + }; + + let no_return_type = self.no_return_type; + Box::pin(conf.client + .request(request) + .map_err(|e| Error::from(e)) + .and_then(move |response| { + let status = response.status(); + if !status.is_success() { + futures::future::err::(Error::from((status, response.into_body()))).boxed() + } else if no_return_type { + // This is a hack; if there's no_ret_type, U is (), but serde_json gives an + // error when deserializing "" into (), so deserialize 'null' into it + // instead. + // An alternate option would be to require U: Default, and then return + // U::default() here instead since () implements that, but then we'd + // need to impl default for all models. + futures::future::ok::(serde_json::from_str("null").expect("serde null value")).boxed() + } else { + let collect = response.into_body().collect().map_err(Error::from); + collect.map(|collected| { + collected.and_then(|collected| { + serde_json::from_slice(&collected.to_bytes()).map_err(Error::from) + }) + }).boxed() + } + })) + } +} diff --git a/scripts/rust-client-templates/reqwest-trait/api.mustache b/scripts/rust-client-templates/reqwest-trait/api.mustache new file mode 100644 index 00000000..7ab6fbde --- /dev/null +++ b/scripts/rust-client-templates/reqwest-trait/api.mustache @@ -0,0 +1,504 @@ +{{>partial_header}} + +use async_trait::async_trait; +{{#mockall}} +#[cfg(feature = "mockall")] +use mockall::automock; +{{/mockall}} +use reqwest; +use std::sync::Arc; +use serde::{Deserialize, Serialize, de::Error as _}; +use crate::{apis::ResponseContent, models}; +use super::{Error, configuration}; +use crate::apis::ContentType; + +{{#mockall}} +#[cfg_attr(feature = "mockall", automock)] +{{/mockall}} +#[async_trait] +pub trait {{{classname}}}: Send + Sync { +{{#operations}} +{{#operation}} + + /// {{{httpMethod}}} {{{path}}} + {{^notes.empty}} + /// + /// {{{notes}}} + {{/notes.empty}} +{{#vendorExtensions.x-group-parameters}} + async fn {{{operationId}}}(&self, {{#allParams}}{{#-first}} params: {{{operationIdCamelCase}}}Params {{/-first}}{{/allParams}}{{! + ### Function return type + }}) -> Result<{{! + ### Multi response support + }}{{#supportMultipleResponses}}ResponseContent<{{{operationIdCamelCase}}}Success>{{/supportMultipleResponses}}{{! + ### Regular return type + }}{{^supportMultipleResponses}}{{^returnType}}(){{/returnType}}{{{returnType}}}{{/supportMultipleResponses}}{{! + ### Error Type + }}, Error<{{{operationIdCamelCase}}}Error>>; +{{/vendorExtensions.x-group-parameters}} +{{^vendorExtensions.x-group-parameters}} + async fn {{{operationId}}}{{! + ### Lifetimes + }}<{{#allParams}}'{{#lambda.lifetimeName}}{{{paramName}}}{{/lambda.lifetimeName}}{{^-last}}, {{/-last}}{{/allParams}}>{{! + ### Function parameter names + }}(&self, {{#allParams}}{{{paramName}}}: {{! + ### Option Start + }}{{^required}}Option<{{/required}}{{#required}}{{#isNullable}}Option<{{/isNullable}}{{/required}}{{! + ### &str and Vec<&str> + }}{{#isString}}{{#isArray}}Vec<{{/isArray}}{{^isUuid}}&'{{#lambda.lifetimeName}}{{{paramName}}}{{/lambda.lifetimeName}} str{{/isUuid}}{{#isArray}}>{{/isArray}}{{/isString}}{{! + ### UUIDs + }}{{#isUuid}}{{#isArray}}Vec<{{/isArray}}&str{{#isArray}}>{{/isArray}}{{/isUuid}}{{! + ### Models and primative types + }}{{^isString}}{{^isUuid}}{{^isPrimitiveType}}{{^isContainer}}models::{{/isContainer}}{{/isPrimitiveType}}{{{dataType}}}{{/isUuid}}{{/isString}}{{! + ### Option End + }}{{^required}}>{{/required}}{{#required}}{{#isNullable}}>{{/isNullable}}{{/required}}{{! + ### Comma for next arguement + }}{{^-last}}, {{/-last}}{{/allParams}}{{! + ### Function return type + }}) -> Result<{{! + ### Multi response support + }}{{#supportMultipleResponses}}ResponseContent<{{{operationIdCamelCase}}}Success>{{/supportMultipleResponses}}{{! + ### Regular return type + }}{{^supportMultipleResponses}}{{^returnType}}(){{/returnType}}{{{returnType}}}{{/supportMultipleResponses}}{{! + ### Error Type + }}, Error<{{{operationIdCamelCase}}}Error>>; +{{/vendorExtensions.x-group-parameters}} +{{/operation}} +{{/operations}} +} + +pub struct {{{classname}}}Client { + configuration: Arc +} + +impl {{classname}}Client { + pub fn new(configuration: Arc) -> Self { + Self { configuration } + } +} + + +{{#operations}} +{{#operation}} +{{#vendorExtensions.x-group-parameters}} +{{#allParams}} +{{#-first}} +/// struct for passing parameters to the method [`{{operationId}}`] +#[derive(Clone, Debug)] +{{#useBonBuilder}} +#[cfg_attr(feature = "bon", derive(::bon::Builder))] +{{/useBonBuilder}} +pub struct {{{operationIdCamelCase}}}Params { +{{/-first}} + {{#description}} + /// {{{.}}} + {{/description}} + pub {{{paramName}}}: {{! + ### Option Start + }}{{^required}}Option<{{/required}}{{#required}}{{#isNullable}}Option<{{/isNullable}}{{/required}}{{! + ### &str and Vec<&str> + }}{{^isUuid}}{{#isString}}{{#isArray}}Vec<{{/isArray}}String{{#isArray}}>{{/isArray}}{{/isString}}{{/isUuid}}{{! + ### UUIDs + }}{{#isUuid}}{{#isArray}}Vec<{{/isArray}}String{{#isArray}}>{{/isArray}}{{/isUuid}}{{! + ### Models and primative types + }}{{^isString}}{{^isUuid}}{{^isPrimitiveType}}{{^isContainer}}models::{{/isContainer}}{{/isPrimitiveType}}{{{dataType}}}{{/isUuid}}{{/isString}}{{! + ### Option End + }}{{^required}}>{{/required}}{{#required}}{{#isNullable}}>{{/isNullable}}{{/required}}{{! + ### Comma for next field + }}{{^-last}},{{/-last}} +{{#-last}} +} + +{{/-last}} +{{/allParams}} +{{/vendorExtensions.x-group-parameters}} +{{/operation}} +{{/operations}} + +#[async_trait] +impl {{classname}} for {{classname}}Client { + {{#operations}} + {{#operation}} + {{#description}} + /// {{{.}}} + {{/description}} + {{#notes}} + /// {{{.}}} + {{/notes}} + {{#vendorExtensions.x-group-parameters}} + async fn {{{operationId}}}(&self, {{#allParams}}{{#-first}} params: {{{operationIdCamelCase}}}Params {{/-first}}{{/allParams}}{{! + ### Function return type + }}) -> Result<{{! + ### Multi response support + }}{{#supportMultipleResponses}}ResponseContent<{{{operationIdCamelCase}}}Success>{{/supportMultipleResponses}}{{! + ### Regular return type + }}{{^supportMultipleResponses}}{{^returnType}}(){{/returnType}}{{{returnType}}}{{/supportMultipleResponses}}{{! + ### Error Type + }}, Error<{{{operationIdCamelCase}}}Error>> { + {{#allParams}}{{#-first}} + let {{{operationIdCamelCase}}}Params { + {{#allParams}} + {{{paramName}}}, + {{/allParams}} + } = params; + {{/-first}}{{/allParams}} + + {{/vendorExtensions.x-group-parameters}} + {{^vendorExtensions.x-group-parameters}} + async fn {{{operationId}}}{{! + ### Lifetimes + }}<{{#allParams}}'{{#lambda.lifetimeName}}{{{paramName}}}{{/lambda.lifetimeName}}{{^-last}}, {{/-last}}{{/allParams}}>{{! + ### Function parameter names + }}(&self, {{#allParams}}{{{paramName}}}: {{! + ### Option Start + }}{{^required}}Option<{{/required}}{{#required}}{{#isNullable}}Option<{{/isNullable}}{{/required}}{{! + ### &str and Vec<&str> + }}{{#isString}}{{#isArray}}Vec<{{/isArray}}{{^isUuid}}&'{{#lambda.lifetimeName}}{{{paramName}}}{{/lambda.lifetimeName}} str{{/isUuid}}{{#isArray}}>{{/isArray}}{{/isString}}{{! + ### UUIDs + }}{{#isUuid}}{{#isArray}}Vec<{{/isArray}}&str{{#isArray}}>{{/isArray}}{{/isUuid}}{{! + ### Models and primative types + }}{{^isString}}{{^isUuid}}{{^isPrimitiveType}}{{^isContainer}}models::{{/isContainer}}{{/isPrimitiveType}}{{{dataType}}}{{/isUuid}}{{/isString}}{{! + ### Option End + }}{{^required}}>{{/required}}{{#required}}{{#isNullable}}>{{/isNullable}}{{/required}}{{! + ### Comma for next arguement + }}{{^-last}}, {{/-last}}{{/allParams}}{{! + ### Function return type + }}) -> Result<{{! + ### Multi response support + }}{{#supportMultipleResponses}}ResponseContent<{{{operationIdCamelCase}}}Success>{{/supportMultipleResponses}}{{! + ### Regular return type + }}{{^supportMultipleResponses}}{{^returnType}}(){{/returnType}}{{{returnType}}}{{/supportMultipleResponses}}{{! + ### Regular return type + }}, Error<{{{operationIdCamelCase}}}Error>> { + {{/vendorExtensions.x-group-parameters}} + let local_var_configuration = &self.configuration; + + let local_var_client = &local_var_configuration.client; + + let local_var_uri_str = format!("{}{{{path}}}", local_var_configuration.base_path{{#pathParams}}, {{{baseName}}}={{#isString}}crate::apis::urlencode({{/isString}}{{{paramName}}}{{^required}}.unwrap(){{/required}}{{#required}}{{#isNullable}}.unwrap(){{/isNullable}}{{/required}}{{#isArray}}.join(",").as_ref(){{/isArray}}{{^isString}}{{^isUuid}}{{^isPrimitiveType}}{{^isContainer}}.to_string(){{/isContainer}}{{/isPrimitiveType}}{{/isUuid}}{{/isString}}{{#isString}}){{/isString}}{{/pathParams}}); + let mut local_var_req_builder = local_var_client.request(reqwest::Method::{{{httpMethod}}}, local_var_uri_str.as_str()); + + {{#queryParams}} + {{#required}} + {{#isArray}} + local_var_req_builder = match "{{collectionFormat}}" { + "multi" => local_var_req_builder.query(&{{{paramName}}}.into_iter().map(|p| ("{{{baseName}}}".to_owned(), p.to_string())).collect::>()), + _ => local_var_req_builder.query(&[("{{{baseName}}}", &{{{paramName}}}.into_iter().map(|p| p.to_string()).collect::>().join(",").to_string())]), + }; + {{/isArray}} + {{^isArray}} + {{^isNullable}} + local_var_req_builder = local_var_req_builder.query(&[("{{{baseName}}}", &{{{paramName}}}.to_string())]); + {{/isNullable}} + {{#isNullable}} + {{#isDeepObject}} + if let Some(ref local_var_str) = {{{paramName}}} { + let params = crate::apis::parse_deep_object("{{{baseName}}}", local_var_str); + local_var_req_builder = local_var_req_builder.query(¶ms); + }; + {{/isDeepObject}} + {{^isDeepObject}} + if let Some(ref local_var_str) = {{{paramName}}} { + local_var_req_builder = local_var_req_builder.query(&[("{{{baseName}}}", &local_var_str.to_string())]); + }; + {{/isDeepObject}} + {{/isNullable}} + {{/isArray}} + {{/required}} + {{^required}} + if let Some(ref local_var_str) = {{{paramName}}} { + {{#isArray}} + local_var_req_builder = match "{{collectionFormat}}" { + "multi" => local_var_req_builder.query(&local_var_str.into_iter().map(|p| ("{{{baseName}}}".to_owned(), p.to_string())).collect::>()), + _ => local_var_req_builder.query(&[("{{{baseName}}}", &local_var_str.into_iter().map(|p| p.to_string()).collect::>().join(",").to_string())]), + }; + {{/isArray}} + {{^isArray}} + {{#isDeepObject}} + let params = crate::apis::parse_deep_object("{{{baseName}}}", local_var_str); + local_var_req_builder = local_var_req_builder.query(¶ms); + {{/isDeepObject}} + {{^isDeepObject}} + local_var_req_builder = local_var_req_builder.query(&[("{{{baseName}}}", &local_var_str.to_string())]); + {{/isDeepObject}} + {{/isArray}} + } + {{/required}} + {{/queryParams}} + {{#hasAuthMethods}} + {{#authMethods}} + {{#isApiKey}} + {{#isKeyInQuery}} + if let Some(ref local_var_apikey) = local_var_configuration.api_key { + let local_var_key = local_var_apikey.key.clone(); + let local_var_value = match local_var_apikey.prefix { + Some(ref local_var_prefix) => format!("{} {}", local_var_prefix, local_var_key), + None => local_var_key, + }; + local_var_req_builder = local_var_req_builder.query(&[("{{{keyParamName}}}", local_var_value)]); + } + {{/isKeyInQuery}} + {{/isApiKey}} + {{/authMethods}} + {{/hasAuthMethods}} + {{#hasAuthMethods}} + {{#withAWSV4Signature}} + if let Some(ref local_var_aws_v4_key) = local_var_configuration.aws_v4_key { + let local_var_new_headers = match local_var_aws_v4_key.sign( + &local_var_uri_str, + "{{{httpMethod}}}", + {{#hasBodyParam}} + {{#bodyParams}} + &serde_json::to_string(&{{{paramName}}}).expect("param should serialize to string"), + {{/bodyParams}} + {{/hasBodyParam}} + {{^hasBodyParam}} + "", + {{/hasBodyParam}} + ) { + Ok(new_headers) => new_headers, + Err(err) => return Err(Error::AWSV4SignatureError(err)), + }; + for (local_var_name, local_var_value) in local_var_new_headers.iter() { + local_var_req_builder = local_var_req_builder.header(local_var_name.as_str(), local_var_value.as_str()); + } + } + {{/withAWSV4Signature}} + {{/hasAuthMethods}} + if let Some(ref local_var_user_agent) = local_var_configuration.user_agent { + local_var_req_builder = local_var_req_builder.header(reqwest::header::USER_AGENT, local_var_user_agent.clone()); + } + {{#hasHeaderParams}} + {{#headerParams}} + {{#required}} + {{^isNullable}} + local_var_req_builder = local_var_req_builder.header("{{{baseName}}}", {{{paramName}}}{{#isArray}}.join(","){{/isArray}}.to_string()); + {{/isNullable}} + {{#isNullable}} + match {{{paramName}}} { + Some(local_var_param_value) => { local_var_req_builder = local_var_req_builder.header("{{{baseName}}}", local_var_param_value{{#isArray}}.join(","){{/isArray}}.to_string()); }, + None => { local_var_req_builder = local_var_req_builder.header("{{{baseName}}}", ""); }, + } + {{/isNullable}} + {{/required}} + {{^required}} + if let Some(local_var_param_value) = {{{paramName}}} { + local_var_req_builder = local_var_req_builder.header("{{{baseName}}}", local_var_param_value{{#isArray}}.join(","){{/isArray}}.to_string()); + } + {{/required}} + {{/headerParams}} + {{/hasHeaderParams}} + {{#hasAuthMethods}} + {{#authMethods}} + {{#supportTokenSource}} + // Obtain a token from source provider. + // Tokens can be Id or access tokens depending on the provider type and configuration. + let token = local_var_configuration.token_source.token().await.map_err(Error::TokenSource)?; + // The token format is the responsibility of the provider, thus we just set the authorization header with whatever is given. + local_var_req_builder = local_var_req_builder.header(reqwest::header::AUTHORIZATION, token); + {{/supportTokenSource}} + {{^supportTokenSource}} + {{#isApiKey}} + {{#isKeyInHeader}} + if let Some(ref local_var_apikey) = local_var_configuration.api_key { + let local_var_key = local_var_apikey.key.clone(); + let local_var_value = match local_var_apikey.prefix { + Some(ref local_var_prefix) => format!("{} {}", local_var_prefix, local_var_key), + None => local_var_key, + }; + local_var_req_builder = local_var_req_builder.header("{{{keyParamName}}}", local_var_value); + }; + {{/isKeyInHeader}} + {{/isApiKey}} + {{#isBasic}} + {{#isBasicBasic}} + if let Some(ref local_var_auth_conf) = local_var_configuration.basic_auth { + local_var_req_builder = local_var_req_builder.basic_auth(local_var_auth_conf.0.to_owned(), local_var_auth_conf.1.to_owned()); + }; + {{/isBasicBasic}} + {{#isBasicBearer}} + if let Some(ref local_var_token) = local_var_configuration.bearer_access_token { + local_var_req_builder = local_var_req_builder.bearer_auth(local_var_token.to_owned()); + }; + {{/isBasicBearer}} + {{/isBasic}} + {{#isOAuth}} + if let Some(ref local_var_token) = local_var_configuration.oauth_access_token { + local_var_req_builder = local_var_req_builder.bearer_auth(local_var_token.to_owned()); + }; + {{/isOAuth}} + {{/supportTokenSource}} + {{/authMethods}} + {{/hasAuthMethods}} + {{#isMultipart}} + {{#hasFormParams}} + let mut local_var_form = reqwest::multipart::Form::new(); + {{#formParams}} + {{#isFile}} + // TODO: support file upload for '{{{baseName}}}' parameter + {{/isFile}} + {{^isFile}} + {{#required}} + {{^isNullable}} + local_var_form = local_var_form.text("{{{baseName}}}", {{{paramName}}}{{#isArray}}.into_iter().map(|p| p.to_string()).collect::>().join(","){{/isArray}}.to_string()); + {{/isNullable}} + {{#isNullable}} + match {{{paramName}}} { + Some(local_var_param_value) => { local_var_form = local_var_form.text("{{{baseName}}}", local_var_param_value{{#isArray}}.into_iter().map(|p| p.to_string()).collect::>().join(","){{/isArray}}.to_string()); }, + None => { local_var_form = local_var_form.text("{{{baseName}}}", ""); }, + } + {{/isNullable}} + {{/required}} + {{^required}} + if let Some(local_var_param_value) = {{{paramName}}} { + local_var_form = local_var_form.text("{{{baseName}}}", local_var_param_value{{#isArray}}.into_iter().map(|p| p.to_string()).collect::>().join(","){{/isArray}}.to_string()); + } + {{/required}} + {{/isFile}} + {{/formParams}} + local_var_req_builder = local_var_req_builder.multipart(local_var_form); + {{/hasFormParams}} + {{/isMultipart}} + {{^isMultipart}} + {{#hasFormParams}} + let mut local_var_form_params = std::collections::HashMap::new(); + {{#formParams}} + {{#isFile}} + {{#required}} + {{^isNullable}} + local_var_form_params.insert("{{{baseName}}}", unimplemented!("File form param not supported with x-www-form-urlencoded content")); + {{/isNullable}} + {{#isNullable}} + match {{{paramName}}} { + Some(local_var_param_value) => { local_var_form_params.insert("{{{baseName}}}", unimplemented!("File form param not supported with x-www-form-urlencoded content")); }, + None => { unimplemented!("Required nullable file form param not supported with x-www-form-urlencoded content"); }, + } + {{/isNullable}} + {{/required}} + {{^required}} + if let Some(local_var_param_value) = {{{paramName}}} { + local_var_form_params.insert("{{{baseName}}}", unimplemented!("File form param not supported with x-www-form-urlencoded content")); + } + {{/required}} + {{/isFile}} + {{^isFile}} + {{#required}} + {{^isNullable}} + local_var_form_params.insert("{{{baseName}}}", {{{paramName}}}{{#isArray}}.into_iter().map(|p| p.to_string()).collect::>().join(","){{/isArray}}.to_string()); + {{/isNullable}} + {{#isNullable}} + match {{{paramName}}} { + Some(local_var_param_value) => { local_var_form_params.insert("{{{baseName}}}", local_var_param_value{{#isArray}}.into_iter().map(|p| p.to_string()).collect::>().join(","){{/isArray}}.to_string()); }, + None => { local_var_form_params.insert("{{{baseName}}}", ""); }, + } + {{/isNullable}} + {{/required}} + {{^required}} + if let Some(local_var_param_value) = {{{paramName}}} { + local_var_form_params.insert("{{{baseName}}}", local_var_param_value{{#isArray}}.into_iter().map(|p| p.to_string()).collect::>().join(","){{/isArray}}.to_string()); + } + {{/required}} + {{/isFile}} + {{/formParams}} + local_var_req_builder = local_var_req_builder.form(&local_var_form_params); + {{/hasFormParams}} + {{/isMultipart}} + {{#hasBodyParam}} + {{#bodyParams}} + local_var_req_builder = local_var_req_builder.json(&{{{paramName}}}); + {{/bodyParams}} + {{/hasBodyParam}} + + let local_var_req = local_var_req_builder.build()?; + let local_var_resp = local_var_client.execute(local_var_req).await?; + + let local_var_status = local_var_resp.status(); + {{^supportMultipleResponses}} + {{#returnType}} + let local_var_content_type = local_var_resp + .headers() + .get("content-type") + .and_then(|v| v.to_str().ok()) + .unwrap_or("application/octet-stream"); + let local_var_content_type = super::ContentType::from(local_var_content_type); + {{/returnType}} + {{/supportMultipleResponses}} + let local_var_content = local_var_resp.text().await?; + + if !local_var_status.is_client_error() && !local_var_status.is_server_error() { + {{^supportMultipleResponses}} + {{^returnType}} + Ok(()) + {{/returnType}} + {{#returnType}} + match local_var_content_type { + ContentType::Json => serde_json::from_str(&local_var_content).map_err(Error::from), + {{#vendorExtensions.x-supports-plain-text}} + ContentType::Text => return Ok(local_var_content), + {{/vendorExtensions.x-supports-plain-text}} + {{^vendorExtensions.x-supports-plain-text}} + ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `{{returnType}}`"))), + {{/vendorExtensions.x-supports-plain-text}} + ContentType::Unsupported(local_var_unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{local_var_unknown_type}` content type response that cannot be converted to `{{returnType}}`")))), + } + {{/returnType}} + {{/supportMultipleResponses}} + {{#supportMultipleResponses}} + let local_var_entity: Option<{{{operationIdCamelCase}}}Success> = serde_json::from_str(&local_var_content).ok(); + let local_var_result = ResponseContent { status: local_var_status, content: local_var_content, entity: local_var_entity }; + Ok(local_var_result) + {{/supportMultipleResponses}} + } else { + let local_var_entity: Option<{{{operationIdCamelCase}}}Error> = serde_json::from_str(&local_var_content).ok(); + let local_var_error = ResponseContent { status: local_var_status, content: local_var_content, entity: local_var_entity }; + Err(Error::ResponseError(local_var_error)) + } + } + + {{/operation}} + {{/operations}} +} + +{{#supportMultipleResponses}} +{{#operations}} +{{#operation}} +/// struct for typed successes of method [`{{operationId}}`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum {{{operationIdCamelCase}}}Success { + {{#responses}} + {{#is2xx}} + Status{{code}}({{#isEnum}}{{{enumName}}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}), + {{/is2xx}} + {{#is3xx}} + Status{{code}}({{#isEnum}}{{{enumName}}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}), + {{/is3xx}} + {{/responses}} + UnknownValue(serde_json::Value), +} + +{{/operation}} +{{/operations}} +{{/supportMultipleResponses}} +{{#operations}} +{{#operation}} +/// struct for typed errors of method [`{{operationId}}`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum {{{operationIdCamelCase}}}Error { + {{#responses}} + {{#is4xx}} + Status{{code}}({{#isEnum}}{{{enumName}}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}), + {{/is4xx}} + {{#is5xx}} + Status{{code}}({{#isEnum}}{{{enumName}}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}), + {{/is5xx}} + {{#isDefault}} + DefaultResponse({{#isEnum}}{{{enumName}}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}), + {{/isDefault}} + {{/responses}} + UnknownValue(serde_json::Value), +} + +{{/operation}} +{{/operations}} diff --git a/scripts/rust-client-templates/reqwest-trait/api_mod.mustache b/scripts/rust-client-templates/reqwest-trait/api_mod.mustache new file mode 100644 index 00000000..41a4ba0f --- /dev/null +++ b/scripts/rust-client-templates/reqwest-trait/api_mod.mustache @@ -0,0 +1,236 @@ +use std::error; +use std::fmt; +{{#withAWSV4Signature}} +use aws_sigv4; +{{/withAWSV4Signature}} + +#[derive(Debug, Clone)] +pub struct ResponseContent { + pub status: reqwest::StatusCode, + pub content: String, + pub entity: Option, +} + +#[derive(Debug)] +pub enum Error { + Reqwest(reqwest::Error), + {{#supportMiddleware}} + ReqwestMiddleware(reqwest_middleware::Error), + {{/supportMiddleware}} + Serde(serde_json::Error), + Io(std::io::Error), + ResponseError(ResponseContent), + {{#withAWSV4Signature}} + AWSV4SignatureError(aws_sigv4::http_request::Error), + {{/withAWSV4Signature}} + {{#supportTokenSource}} + TokenSource(Box), + {{/supportTokenSource}} +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let (module, e) = match self { + Error::Reqwest(e) => ("reqwest", e.to_string()), + {{#supportMiddleware}} + Error::ReqwestMiddleware(e) => ("reqwest-middleware", e.to_string()), + {{/supportMiddleware}} + Error::Serde(e) => ("serde", e.to_string()), + Error::Io(e) => ("IO", e.to_string()), + Error::ResponseError(e) => ("response", format!("status code {}", e.status)), + {{#withAWSV4Signature}} + Error::AWSV4SignatureError(e) => ("aws v4 signature", e.to_string()), + {{/withAWSV4Signature}} + {{#supportTokenSource}} + Error::TokenSource(e) => ("token source failure", e.to_string()), + {{/supportTokenSource}} + }; + write!(f, "error in {}: {}", module, e) + } +} + +impl error::Error for Error { + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + Some(match self { + Error::Reqwest(e) => e, + {{#supportMiddleware}} + Error::ReqwestMiddleware(e) => e, + {{/supportMiddleware}} + Error::Serde(e) => e, + Error::Io(e) => e, + Error::ResponseError(_) => return None, + {{#withAWSV4Signature}} + Error::AWSV4SignatureError(_) => return None, + {{/withAWSV4Signature}} + {{#supportTokenSource}} + Error::TokenSource(e) => &**e, + {{/supportTokenSource}} + }) + } +} + +impl From for Error { + fn from(e: reqwest::Error) -> Self { + Error::Reqwest(e) + } +} + +{{#supportMiddleware}} +impl From for Error { + fn from(e: reqwest_middleware::Error) -> Self { + Error::ReqwestMiddleware(e) + } +} + +{{/supportMiddleware}} +impl From for Error { + fn from(e: serde_json::Error) -> Self { + Error::Serde(e) + } +} + +impl From for Error { + fn from(e: std::io::Error) -> Self { + Error::Io(e) + } +} + +pub fn urlencode>(s: T) -> String { + ::url::form_urlencoded::byte_serialize(s.as_ref().as_bytes()).collect() +} + +pub fn parse_deep_object(prefix: &str, value: &serde_json::Value) -> Vec<(String, String)> { + if let serde_json::Value::Object(object) = value { + let mut params = vec![]; + + for (key, value) in object { + match value { + serde_json::Value::Object(_) => params.append(&mut parse_deep_object( + &format!("{}[{}]", prefix, key), + value, + )), + serde_json::Value::Array(array) => { + for (i, value) in array.iter().enumerate() { + params.append(&mut parse_deep_object( + &format!("{}[{}][{}]", prefix, key, i), + value, + )); + } + }, + serde_json::Value::String(s) => params.push((format!("{}[{}]", prefix, key), s.clone())), + _ => params.push((format!("{}[{}]", prefix, key), value.to_string())), + } + } + + return params; + } + + unimplemented!("Only objects are supported with style=deepObject") +} + +/// Internal use only +/// A content type supported by this client. +#[allow(dead_code)] +enum ContentType { + Json, + Text, + Unsupported(String) +} + +impl From<&str> for ContentType { + fn from(content_type: &str) -> Self { + if content_type.starts_with("application") && content_type.contains("json") { + return Self::Json; + } else if content_type.starts_with("text/plain") { + return Self::Text; + } else { + return Self::Unsupported(content_type.to_string()); + } + } +} + +{{#apiInfo}} +{{#apis}} +pub mod {{{classFilename}}}; +{{/apis}} +{{/apiInfo}} + +pub mod configuration; + +{{#topLevelApiClient}} +use std::sync::Arc; + +pub trait Api { + {{#apiInfo}} + {{#apis}} + fn {{{classFilename}}}(&self) -> &dyn {{{classFilename}}}::{{classname}}; + {{/apis}} + {{/apiInfo}} +} + +pub struct ApiClient { + {{#apiInfo}} + {{#apis}} + {{{classFilename}}}: Box, + {{/apis}} + {{/apiInfo}} +} + +impl ApiClient { + pub fn new(configuration: Arc) -> Self { + Self { + {{#apiInfo}} + {{#apis}} + {{{classFilename}}}: Box::new({{{classFilename}}}::{{classname}}Client::new(configuration.clone())), + {{/apis}} + {{/apiInfo}} + } + } +} + +impl Api for ApiClient { + {{#apiInfo}} + {{#apis}} + fn {{{classFilename}}}(&self) -> &dyn {{{classFilename}}}::{{classname}} { + self.{{{classFilename}}}.as_ref() + } + {{/apis}} + {{/apiInfo}} +} + +{{#mockall}} +#[cfg(feature = "mockall")] +pub struct MockApiClient { + {{#apiInfo}} + {{#apis}} + pub {{{classFilename}}}_mock: {{{classFilename}}}::Mock{{classname}}, + {{/apis}} + {{/apiInfo}} +} + +#[cfg(feature = "mockall")] +impl MockApiClient { + pub fn new() -> Self { + Self { + {{#apiInfo}} + {{#apis}} + {{{classFilename}}}_mock: {{{classFilename}}}::Mock{{classname}}::new(), + {{/apis}} + {{/apiInfo}} + } + } +} + +#[cfg(feature = "mockall")] +impl Api for MockApiClient { + {{#apiInfo}} + {{#apis}} + fn {{{classFilename}}}(&self) -> &dyn {{{classFilename}}}::{{classname}} { + &self.{{{classFilename}}}_mock + } + {{/apis}} + {{/apiInfo}} +} +{{/mockall}} + +{{/topLevelApiClient}} diff --git a/scripts/rust-client-templates/reqwest-trait/configuration.mustache b/scripts/rust-client-templates/reqwest-trait/configuration.mustache new file mode 100644 index 00000000..25360691 --- /dev/null +++ b/scripts/rust-client-templates/reqwest-trait/configuration.mustache @@ -0,0 +1,120 @@ +{{>partial_header}} + +{{#withAWSV4Signature}} +use std::time::SystemTime; +use aws_sigv4::http_request::{sign, SigningSettings, SigningParams, SignableRequest}; +use http; +use secrecy::{SecretString, ExposeSecret}; +{{/withAWSV4Signature}} +{{#supportTokenSource}} +use std::sync::Arc; +use google_cloud_token::TokenSource; +use async_trait::async_trait; +{{/supportTokenSource}} + +#[derive(Debug, Clone)] +pub struct Configuration { + pub base_path: String, + pub user_agent: Option, + pub client: {{#supportMiddleware}}reqwest_middleware::ClientWithMiddleware{{/supportMiddleware}}{{^supportMiddleware}}reqwest::Client{{/supportMiddleware}}, + {{^supportTokenSource}} + pub basic_auth: Option, + pub oauth_access_token: Option, + pub bearer_access_token: Option, + pub api_key: Option, + {{/supportTokenSource}} + {{#withAWSV4Signature}} + pub aws_v4_key: Option, + {{/withAWSV4Signature}} + {{#supportTokenSource}} + pub token_source: Arc, + {{/supportTokenSource}} +} +{{^supportTokenSource}} + +pub type BasicAuth = (String, Option); + +#[derive(Debug, Clone)] +pub struct ApiKey { + pub prefix: Option, + pub key: String, +} +{{/supportTokenSource}} + +{{#withAWSV4Signature}} +#[derive(Debug, Clone)] +pub struct AWSv4Key { + pub access_key: String, + pub secret_key: SecretString, + pub region: String, + pub service: String, +} + +impl AWSv4Key { + pub fn sign(&self, uri: &str, method: &str, body: &str) -> Result, aws_sigv4::http_request::Error> { + let request = http::Request::builder() + .uri(uri) + .method(method) + .body(body).unwrap(); + let signing_settings = SigningSettings::default(); + let signing_params = SigningParams::builder() + .access_key(self.access_key.as_str()) + .secret_key(self.secret_key.expose_secret().as_str()) + .region(self.region.as_str()) + .service_name(self.service.as_str()) + .time(SystemTime::now()) + .settings(signing_settings) + .build() + .unwrap(); + let signable_request = SignableRequest::from(&request); + let (mut signing_instructions, _signature) = sign(signable_request, &signing_params)?.into_parts(); + let mut additional_headers = Vec::<(String, String)>::new(); + if let Some(new_headers) = signing_instructions.take_headers() { + for (name, value) in new_headers.into_iter() { + additional_headers.push((name.expect("header should have name").to_string(), + value.to_str().expect("header value should be a string").to_string())); + } + } + Ok(additional_headers) + } +} +{{/withAWSV4Signature}} + +impl Configuration { + pub fn new() -> Configuration { + Configuration::default() + } +} + +impl Default for Configuration { + fn default() -> Self { + Configuration { + base_path: "{{{basePath}}}".to_owned(), + user_agent: {{#httpUserAgent}}Some("{{{.}}}".to_owned()){{/httpUserAgent}}{{^httpUserAgent}}Some("OpenAPI-Generator/{{{version}}}/rust".to_owned()){{/httpUserAgent}}, + client: {{#supportMiddleware}}reqwest_middleware::ClientBuilder::new(reqwest::Client::new()).build(){{/supportMiddleware}}{{^supportMiddleware}}reqwest::Client::new(){{/supportMiddleware}}, + {{^supportTokenSource}} + basic_auth: None, + oauth_access_token: None, + bearer_access_token: None, + api_key: None, + {{/supportTokenSource}} + {{#withAWSV4Signature}} + aws_v4_key: None, + {{/withAWSV4Signature}} + {{#supportTokenSource}} + token_source: Arc::new(NoopTokenSource{}), + {{/supportTokenSource}} + } + } +} +{{#supportTokenSource}} +#[derive(Debug)] +struct NoopTokenSource{} + +#[async_trait] +impl TokenSource for NoopTokenSource { + async fn token(&self) -> Result> { + panic!("This is dummy token source. You can use TokenSourceProvider from 'google_cloud_auth' crate, or any other compatible crate.") + } +} +{{/supportTokenSource}} diff --git a/scripts/rust-client-templates/reqwest/api.mustache b/scripts/rust-client-templates/reqwest/api.mustache new file mode 100644 index 00000000..837cbcd3 --- /dev/null +++ b/scripts/rust-client-templates/reqwest/api.mustache @@ -0,0 +1,471 @@ +{{>partial_header}} + +use reqwest; +use serde::{Deserialize, Serialize, de::Error as _}; +use crate::{apis::ResponseContent, models}; +use super::{Error, configuration, ContentType}; + +const DEFAULT_TOWER_TRACE_ID: &str = "(none)"; + +{{#operations}} +{{#operation}} +{{#vendorExtensions.x-group-parameters}} +{{#allParams}} +{{#-first}} +/// struct for passing parameters to the method [`{{operationId}}`] +#[derive(Clone, Debug)] +pub struct {{{operationIdCamelCase}}}Params { +{{/-first}} + {{#description}} + /// {{{.}}} + {{/description}} + pub {{{paramName}}}: {{! + ### Option Start + }}{{^required}}Option<{{/required}}{{#required}}{{#isNullable}}Option<{{/isNullable}}{{/required}}{{! + ### &str and Vec<&str> + }}{{^isUuid}}{{#isString}}{{#isArray}}Vec<{{/isArray}}String{{#isArray}}>{{/isArray}}{{/isString}}{{/isUuid}}{{! + ### UUIDs + }}{{#isUuid}}{{#isArray}}Vec<{{/isArray}}String{{#isArray}}>{{/isArray}}{{/isUuid}}{{! + ### Models and primative types + }}{{^isString}}{{^isUuid}}{{^isPrimitiveType}}{{^isContainer}}models::{{/isContainer}}{{/isPrimitiveType}}{{{dataType}}}{{/isUuid}}{{/isString}}{{! + ### Option End + }}{{^required}}>{{/required}}{{#required}}{{#isNullable}}>{{/isNullable}}{{/required}}{{! + ### Comma for next arguement + }}{{^-last}},{{/-last}} +{{#-last}} +} + +{{/-last}} +{{/allParams}} +{{/vendorExtensions.x-group-parameters}} +{{/operation}} +{{/operations}} + +{{#supportMultipleResponses}} +{{#operations}} +{{#operation}} +/// struct for typed successes of method [`{{operationId}}`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum {{{operationIdCamelCase}}}Success { + {{#responses}} + {{#is2xx}} + Status{{code}}({{#isEnum}}{{{enumName}}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}), + {{/is2xx}} + {{#is3xx}} + Status{{code}}({{#isEnum}}{{{enumName}}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}), + {{/is3xx}} + {{/responses}} + UnknownValue(serde_json::Value), +} + +{{/operation}} +{{/operations}} +{{/supportMultipleResponses}} +{{#operations}} +{{#operation}} +/// struct for typed errors of method [`{{operationId}}`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum {{{operationIdCamelCase}}}Error { + {{#responses}} + {{#is4xx}} + Status{{code}}({{#isEnum}}{{{enumName}}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}), + {{/is4xx}} + {{#is5xx}} + Status{{code}}({{#isEnum}}{{{enumName}}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}), + {{/is5xx}} + {{#isDefault}} + DefaultResponse({{#isEnum}}{{{enumName}}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}), + {{/isDefault}} + {{/responses}} + UnknownValue(serde_json::Value), +} + +{{/operation}} +{{/operations}} + +{{#operations}} +{{#operation}} +{{#description}} +/// {{{.}}} +{{/description}} +{{#notes}} +/// {{{.}}} +{{/notes}} +{{#vendorExtensions.x-group-parameters}} +pub {{#supportAsync}}async {{/supportAsync}}fn {{{operationId}}}(configuration: &configuration::Configuration{{#allParams}}{{#-first}}, {{! +### Params +}}params: {{{operationIdCamelCase}}}Params{{/-first}}{{/allParams}}{{! +### Function return type +}}) -> Result<{{! +### Response File Support +}}{{#isResponseFile}}{{#supportAsync}}reqwest::Response{{/supportAsync}}{{^supportAsync}}reqwest::blocking::Response{{/supportAsync}}{{/isResponseFile}}{{! +### Regular Responses +}}{{^isResponseFile}}{{! +### Multi response support +}}{{#supportMultipleResponses}}ResponseContent<{{{operationIdCamelCase}}}Success>{{/supportMultipleResponses}}{{! +### Regular return type +}}{{^supportMultipleResponses}}{{^returnType}}(){{/returnType}}{{{returnType}}}{{/supportMultipleResponses}}{{/isResponseFile}}{{! +### Error Type +}}, Error<{{{operationIdCamelCase}}}Error>> { +{{/vendorExtensions.x-group-parameters}} +{{^vendorExtensions.x-group-parameters}} +pub {{#supportAsync}}async {{/supportAsync}}fn {{{operationId}}}(configuration: &configuration::Configuration, {{#allParams}}{{{paramName}}}: {{! +### Option Start +}}{{^required}}Option<{{/required}}{{#required}}{{#isNullable}}Option<{{/isNullable}}{{/required}}{{! +### &str and Vec<&str> +}}{{#isString}}{{#isArray}}Vec<{{/isArray}}{{^isUuid}}&str{{/isUuid}}{{#isArray}}>{{/isArray}}{{/isString}}{{! +### UUIDs +}}{{#isUuid}}{{#isArray}}Vec<{{/isArray}}&str{{#isArray}}>{{/isArray}}{{/isUuid}}{{! +### Models and primative types +}}{{^isString}}{{^isUuid}}{{^isPrimitiveType}}{{^isContainer}}models::{{/isContainer}}{{/isPrimitiveType}}{{{dataType}}}{{/isUuid}}{{/isString}}{{! +### Option End +}}{{^required}}>{{/required}}{{#required}}{{#isNullable}}>{{/isNullable}}{{/required}}{{! +### Comma for next arguement +}}{{^-last}}, {{/-last}}{{/allParams}}{{! +### Function return type +}}) -> Result<{{! +### Response File Support +}}{{#isResponseFile}}{{#supportAsync}}reqwest::Response{{/supportAsync}}{{^supportAsync}}reqwest::blocking::Response{{/supportAsync}}{{/isResponseFile}}{{! +### Regular Responses +}}{{^isResponseFile}}{{! +### Multi response support +}}{{#supportMultipleResponses}}ResponseContent<{{{operationIdCamelCase}}}Success>{{/supportMultipleResponses}}{{! +### Regular return type +}}{{^supportMultipleResponses}}{{^returnType}}(){{/returnType}}{{{returnType}}}{{/supportMultipleResponses}}{{/isResponseFile}}{{! +### Error Type +}}, Error<{{{operationIdCamelCase}}}Error>> { + {{#allParams.0}} + // add a prefix to parameters to efficiently prevent name collisions + {{/allParams.0}} + {{#allParams}} + let {{{vendorExtensions.x-rust-param-identifier}}} = {{{paramName}}}; + {{/allParams}} +{{/vendorExtensions.x-group-parameters}} + + let uri_str = format!("{}{{{path}}}", configuration.base_path{{#pathParams}}, {{{baseName}}}={{#isString}}crate::apis::urlencode({{/isString}}{{{vendorExtensions.x-rust-param-identifier}}}{{^required}}.unwrap(){{/required}}{{#required}}{{#isNullable}}.unwrap(){{/isNullable}}{{/required}}{{#isArray}}.join(",").as_ref(){{/isArray}}{{^isString}}{{^isUuid}}{{^isPrimitiveType}}{{^isContainer}}.to_string(){{/isContainer}}{{/isPrimitiveType}}{{/isUuid}}{{/isString}}{{#isString}}){{/isString}}{{/pathParams}}); + let mut req_builder = configuration.client.request(reqwest::Method::{{{httpMethod}}}, &uri_str); + + {{#queryParams}} + {{#required}} + {{#isArray}} + req_builder = match "{{collectionFormat}}" { + "multi" => req_builder.query(&{{{vendorExtensions.x-rust-param-identifier}}}.into_iter().map(|p| ("{{{baseName}}}".to_owned(), p.to_string())).collect::>()), + _ => req_builder.query(&[("{{{baseName}}}", &{{{vendorExtensions.x-rust-param-identifier}}}.into_iter().map(|p| p.to_string()).collect::>().join(",").to_string())]), + }; + {{/isArray}} + {{^isArray}} + {{^isNullable}} + req_builder = req_builder.query(&[("{{{baseName}}}", &{{{vendorExtensions.x-rust-param-identifier}}}.to_string())]); + {{/isNullable}} + {{#isNullable}} + {{#isDeepObject}} + if let Some(ref param_value) = {{{vendorExtensions.x-rust-param-identifier}}} { + let params = crate::apis::parse_deep_object("{{{baseName}}}", param_value); + req_builder = req_builder.query(¶ms); + }; + {{/isDeepObject}} + {{^isDeepObject}} + if let Some(ref param_value) = {{{vendorExtensions.x-rust-param-identifier}}} { + req_builder = req_builder.query(&[("{{{baseName}}}", ¶m_value.to_string())]); + }; + {{/isDeepObject}} + {{/isNullable}} + {{/isArray}} + {{/required}} + {{^required}} + if let Some(ref param_value) = {{{vendorExtensions.x-rust-param-identifier}}} { + {{#isArray}} + req_builder = match "{{collectionFormat}}" { + "multi" => req_builder.query(¶m_value.into_iter().map(|p| ("{{{baseName}}}".to_owned(), p.to_string())).collect::>()), + _ => req_builder.query(&[("{{{baseName}}}", ¶m_value.into_iter().map(|p| p.to_string()).collect::>().join(",").to_string())]), + }; + {{/isArray}} + {{^isArray}} + {{#isDeepObject}} + let params = crate::apis::parse_deep_object("{{{baseName}}}", param_value); + req_builder = req_builder.query(¶ms); + {{/isDeepObject}} + {{^isDeepObject}} + req_builder = req_builder.query(&[("{{{baseName}}}", ¶m_value.to_string())]); + {{/isDeepObject}} + {{/isArray}} + } + {{/required}} + {{/queryParams}} + {{#hasAuthMethods}} + {{#authMethods}} + {{#isApiKey}} + {{#isKeyInQuery}} + if let Some(ref apikey) = configuration.api_key { + let key = apikey.key.clone(); + let value = match apikey.prefix { + Some(ref prefix) => format!("{} {}", prefix, key), + None => key, + }; + req_builder = req_builder.query(&[("{{{keyParamName}}}", value)]); + } + {{/isKeyInQuery}} + {{/isApiKey}} + {{/authMethods}} + {{/hasAuthMethods}} + {{#hasAuthMethods}} + {{#withAWSV4Signature}} + if let Some(ref aws_v4_key) = configuration.aws_v4_key { + let new_headers = match aws_v4_key.sign( + &uri_str, + "{{{httpMethod}}}", + {{#hasBodyParam}} + {{#bodyParams}} + &serde_json::to_string(&{{{vendorExtensions.x-rust-param-identifier}}}).expect("param should serialize to string"), + {{/bodyParams}} + {{/hasBodyParam}} + {{^hasBodyParam}} + "", + {{/hasBodyParam}} + ) { + Ok(new_headers) => new_headers, + Err(err) => return Err(Error::AWSV4SignatureError(err)), + }; + for (name, value) in new_headers.iter() { + req_builder = req_builder.header(name.as_str(), value.as_str()); + } + } + {{/withAWSV4Signature}} + {{/hasAuthMethods}} + if let Some(ref user_agent) = configuration.user_agent { + req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone()); + } + {{#hasHeaderParams}} + {{#headerParams}} + {{#required}} + {{^isNullable}} + req_builder = req_builder.header("{{{baseName}}}", {{{vendorExtensions.x-rust-param-identifier}}}{{#isArray}}.join(","){{/isArray}}.to_string()); + {{/isNullable}} + {{#isNullable}} + match {{{vendorExtensions.x-rust-param-identifier}}} { + Some(param_value) => { req_builder = req_builder.header("{{{baseName}}}", param_value{{#isArray}}.join(","){{/isArray}}.to_string()); }, + None => { req_builder = req_builder.header("{{{baseName}}}", ""); }, + } + {{/isNullable}} + {{/required}} + {{^required}} + if let Some(param_value) = {{{vendorExtensions.x-rust-param-identifier}}} { + req_builder = req_builder.header("{{{baseName}}}", param_value{{#isArray}}.join(","){{/isArray}}.to_string()); + } + {{/required}} + {{/headerParams}} + {{/hasHeaderParams}} + {{#hasAuthMethods}} + {{#authMethods}} + {{#supportTokenSource}} + // Obtain a token from source provider. + // Tokens can be Id or access tokens depending on the provider type and configuration. + let token = configuration.token_source.token().await.map_err(Error::TokenSource)?; + // The token format is the responsibility of the provider, thus we just set the authorization header with whatever is given. + req_builder = req_builder.header(reqwest::header::AUTHORIZATION, token); + {{/supportTokenSource}} + {{^supportTokenSource}} + {{#isApiKey}} + {{#isKeyInHeader}} + if let Some(ref apikey) = configuration.api_key { + let key = apikey.key.clone(); + let value = match apikey.prefix { + Some(ref prefix) => format!("{} {}", prefix, key), + None => key, + }; + req_builder = req_builder.header("{{{keyParamName}}}", value); + }; + {{/isKeyInHeader}} + {{/isApiKey}} + {{#isBasic}} + {{#isBasicBasic}} + if let Some(ref auth_conf) = configuration.basic_auth { + req_builder = req_builder.basic_auth(auth_conf.0.to_owned(), auth_conf.1.to_owned()); + }; + {{/isBasicBasic}} + {{#isBasicBearer}} + if let Some(ref token) = configuration.bearer_access_token { + req_builder = req_builder.bearer_auth(token.to_owned()); + }; + {{/isBasicBearer}} + {{/isBasic}} + {{#isOAuth}} + if let Some(ref token) = configuration.oauth_access_token { + req_builder = req_builder.bearer_auth(token.to_owned()); + }; + {{/isOAuth}} + {{/supportTokenSource}} + {{/authMethods}} + {{/hasAuthMethods}} + {{#isMultipart}} + {{#hasFormParams}} + let mut multipart_form = reqwest{{^supportAsync}}::blocking{{/supportAsync}}::multipart::Form::new(); + {{#formParams}} + {{#isFile}} + {{^supportAsync}} + {{#required}} + {{^isNullable}} + multipart_form = multipart_form.file("{{{baseName}}}", {{{vendorExtensions.x-rust-param-identifier}}})?; + {{/isNullable}} + {{#isNullable}} + match {{{vendorExtensions.x-rust-param-identifier}}} { + Some(param_value) => { multipart_form = multipart_form.file("{{{baseName}}}", param_value)?; }, + None => { unimplemented!("Required nullable form file param not supported"); }, + } + {{/isNullable}} + {{/required}} + {{^required}} + if let Some(param_value) = {{{vendorExtensions.x-rust-param-identifier}}} { + multipart_form = multipart_form.file("{{{baseName}}}", param_value)?; + } + {{/required}} + {{/supportAsync}} + {{#supportAsync}} + // TODO: support file upload for '{{{baseName}}}' parameter + {{/supportAsync}} + {{/isFile}} + {{^isFile}} + {{#required}} + {{^isNullable}} + multipart_form = multipart_form.text("{{{baseName}}}", {{{vendorExtensions.x-rust-param-identifier}}}{{#isArray}}.into_iter().map(|p| p.to_string()).collect::>().join(","){{/isArray}}.to_string()); + {{/isNullable}} + {{#isNullable}} + match {{{vendorExtensions.x-rust-param-identifier}}} { + Some(param_value) => { multipart_form = multipart_form.text("{{{baseName}}}", param_value{{#isArray}}.into_iter().map(|p| p.to_string()).collect::>().join(","){{/isArray}}.to_string()); }, + None => { multipart_form = multipart_form.text("{{{baseName}}}", ""); }, + } + {{/isNullable}} + {{/required}} + {{^required}} + if let Some(param_value) = {{{vendorExtensions.x-rust-param-identifier}}} { + multipart_form = multipart_form.text("{{{baseName}}}", param_value{{#isArray}}.into_iter().map(|p| p.to_string()).collect::>().join(","){{/isArray}}.to_string()); + } + {{/required}} + {{/isFile}} + {{/formParams}} + req_builder = req_builder.multipart(multipart_form); + {{/hasFormParams}} + {{/isMultipart}} + {{^isMultipart}} + {{#hasFormParams}} + let mut multipart_form_params = std::collections::HashMap::new(); + {{#formParams}} + {{#isFile}} + {{#required}} + {{^isNullable}} + multipart_form_params.insert("{{{baseName}}}", unimplemented!("File form param not supported with x-www-form-urlencoded content")); + {{/isNullable}} + {{#isNullable}} + match {{{vendorExtensions.x-rust-param-identifier}}} { + Some(param_value) => { multipart_form_params.insert("{{{baseName}}}", unimplemented!("File form param not supported with x-www-form-urlencoded content")); }, + None => { unimplemented!("Required nullable file form param not supported with x-www-form-urlencoded content"); }, + } + {{/isNullable}} + {{/required}} + {{^required}} + if let Some(param_value) = {{{vendorExtensions.x-rust-param-identifier}}} { + multipart_form_params.insert("{{{baseName}}}", unimplemented!("File form param not supported with x-www-form-urlencoded content")); + } + {{/required}} + {{/isFile}} + {{^isFile}} + {{#required}} + {{^isNullable}} + multipart_form_params.insert("{{{baseName}}}", {{{vendorExtensions.x-rust-param-identifier}}}{{#isArray}}.into_iter().map(|p| p.to_string()).collect::>().join(","){{/isArray}}.to_string()); + {{/isNullable}} + {{#isNullable}} + match {{{vendorExtensions.x-rust-param-identifier}}} { + Some(param_value) => { multipart_form_params.insert("{{{baseName}}}", param_value{{#isArray}}.into_iter().map(|p| p.to_string()).collect::>().join(","){{/isArray}}.to_string()); }, + None => { multipart_form_params.insert("{{{baseName}}}", ""); }, + } + {{/isNullable}} + {{/required}} + {{^required}} + if let Some(param_value) = {{{vendorExtensions.x-rust-param-identifier}}} { + multipart_form_params.insert("{{{baseName}}}", param_value{{#isArray}}.into_iter().map(|p| p.to_string()).collect::>().join(","){{/isArray}}.to_string()); + } + {{/required}} + {{/isFile}} + {{/formParams}} + req_builder = req_builder.form(&multipart_form_params); + {{/hasFormParams}} + {{/isMultipart}} + {{#hasBodyParam}} + {{#bodyParams}} + {{#isFile}} + req_builder = req_builder.body({{{vendorExtensions.x-rust-param-identifier}}}); + {{/isFile}} + {{^isFile}} + req_builder = req_builder.json(&{{{vendorExtensions.x-rust-param-identifier}}}); + {{/isFile}} + {{/bodyParams}} + {{/hasBodyParam}} + + let req = req_builder.build()?; + let resp = configuration.client.execute(req){{#supportAsync}}.await{{/supportAsync}}?; + + let status = resp.status(); + {{^supportMultipleResponses}} + {{^isResponseFile}} + {{#returnType}} + let content_type = resp + .headers() + .get("content-type") + .and_then(|v| v.to_str().ok()) + .unwrap_or("application/octet-stream"); + let content_type = super::ContentType::from(content_type); + {{/returnType}} + {{/isResponseFile}} + {{/supportMultipleResponses}} + + let tower_trace_id = resp + .headers() + .get("x-tower-trace-id") + .and_then(|v| v.to_str().ok()) + .map_or(String::from(DEFAULT_TOWER_TRACE_ID), String::from); + + if !status.is_client_error() && !status.is_server_error() { + {{^supportMultipleResponses}} + {{#isResponseFile}} + Ok(resp) + {{/isResponseFile}} + {{^isResponseFile}} + {{^returnType}} + Ok(()) + {{/returnType}} + {{#returnType}} + let content = resp.text(){{#supportAsync}}.await{{/supportAsync}}?; + match content_type { + ContentType::Json => serde_json::from_str(&content).map_err(Error::from), + {{#vendorExtensions.x-supports-plain-text}} + ContentType::Text => return Ok(content), + {{/vendorExtensions.x-supports-plain-text}} + {{^vendorExtensions.x-supports-plain-text}} + ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `{{returnType}}`"))), + {{/vendorExtensions.x-supports-plain-text}} + ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `{{returnType}}`")))), + } + {{/returnType}} + {{/isResponseFile}} + {{/supportMultipleResponses}} + {{#supportMultipleResponses}} + {{#isResponseFile}} + Ok(resp) + {{/isResponseFile}} + {{^isResponseFile}} + let content = resp.text(){{#supportAsync}}.await{{/supportAsync}}?; + let entity: Option<{{{operationIdCamelCase}}}Success> = serde_json::from_str(&content).ok(); + Ok(ResponseContent { tower_trace_id, status, content, entity }) + {{/isResponseFile}} + {{/supportMultipleResponses}} + } else { + let content = resp.text(){{#supportAsync}}.await{{/supportAsync}}?; + let entity: Option<{{{operationIdCamelCase}}}Error> = serde_json::from_str(&content).ok(); + Err(Error::ResponseError(ResponseContent { tower_trace_id, status, content, entity })) + } +} + +{{/operation}} +{{/operations}} diff --git a/scripts/rust-client-templates/reqwest/api_mod.mustache b/scripts/rust-client-templates/reqwest/api_mod.mustache new file mode 100644 index 00000000..f411c368 --- /dev/null +++ b/scripts/rust-client-templates/reqwest/api_mod.mustache @@ -0,0 +1,165 @@ +use std::error; +use std::fmt; +{{#withAWSV4Signature}} +use aws_sigv4; +{{/withAWSV4Signature}} + +#[derive(Debug, Clone)] +pub struct ResponseContent { + pub tower_trace_id: String, + pub status: reqwest::StatusCode, + pub content: String, + pub entity: Option, +} + +#[derive(Debug)] +pub enum Error { + Reqwest(reqwest::Error), + {{#supportMiddleware}} + ReqwestMiddleware(reqwest_middleware::Error), + {{/supportMiddleware}} + Serde(serde_json::Error), + Io(std::io::Error), + ResponseError(ResponseContent), + {{#withAWSV4Signature}} + AWSV4SignatureError(aws_sigv4::http_request::Error), + {{/withAWSV4Signature}} + {{#supportAsync}} + {{#supportTokenSource}} + TokenSource(Box), + {{/supportTokenSource}} + {{/supportAsync}} +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let (module, e) = match self { + Error::Reqwest(e) => ("reqwest", e.to_string()), + {{#supportMiddleware}} + Error::ReqwestMiddleware(e) => ("reqwest-middleware", e.to_string()), + {{/supportMiddleware}} + Error::Serde(e) => ("serde", e.to_string()), + Error::Io(e) => ("IO", e.to_string()), + Error::ResponseError(e) => ("response", format!("status code {}", e.status)), + {{#withAWSV4Signature}} + Error::AWSV4SignatureError(e) => ("aws v4 signature", e.to_string()), + {{/withAWSV4Signature}} + {{#supportAsync}} + {{#supportTokenSource}} + Error::TokenSource(e) => ("token source failure", e.to_string()), + {{/supportTokenSource}} + {{/supportAsync}} + }; + write!(f, "error in {}: {}", module, e) + } +} + +impl error::Error for Error { + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + Some(match self { + Error::Reqwest(e) => e, + {{#supportMiddleware}} + Error::ReqwestMiddleware(e) => e, + {{/supportMiddleware}} + Error::Serde(e) => e, + Error::Io(e) => e, + Error::ResponseError(_) => return None, + {{#withAWSV4Signature}} + Error::AWSV4SignatureError(_) => return None, + {{/withAWSV4Signature}} + {{#supportAsync}} + {{#supportTokenSource}} + Error::TokenSource(e) => &**e, + {{/supportTokenSource}} + {{/supportAsync}} + }) + } +} + +impl From for Error { + fn from(e: reqwest::Error) -> Self { + Error::Reqwest(e) + } +} + +{{#supportMiddleware}} +impl From for Error { + fn from(e: reqwest_middleware::Error) -> Self { + Error::ReqwestMiddleware(e) + } +} + +{{/supportMiddleware}} +impl From for Error { + fn from(e: serde_json::Error) -> Self { + Error::Serde(e) + } +} + +impl From for Error { + fn from(e: std::io::Error) -> Self { + Error::Io(e) + } +} + +pub fn urlencode>(s: T) -> String { + ::url::form_urlencoded::byte_serialize(s.as_ref().as_bytes()).collect() +} + +pub fn parse_deep_object(prefix: &str, value: &serde_json::Value) -> Vec<(String, String)> { + if let serde_json::Value::Object(object) = value { + let mut params = vec![]; + + for (key, value) in object { + match value { + serde_json::Value::Object(_) => params.append(&mut parse_deep_object( + &format!("{}[{}]", prefix, key), + value, + )), + serde_json::Value::Array(array) => { + for (i, value) in array.iter().enumerate() { + params.append(&mut parse_deep_object( + &format!("{}[{}][{}]", prefix, key, i), + value, + )); + } + }, + serde_json::Value::String(s) => params.push((format!("{}[{}]", prefix, key), s.clone())), + _ => params.push((format!("{}[{}]", prefix, key), value.to_string())), + } + } + + return params; + } + + unimplemented!("Only objects are supported with style=deepObject") +} + +/// Internal use only +/// A content type supported by this client. +#[allow(dead_code)] +enum ContentType { + Json, + Text, + Unsupported(String) +} + +impl From<&str> for ContentType { + fn from(content_type: &str) -> Self { + if content_type.starts_with("application") && content_type.contains("json") { + return Self::Json; + } else if content_type.starts_with("text/plain") { + return Self::Text; + } else { + return Self::Unsupported(content_type.to_string()); + } + } +} + +{{#apiInfo}} +{{#apis}} +pub mod {{{classFilename}}}; +{{/apis}} +{{/apiInfo}} + +pub mod configuration; diff --git a/scripts/rust-client-templates/reqwest/configuration.mustache b/scripts/rust-client-templates/reqwest/configuration.mustache new file mode 100644 index 00000000..e29c73b1 --- /dev/null +++ b/scripts/rust-client-templates/reqwest/configuration.mustache @@ -0,0 +1,122 @@ +{{>partial_header}} + +{{#withAWSV4Signature}} +use std::time::SystemTime; +use aws_sigv4::http_request::{sign, SigningSettings, SigningParams, SignableRequest}; +use http; +use secrecy::{SecretString, ExposeSecret}; +{{/withAWSV4Signature}} +{{#supportTokenSource}} +use std::sync::Arc; +use google_cloud_token::TokenSource; +use async_trait::async_trait; +{{/supportTokenSource}} + +#[derive(Debug, Clone)] +pub struct Configuration { + pub base_path: String, + pub user_agent: Option, + pub client: {{#supportMiddleware}}reqwest_middleware::ClientWithMiddleware{{/supportMiddleware}}{{^supportMiddleware}}reqwest{{^supportAsync}}::blocking{{/supportAsync}}::Client{{/supportMiddleware}}, + {{^supportTokenSource}} + pub basic_auth: Option, + pub oauth_access_token: Option, + pub bearer_access_token: Option, + pub api_key: Option, + {{/supportTokenSource}} + {{#withAWSV4Signature}} + pub aws_v4_key: Option, + {{/withAWSV4Signature}} + {{#supportAsync}} + {{#supportTokenSource}} + pub token_source: Arc, + {{/supportTokenSource}} + {{/supportAsync}} +} +{{^supportTokenSource}} + +pub type BasicAuth = (String, Option); + +#[derive(Debug, Clone)] +pub struct ApiKey { + pub prefix: Option, + pub key: String, +} +{{/supportTokenSource}} + +{{#withAWSV4Signature}} +#[derive(Debug, Clone)] +pub struct AWSv4Key { + pub access_key: String, + pub secret_key: SecretString, + pub region: String, + pub service: String, +} + +impl AWSv4Key { + pub fn sign(&self, uri: &str, method: &str, body: &str) -> Result, aws_sigv4::http_request::Error> { + let request = http::Request::builder() + .uri(uri) + .method(method) + .body(body).unwrap(); + let signing_settings = SigningSettings::default(); + let signing_params = SigningParams::builder() + .access_key(self.access_key.as_str()) + .secret_key(self.secret_key.expose_secret().as_str()) + .region(self.region.as_str()) + .service_name(self.service.as_str()) + .time(SystemTime::now()) + .settings(signing_settings) + .build() + .unwrap(); + let signable_request = SignableRequest::from(&request); + let (mut signing_instructions, _signature) = sign(signable_request, &signing_params)?.into_parts(); + let mut additional_headers = Vec::<(String, String)>::new(); + if let Some(new_headers) = signing_instructions.take_headers() { + for (name, value) in new_headers.into_iter() { + additional_headers.push((name.expect("header should have name").to_string(), + value.to_str().expect("header value should be a string").to_string())); + } + } + Ok(additional_headers) + } +} +{{/withAWSV4Signature}} + +impl Configuration { + pub fn new() -> Configuration { + Configuration::default() + } +} + +impl Default for Configuration { + fn default() -> Self { + Configuration { + base_path: "{{{basePath}}}".to_owned(), + user_agent: {{#httpUserAgent}}Some("{{{.}}}".to_owned()){{/httpUserAgent}}{{^httpUserAgent}}Some("OpenAPI-Generator/{{{version}}}/rust".to_owned()){{/httpUserAgent}}, + client: {{#supportMiddleware}}reqwest_middleware::ClientBuilder::new(reqwest{{^supportAsync}}::blocking{{/supportAsync}}::Client::new()).build(){{/supportMiddleware}}{{^supportMiddleware}}reqwest{{^supportAsync}}::blocking{{/supportAsync}}::Client::new(){{/supportMiddleware}}, + {{^supportTokenSource}} + basic_auth: None, + oauth_access_token: None, + bearer_access_token: None, + api_key: None, + {{/supportTokenSource}} + {{#withAWSV4Signature}} + aws_v4_key: None, + {{/withAWSV4Signature}} + {{#supportTokenSource}} + token_source: Arc::new(NoopTokenSource{}), + {{/supportTokenSource}} + } + } +} +{{#supportTokenSource}} +#[derive(Debug)] +struct NoopTokenSource{} + +#[async_trait] +impl TokenSource for NoopTokenSource { + async fn token(&self) -> Result> { + panic!("This is dummy token source. You can use TokenSourceProvider from 'google_cloud_auth' crate, or any other compatible crate.") + } +} +{{/supportTokenSource}} diff --git a/src/tower/tower_api_client/api/default/acknowledge_alert.py b/src/tower/tower_api_client/api/default/acknowledge_alert.py new file mode 100644 index 00000000..6723ac2d --- /dev/null +++ b/src/tower/tower_api_client/api/default/acknowledge_alert.py @@ -0,0 +1,160 @@ +from http import HTTPStatus +from typing import Any, Optional, Union + +import httpx + +from ... import errors +from ...client import AuthenticatedClient, Client +from ...models.acknowledge_alert_response import AcknowledgeAlertResponse +from ...types import Response + + +def _get_kwargs( + alert_id: str, +) -> dict[str, Any]: + _kwargs: dict[str, Any] = { + "method": "post", + "url": "/alerts/{alert_id}/acknowledge".format( + alert_id=alert_id, + ), + } + + return _kwargs + + +def _parse_response( + *, client: Union[AuthenticatedClient, Client], response: httpx.Response +) -> Optional[AcknowledgeAlertResponse]: + if response.status_code == 200: + response_200 = AcknowledgeAlertResponse.from_dict(response.json()) + + return response_200 + if client.raise_on_unexpected_status: + raise errors.UnexpectedStatus(response.status_code, response.content) + else: + return None + + +def _build_response( + *, client: Union[AuthenticatedClient, Client], response: httpx.Response +) -> Response[AcknowledgeAlertResponse]: + return Response( + status_code=HTTPStatus(response.status_code), + content=response.content, + headers=response.headers, + parsed=_parse_response(client=client, response=response), + ) + + +def sync_detailed( + alert_id: str, + *, + client: AuthenticatedClient, +) -> Response[AcknowledgeAlertResponse]: + """Acknowledge alert + + Mark an alert as acknowledged + + Args: + alert_id (str): ID of the alert to acknowledge + + Raises: + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. + httpx.TimeoutException: If the request takes longer than Client.timeout. + + Returns: + Response[AcknowledgeAlertResponse] + """ + + kwargs = _get_kwargs( + alert_id=alert_id, + ) + + response = client.get_httpx_client().request( + **kwargs, + ) + + return _build_response(client=client, response=response) + + +def sync( + alert_id: str, + *, + client: AuthenticatedClient, +) -> Optional[AcknowledgeAlertResponse]: + """Acknowledge alert + + Mark an alert as acknowledged + + Args: + alert_id (str): ID of the alert to acknowledge + + Raises: + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. + httpx.TimeoutException: If the request takes longer than Client.timeout. + + Returns: + AcknowledgeAlertResponse + """ + + return sync_detailed( + alert_id=alert_id, + client=client, + ).parsed + + +async def asyncio_detailed( + alert_id: str, + *, + client: AuthenticatedClient, +) -> Response[AcknowledgeAlertResponse]: + """Acknowledge alert + + Mark an alert as acknowledged + + Args: + alert_id (str): ID of the alert to acknowledge + + Raises: + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. + httpx.TimeoutException: If the request takes longer than Client.timeout. + + Returns: + Response[AcknowledgeAlertResponse] + """ + + kwargs = _get_kwargs( + alert_id=alert_id, + ) + + response = await client.get_async_httpx_client().request(**kwargs) + + return _build_response(client=client, response=response) + + +async def asyncio( + alert_id: str, + *, + client: AuthenticatedClient, +) -> Optional[AcknowledgeAlertResponse]: + """Acknowledge alert + + Mark an alert as acknowledged + + Args: + alert_id (str): ID of the alert to acknowledge + + Raises: + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. + httpx.TimeoutException: If the request takes longer than Client.timeout. + + Returns: + AcknowledgeAlertResponse + """ + + return ( + await asyncio_detailed( + alert_id=alert_id, + client=client, + ) + ).parsed diff --git a/src/tower/tower_api_client/api/default/delete_alert.py b/src/tower/tower_api_client/api/default/delete_alert.py new file mode 100644 index 00000000..0881d00c --- /dev/null +++ b/src/tower/tower_api_client/api/default/delete_alert.py @@ -0,0 +1,103 @@ +from http import HTTPStatus +from typing import Any, Optional, Union + +import httpx + +from ... import errors +from ...client import AuthenticatedClient, Client +from ...types import Response + + +def _get_kwargs( + alert_id: str, +) -> dict[str, Any]: + _kwargs: dict[str, Any] = { + "method": "delete", + "url": "/alerts/{alert_id}".format( + alert_id=alert_id, + ), + } + + return _kwargs + + +def _parse_response( + *, client: Union[AuthenticatedClient, Client], response: httpx.Response +) -> Optional[Any]: + if response.status_code == 204: + return None + if client.raise_on_unexpected_status: + raise errors.UnexpectedStatus(response.status_code, response.content) + else: + return None + + +def _build_response( + *, client: Union[AuthenticatedClient, Client], response: httpx.Response +) -> Response[Any]: + return Response( + status_code=HTTPStatus(response.status_code), + content=response.content, + headers=response.headers, + parsed=_parse_response(client=client, response=response), + ) + + +def sync_detailed( + alert_id: str, + *, + client: AuthenticatedClient, +) -> Response[Any]: + """Delete alert + + Permanently delete an alert + + Args: + alert_id (str): ID of the alert to delete + + Raises: + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. + httpx.TimeoutException: If the request takes longer than Client.timeout. + + Returns: + Response[Any] + """ + + kwargs = _get_kwargs( + alert_id=alert_id, + ) + + response = client.get_httpx_client().request( + **kwargs, + ) + + return _build_response(client=client, response=response) + + +async def asyncio_detailed( + alert_id: str, + *, + client: AuthenticatedClient, +) -> Response[Any]: + """Delete alert + + Permanently delete an alert + + Args: + alert_id (str): ID of the alert to delete + + Raises: + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. + httpx.TimeoutException: If the request takes longer than Client.timeout. + + Returns: + Response[Any] + """ + + kwargs = _get_kwargs( + alert_id=alert_id, + ) + + response = await client.get_async_httpx_client().request(**kwargs) + + return _build_response(client=client, response=response) diff --git a/src/tower/tower_api_client/api/default/list_alerts.py b/src/tower/tower_api_client/api/default/list_alerts.py new file mode 100644 index 00000000..1157cfd2 --- /dev/null +++ b/src/tower/tower_api_client/api/default/list_alerts.py @@ -0,0 +1,260 @@ +import datetime +from http import HTTPStatus +from typing import Any, Optional, Union + +import httpx + +from ... import errors +from ...client import AuthenticatedClient, Client +from ...models.list_alerts_response import ListAlertsResponse +from ...types import UNSET, Response, Unset + + +def _get_kwargs( + *, + alert_type: Union[Unset, str] = UNSET, + acked: Union[Unset, bool] = UNSET, + start_at: Union[Unset, datetime.datetime] = UNSET, + end_at: Union[Unset, datetime.datetime] = UNSET, + page: Union[Unset, int] = UNSET, + page_size: Union[Unset, int] = UNSET, +) -> dict[str, Any]: + params: dict[str, Any] = {} + + params["alert_type"] = alert_type + + params["acked"] = acked + + json_start_at: Union[Unset, str] = UNSET + if not isinstance(start_at, Unset): + json_start_at = start_at.isoformat() + params["start_at"] = json_start_at + + json_end_at: Union[Unset, str] = UNSET + if not isinstance(end_at, Unset): + json_end_at = end_at.isoformat() + params["end_at"] = json_end_at + + params["page"] = page + + params["page_size"] = page_size + + params = {k: v for k, v in params.items() if v is not UNSET and v is not None} + + _kwargs: dict[str, Any] = { + "method": "get", + "url": "/alerts", + "params": params, + } + + return _kwargs + + +def _parse_response( + *, client: Union[AuthenticatedClient, Client], response: httpx.Response +) -> Optional[ListAlertsResponse]: + if response.status_code == 200: + response_200 = ListAlertsResponse.from_dict(response.json()) + + return response_200 + if client.raise_on_unexpected_status: + raise errors.UnexpectedStatus(response.status_code, response.content) + else: + return None + + +def _build_response( + *, client: Union[AuthenticatedClient, Client], response: httpx.Response +) -> Response[ListAlertsResponse]: + return Response( + status_code=HTTPStatus(response.status_code), + content=response.content, + headers=response.headers, + parsed=_parse_response(client=client, response=response), + ) + + +def sync_detailed( + *, + client: AuthenticatedClient, + alert_type: Union[Unset, str] = UNSET, + acked: Union[Unset, bool] = UNSET, + start_at: Union[Unset, datetime.datetime] = UNSET, + end_at: Union[Unset, datetime.datetime] = UNSET, + page: Union[Unset, int] = UNSET, + page_size: Union[Unset, int] = UNSET, +) -> Response[ListAlertsResponse]: + """List alerts + + List alerts for the current account with optional filtering + + Args: + alert_type (Union[Unset, str]): Filter alerts by alert type + acked (Union[Unset, bool]): Filter by acknowledgement status (true=acknowledged, + false=unacknowledged) + start_at (Union[Unset, datetime.datetime]): Filter alerts created after or at this + datetime (inclusive) + end_at (Union[Unset, datetime.datetime]): Filter alerts created before or at this datetime + (inclusive) + page (Union[Unset, int]): The page number to fetch. + page_size (Union[Unset, int]): The number of records to fetch on each page. + + Raises: + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. + httpx.TimeoutException: If the request takes longer than Client.timeout. + + Returns: + Response[ListAlertsResponse] + """ + + kwargs = _get_kwargs( + alert_type=alert_type, + acked=acked, + start_at=start_at, + end_at=end_at, + page=page, + page_size=page_size, + ) + + response = client.get_httpx_client().request( + **kwargs, + ) + + return _build_response(client=client, response=response) + + +def sync( + *, + client: AuthenticatedClient, + alert_type: Union[Unset, str] = UNSET, + acked: Union[Unset, bool] = UNSET, + start_at: Union[Unset, datetime.datetime] = UNSET, + end_at: Union[Unset, datetime.datetime] = UNSET, + page: Union[Unset, int] = UNSET, + page_size: Union[Unset, int] = UNSET, +) -> Optional[ListAlertsResponse]: + """List alerts + + List alerts for the current account with optional filtering + + Args: + alert_type (Union[Unset, str]): Filter alerts by alert type + acked (Union[Unset, bool]): Filter by acknowledgement status (true=acknowledged, + false=unacknowledged) + start_at (Union[Unset, datetime.datetime]): Filter alerts created after or at this + datetime (inclusive) + end_at (Union[Unset, datetime.datetime]): Filter alerts created before or at this datetime + (inclusive) + page (Union[Unset, int]): The page number to fetch. + page_size (Union[Unset, int]): The number of records to fetch on each page. + + Raises: + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. + httpx.TimeoutException: If the request takes longer than Client.timeout. + + Returns: + ListAlertsResponse + """ + + return sync_detailed( + client=client, + alert_type=alert_type, + acked=acked, + start_at=start_at, + end_at=end_at, + page=page, + page_size=page_size, + ).parsed + + +async def asyncio_detailed( + *, + client: AuthenticatedClient, + alert_type: Union[Unset, str] = UNSET, + acked: Union[Unset, bool] = UNSET, + start_at: Union[Unset, datetime.datetime] = UNSET, + end_at: Union[Unset, datetime.datetime] = UNSET, + page: Union[Unset, int] = UNSET, + page_size: Union[Unset, int] = UNSET, +) -> Response[ListAlertsResponse]: + """List alerts + + List alerts for the current account with optional filtering + + Args: + alert_type (Union[Unset, str]): Filter alerts by alert type + acked (Union[Unset, bool]): Filter by acknowledgement status (true=acknowledged, + false=unacknowledged) + start_at (Union[Unset, datetime.datetime]): Filter alerts created after or at this + datetime (inclusive) + end_at (Union[Unset, datetime.datetime]): Filter alerts created before or at this datetime + (inclusive) + page (Union[Unset, int]): The page number to fetch. + page_size (Union[Unset, int]): The number of records to fetch on each page. + + Raises: + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. + httpx.TimeoutException: If the request takes longer than Client.timeout. + + Returns: + Response[ListAlertsResponse] + """ + + kwargs = _get_kwargs( + alert_type=alert_type, + acked=acked, + start_at=start_at, + end_at=end_at, + page=page, + page_size=page_size, + ) + + response = await client.get_async_httpx_client().request(**kwargs) + + return _build_response(client=client, response=response) + + +async def asyncio( + *, + client: AuthenticatedClient, + alert_type: Union[Unset, str] = UNSET, + acked: Union[Unset, bool] = UNSET, + start_at: Union[Unset, datetime.datetime] = UNSET, + end_at: Union[Unset, datetime.datetime] = UNSET, + page: Union[Unset, int] = UNSET, + page_size: Union[Unset, int] = UNSET, +) -> Optional[ListAlertsResponse]: + """List alerts + + List alerts for the current account with optional filtering + + Args: + alert_type (Union[Unset, str]): Filter alerts by alert type + acked (Union[Unset, bool]): Filter by acknowledgement status (true=acknowledged, + false=unacknowledged) + start_at (Union[Unset, datetime.datetime]): Filter alerts created after or at this + datetime (inclusive) + end_at (Union[Unset, datetime.datetime]): Filter alerts created before or at this datetime + (inclusive) + page (Union[Unset, int]): The page number to fetch. + page_size (Union[Unset, int]): The number of records to fetch on each page. + + Raises: + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. + httpx.TimeoutException: If the request takes longer than Client.timeout. + + Returns: + ListAlertsResponse + """ + + return ( + await asyncio_detailed( + client=client, + alert_type=alert_type, + acked=acked, + start_at=start_at, + end_at=end_at, + page=page, + page_size=page_size, + ) + ).parsed diff --git a/src/tower/tower_api_client/models/__init__.py b/src/tower/tower_api_client/models/__init__.py index 2a83e2a2..2f55e087 100644 --- a/src/tower/tower_api_client/models/__init__.py +++ b/src/tower/tower_api_client/models/__init__.py @@ -3,6 +3,11 @@ from .accept_invitation_params import AcceptInvitationParams from .accept_invitation_response import AcceptInvitationResponse from .account import Account +from .acknowledge_alert_response import AcknowledgeAlertResponse +from .alert import Alert +from .alert_alert_type import AlertAlertType +from .alert_detail import AlertDetail +from .alert_status import AlertStatus from .api_key import APIKey from .app import App from .app_statistics import AppStatistics @@ -58,6 +63,7 @@ from .invite_team_member_params import InviteTeamMemberParams from .invite_team_member_response import InviteTeamMemberResponse from .leave_team_response import LeaveTeamResponse +from .list_alerts_response import ListAlertsResponse from .list_api_keys_response import ListAPIKeysResponse from .list_app_environments_response import ListAppEnvironmentsResponse from .list_app_versions_response import ListAppVersionsResponse @@ -88,6 +94,7 @@ from .run_app_params_parameters import RunAppParamsParameters from .run_app_response import RunAppResponse from .run_log_line import RunLogLine +from .run_parameter import RunParameter from .run_results import RunResults from .run_statistics import RunStatistics from .run_status import RunStatus @@ -119,6 +126,11 @@ "AcceptInvitationParams", "AcceptInvitationResponse", "Account", + "AcknowledgeAlertResponse", + "Alert", + "AlertAlertType", + "AlertDetail", + "AlertStatus", "APIKey", "App", "AppStatistics", @@ -174,6 +186,7 @@ "InviteTeamMemberParams", "InviteTeamMemberResponse", "LeaveTeamResponse", + "ListAlertsResponse", "ListAPIKeysResponse", "ListAppEnvironmentsResponse", "ListAppsResponse", @@ -204,6 +217,7 @@ "RunAppParamsParameters", "RunAppResponse", "RunLogLine", + "RunParameter", "RunResults", "RunStatistics", "RunStatus", diff --git a/src/tower/tower_api_client/models/acknowledge_alert_response.py b/src/tower/tower_api_client/models/acknowledge_alert_response.py new file mode 100644 index 00000000..7f0fb469 --- /dev/null +++ b/src/tower/tower_api_client/models/acknowledge_alert_response.py @@ -0,0 +1,51 @@ +from collections.abc import Mapping +from typing import Any, TypeVar, Union + +from attrs import define as _attrs_define + +from ..types import UNSET, Unset + +T = TypeVar("T", bound="AcknowledgeAlertResponse") + + +@_attrs_define +class AcknowledgeAlertResponse: + """ + Attributes: + status (str): + schema (Union[Unset, str]): A URL to the JSON Schema for this object. Example: + https://api.tower.dev/v1/schemas/AcknowledgeAlertResponse.json. + """ + + status: str + schema: Union[Unset, str] = UNSET + + def to_dict(self) -> dict[str, Any]: + status = self.status + + schema = self.schema + + field_dict: dict[str, Any] = {} + field_dict.update( + { + "status": status, + } + ) + if schema is not UNSET: + field_dict["$schema"] = schema + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + d = dict(src_dict) + status = d.pop("status") + + schema = d.pop("$schema", UNSET) + + acknowledge_alert_response = cls( + status=status, + schema=schema, + ) + + return acknowledge_alert_response diff --git a/src/tower/tower_api_client/models/alert.py b/src/tower/tower_api_client/models/alert.py new file mode 100644 index 00000000..4882aa9e --- /dev/null +++ b/src/tower/tower_api_client/models/alert.py @@ -0,0 +1,90 @@ +import datetime +from collections.abc import Mapping +from typing import TYPE_CHECKING, Any, TypeVar + +from attrs import define as _attrs_define +from dateutil.parser import isoparse + +from ..models.alert_alert_type import AlertAlertType +from ..models.alert_status import AlertStatus + +if TYPE_CHECKING: + from ..models.alert_detail import AlertDetail + + +T = TypeVar("T", bound="Alert") + + +@_attrs_define +class Alert: + """ + Attributes: + alert_type (AlertAlertType): Type of the alert + created_at (datetime.datetime): Time when the alert was created + details (list['AlertDetail']): Detailed description of the alert + id (str): Unique identifier for the alert + status (AlertStatus): Status of the alert + """ + + alert_type: AlertAlertType + created_at: datetime.datetime + details: list["AlertDetail"] + id: str + status: AlertStatus + + def to_dict(self) -> dict[str, Any]: + alert_type = self.alert_type.value + + created_at = self.created_at.isoformat() + + details = [] + for details_item_data in self.details: + details_item = details_item_data.to_dict() + details.append(details_item) + + id = self.id + + status = self.status.value + + field_dict: dict[str, Any] = {} + field_dict.update( + { + "alert_type": alert_type, + "created_at": created_at, + "details": details, + "id": id, + "status": status, + } + ) + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + from ..models.alert_detail import AlertDetail + + d = dict(src_dict) + alert_type = AlertAlertType(d.pop("alert_type")) + + created_at = isoparse(d.pop("created_at")) + + details = [] + _details = d.pop("details") + for details_item_data in _details: + details_item = AlertDetail.from_dict(details_item_data) + + details.append(details_item) + + id = d.pop("id") + + status = AlertStatus(d.pop("status")) + + alert = cls( + alert_type=alert_type, + created_at=created_at, + details=details, + id=id, + status=status, + ) + + return alert diff --git a/src/tower/tower_api_client/models/alert_alert_type.py b/src/tower/tower_api_client/models/alert_alert_type.py new file mode 100644 index 00000000..a93fe3e2 --- /dev/null +++ b/src/tower/tower_api_client/models/alert_alert_type.py @@ -0,0 +1,9 @@ +from enum import Enum + + +class AlertAlertType(str, Enum): + ERROR = "error" + SUCCESS = "success" + + def __str__(self) -> str: + return str(self.value) diff --git a/src/tower/tower_api_client/models/alert_detail.py b/src/tower/tower_api_client/models/alert_detail.py new file mode 100644 index 00000000..90ae1c29 --- /dev/null +++ b/src/tower/tower_api_client/models/alert_detail.py @@ -0,0 +1,47 @@ +from collections.abc import Mapping +from typing import Any, TypeVar + +from attrs import define as _attrs_define + +T = TypeVar("T", bound="AlertDetail") + + +@_attrs_define +class AlertDetail: + """ + Attributes: + description (str): + name (str): + """ + + description: str + name: str + + def to_dict(self) -> dict[str, Any]: + description = self.description + + name = self.name + + field_dict: dict[str, Any] = {} + field_dict.update( + { + "description": description, + "name": name, + } + ) + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + d = dict(src_dict) + description = d.pop("description") + + name = d.pop("name") + + alert_detail = cls( + description=description, + name=name, + ) + + return alert_detail diff --git a/src/tower/tower_api_client/models/alert_status.py b/src/tower/tower_api_client/models/alert_status.py new file mode 100644 index 00000000..41c75f41 --- /dev/null +++ b/src/tower/tower_api_client/models/alert_status.py @@ -0,0 +1,10 @@ +from enum import Enum + + +class AlertStatus(str, Enum): + ERRORED = "errored" + PENDING = "pending" + SENT = "sent" + + def __str__(self) -> str: + return str(self.value) diff --git a/src/tower/tower_api_client/models/list_alerts_response.py b/src/tower/tower_api_client/models/list_alerts_response.py new file mode 100644 index 00000000..585f75ab --- /dev/null +++ b/src/tower/tower_api_client/models/list_alerts_response.py @@ -0,0 +1,75 @@ +from collections.abc import Mapping +from typing import TYPE_CHECKING, Any, TypeVar, Union + +from attrs import define as _attrs_define + +from ..types import UNSET, Unset + +if TYPE_CHECKING: + from ..models.alert import Alert + from ..models.pagination import Pagination + + +T = TypeVar("T", bound="ListAlertsResponse") + + +@_attrs_define +class ListAlertsResponse: + """ + Attributes: + alerts (list['Alert']): List of alerts + pages (Pagination): + schema (Union[Unset, str]): A URL to the JSON Schema for this object. Example: + https://api.tower.dev/v1/schemas/ListAlertsResponse.json. + """ + + alerts: list["Alert"] + pages: "Pagination" + schema: Union[Unset, str] = UNSET + + def to_dict(self) -> dict[str, Any]: + alerts = [] + for alerts_item_data in self.alerts: + alerts_item = alerts_item_data.to_dict() + alerts.append(alerts_item) + + pages = self.pages.to_dict() + + schema = self.schema + + field_dict: dict[str, Any] = {} + field_dict.update( + { + "alerts": alerts, + "pages": pages, + } + ) + if schema is not UNSET: + field_dict["$schema"] = schema + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + from ..models.alert import Alert + from ..models.pagination import Pagination + + d = dict(src_dict) + alerts = [] + _alerts = d.pop("alerts") + for alerts_item_data in _alerts: + alerts_item = Alert.from_dict(alerts_item_data) + + alerts.append(alerts_item) + + pages = Pagination.from_dict(d.pop("pages")) + + schema = d.pop("$schema", UNSET) + + list_alerts_response = cls( + alerts=alerts, + pages=pages, + schema=schema, + ) + + return list_alerts_response diff --git a/src/tower/tower_api_client/models/run.py b/src/tower/tower_api_client/models/run.py index d8457b6c..b439348c 100644 --- a/src/tower/tower_api_client/models/run.py +++ b/src/tower/tower_api_client/models/run.py @@ -1,6 +1,6 @@ import datetime from collections.abc import Mapping -from typing import Any, TypeVar, Union, cast +from typing import TYPE_CHECKING, Any, TypeVar, Union, cast from attrs import define as _attrs_define from dateutil.parser import isoparse @@ -8,6 +8,10 @@ from ..models.run_status import RunStatus from ..models.run_status_group import RunStatusGroup +if TYPE_CHECKING: + from ..models.run_parameter import RunParameter + + T = TypeVar("T", bound="Run") @@ -22,6 +26,7 @@ class Run: ended_at (Union[None, datetime.datetime]): environment (str): number (int): + parameters (list['RunParameter']): Parameters used to invoke this run. run_id (str): scheduled_at (datetime.datetime): started_at (Union[None, datetime.datetime]): @@ -36,6 +41,7 @@ class Run: ended_at: Union[None, datetime.datetime] environment: str number: int + parameters: list["RunParameter"] run_id: str scheduled_at: datetime.datetime started_at: Union[None, datetime.datetime] @@ -65,6 +71,11 @@ def to_dict(self) -> dict[str, Any]: number = self.number + parameters = [] + for parameters_item_data in self.parameters: + parameters_item = parameters_item_data.to_dict() + parameters.append(parameters_item) + run_id = self.run_id scheduled_at = self.scheduled_at.isoformat() @@ -89,6 +100,7 @@ def to_dict(self) -> dict[str, Any]: "ended_at": ended_at, "environment": environment, "number": number, + "parameters": parameters, "run_id": run_id, "scheduled_at": scheduled_at, "started_at": started_at, @@ -101,6 +113,8 @@ def to_dict(self) -> dict[str, Any]: @classmethod def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + from ..models.run_parameter import RunParameter + d = dict(src_dict) app_slug = d.pop("app_slug") @@ -142,6 +156,13 @@ def _parse_ended_at(data: object) -> Union[None, datetime.datetime]: number = d.pop("number") + parameters = [] + _parameters = d.pop("parameters") + for parameters_item_data in _parameters: + parameters_item = RunParameter.from_dict(parameters_item_data) + + parameters.append(parameters_item) + run_id = d.pop("run_id") scheduled_at = isoparse(d.pop("scheduled_at")) @@ -173,6 +194,7 @@ def _parse_started_at(data: object) -> Union[None, datetime.datetime]: ended_at=ended_at, environment=environment, number=number, + parameters=parameters, run_id=run_id, scheduled_at=scheduled_at, started_at=started_at, diff --git a/src/tower/tower_api_client/models/run_parameter.py b/src/tower/tower_api_client/models/run_parameter.py new file mode 100644 index 00000000..c61bf925 --- /dev/null +++ b/src/tower/tower_api_client/models/run_parameter.py @@ -0,0 +1,47 @@ +from collections.abc import Mapping +from typing import Any, TypeVar + +from attrs import define as _attrs_define + +T = TypeVar("T", bound="RunParameter") + + +@_attrs_define +class RunParameter: + """ + Attributes: + name (str): + value (str): + """ + + name: str + value: str + + def to_dict(self) -> dict[str, Any]: + name = self.name + + value = self.value + + field_dict: dict[str, Any] = {} + field_dict.update( + { + "name": name, + "value": value, + } + ) + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + d = dict(src_dict) + name = d.pop("name") + + value = d.pop("value") + + run_parameter = cls( + name=name, + value=value, + ) + + return run_parameter diff --git a/tests/test_client.py b/tests/tower/test_client.py similarity index 93% rename from tests/test_client.py rename to tests/tower/test_client.py index e65df9fd..e9a75c70 100644 --- a/tests/test_client.py +++ b/tests/tower/test_client.py @@ -25,7 +25,8 @@ def test_running_apps(httpx_mock): "scheduled_at": "2025-04-25T20:54:58.761867Z", "started_at": "2025-04-25T20:54:59.366937Z", "status": "pending", - "status_group": "" + "status_group": "", + "parameters": [] } }, status_code=200, @@ -60,7 +61,8 @@ def test_waiting_for_runs(httpx_mock): "scheduled_at": "2025-04-25T20:54:58.761867Z", "started_at": "2025-04-25T20:54:59.366937Z", "status": "pending", - "status_group": "" + "status_group": "", + "parameters": [] } }, status_code=200, @@ -83,7 +85,8 @@ def test_waiting_for_runs(httpx_mock): "scheduled_at": "2025-04-25T20:54:58.761867Z", "started_at": "2025-04-25T20:54:59.366937Z", "status": "exited", - "status_group": "successful" + "status_group": "successful", + "parameters": [] } }, status_code=200, @@ -107,7 +110,8 @@ def test_waiting_for_runs(httpx_mock): scheduled_at="2025-04-25T20:54:58.761867Z", started_at="2025-04-25T20:54:59.366937Z", status="crashed", - status_group="failed" + status_group="failed", + parameters=[] ) # Set WAIT_TIMEOUT to 0 so we don't have to...wait. diff --git a/uv.lock b/uv.lock index f5e005fb..1b9f95c2 100644 --- a/uv.lock +++ b/uv.lock @@ -1056,7 +1056,7 @@ wheels = [ [[package]] name = "tower" -version = "0.3.9" +version = "0.3.11" source = { editable = "." } dependencies = [ { name = "attrs" },