From fc7a5dfc9cfd692aaa0754fca148e1cb0336fe7d Mon Sep 17 00:00:00 2001 From: Paulo Bressan Date: Fri, 4 Oct 2024 10:24:17 -0300 Subject: [PATCH] Implement port annotation (#49) * feat: improved port creation and refactor * feat: improved port commands * chore: removed utils * feat: improved init context --- Cargo.lock | 131 ++++++++++++++++---------------- src/api/account.rs | 37 ---------- src/api/format.rs | 40 ---------- src/api/mod.rs | 160 ---------------------------------------- src/context.rs | 76 ++++++------------- src/dirs.rs | 1 + src/init/login.rs | 9 +-- src/init/manual.rs | 24 +++--- src/init/mod.rs | 40 ++++------ src/main.rs | 23 +++--- src/pages/deploy.rs | 3 +- src/ports/create.rs | 76 ++++++++----------- src/ports/delete.rs | 18 ++--- src/ports/format.rs | 115 ++++++++++++++--------------- src/ports/list.rs | 4 +- src/ports/mod.rs | 2 +- src/ports/show.rs | 20 ++--- src/ports/tunnel.rs | 47 +++--------- src/rpc/metadata/mod.rs | 29 ++++++-- src/rpc/projects/mod.rs | 24 ++++++ src/utils.rs | 30 -------- 21 files changed, 283 insertions(+), 626 deletions(-) delete mode 100644 src/api/account.rs delete mode 100644 src/api/format.rs delete mode 100644 src/api/mod.rs delete mode 100644 src/utils.rs diff --git a/Cargo.lock b/Cargo.lock index e426acf..0e2c421 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -98,9 +98,9 @@ checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" [[package]] name = "async-stream" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" dependencies = [ "async-stream-impl", "futures-core", @@ -109,13 +109,13 @@ dependencies = [ [[package]] name = "async-stream-impl" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -126,14 +126,14 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] name = "autocfg" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "axum" @@ -269,9 +269,9 @@ checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" [[package]] name = "cc" -version = "1.1.21" +version = "1.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07b1695e2c7e8fc85310cde85aeaab7e3097f593c91d209d3f9df76c928100f0" +checksum = "812acba72f0a070b003d3697490d2b55b837230ae7c6c6497f05cc2ddbb8d938" dependencies = [ "shlex", ] @@ -299,9 +299,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.18" +version = "4.5.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0956a43b323ac1afaffc053ed5c4b7c1f1800bacd1683c353aabbb752515dd3" +checksum = "7be5744db7978a28d9df86a214130d106a89ce49644cbc4e3f0c22c3fba30615" dependencies = [ "clap_builder", "clap_derive", @@ -309,9 +309,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.18" +version = "4.5.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d72166dd41634086d5803a47eb71ae740e61d84709c36f3c34110173db3961b" +checksum = "a5fbc17d3ef8278f55b282b2a2e75ae6f6c7d4bb70ed3d0382375104bfafdb4b" dependencies = [ "anstream", "anstyle", @@ -328,7 +328,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -468,7 +468,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -479,7 +479,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -500,7 +500,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -510,7 +510,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4abae7035bf79b9877b779505d8cf3749285b80c43941eda66604841889451dc" dependencies = [ "derive_builder_core", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -563,7 +563,7 @@ dependencies = [ "comfy-table", "dirs", "dmtri", - "indexmap 2.5.0", + "indexmap 2.6.0", "inquire", "json", "k8s-openapi", @@ -588,7 +588,7 @@ dependencies = [ [[package]] name = "dmtri" version = "0.1.0" -source = "git+https://github.com/demeter-run/specs.git#25e4853c635e1fab25305543e59a280fd8742ab6" +source = "git+https://github.com/demeter-run/specs.git#10042190e0f73b7d64cc3c00764fedf320e3a38c" dependencies = [ "bytes", "pbjson", @@ -661,9 +661,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.33" +version = "1.0.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "324a1be68054ef05ad64b861cc9eaf1d623d2d8cb25b4bf2cb9cdd902b4bf253" +checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" dependencies = [ "crc32fast", "miniz_oxide", @@ -777,7 +777,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -798,7 +798,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap 2.5.0", + "indexmap 2.6.0", "slab", "tokio", "tokio-util", @@ -813,9 +813,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" -version = "0.14.5" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" [[package]] name = "heck" @@ -874,9 +874,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.9.4" +version = "1.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" +checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" [[package]] name = "httpdate" @@ -998,12 +998,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown 0.14.5", + "hashbrown 0.15.0", "serde", ] @@ -1190,7 +1190,7 @@ checksum = "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -1365,7 +1365,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -1492,7 +1492,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", - "indexmap 2.5.0", + "indexmap 2.6.0", ] [[package]] @@ -1512,7 +1512,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -1571,7 +1571,7 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -1648,7 +1648,7 @@ dependencies = [ "itertools 0.12.1", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -1701,9 +1701,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.6" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "355ae415ccd3a04315d3f8246e86d67689ea74d88d915576e1589a351062a13b" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" dependencies = [ "bitflags 2.6.0", ] @@ -1733,9 +1733,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" dependencies = [ "aho-corasick", "memchr", @@ -1744,9 +1744,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" @@ -1874,7 +1874,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5" dependencies = [ "openssl-probe", - "rustls-pemfile 2.1.3", + "rustls-pemfile 2.2.0", "rustls-pki-types", "schannel", "security-framework", @@ -1891,19 +1891,18 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "2.1.3" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" dependencies = [ - "base64 0.22.1", "rustls-pki-types", ] [[package]] name = "rustls-pki-types" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" +checksum = "0e696e35370c65c9c541198af4543ccd580cf17fc25d8e05c5a242b202488c55" [[package]] name = "rustls-webpki" @@ -2028,7 +2027,7 @@ checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -2190,7 +2189,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -2240,9 +2239,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.77" +version = "2.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" dependencies = [ "proc-macro2", "quote", @@ -2289,9 +2288,9 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.12.0" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" +checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" dependencies = [ "cfg-if", "fastrand", @@ -2338,7 +2337,7 @@ checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -2401,7 +2400,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -2486,7 +2485,7 @@ version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ - "indexmap 2.5.0", + "indexmap 2.6.0", "serde", "serde_spanned", "toml_datetime", @@ -2513,7 +2512,7 @@ dependencies = [ "pin-project", "prost 0.12.6", "rustls-native-certs", - "rustls-pemfile 2.1.3", + "rustls-pemfile 2.2.0", "rustls-pki-types", "tokio", "tokio-rustls 0.25.0", @@ -2575,7 +2574,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -2627,9 +2626,9 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "unicode-bidi" -version = "0.3.15" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" +checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" [[package]] name = "unicode-ident" @@ -2779,7 +2778,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", "wasm-bindgen-shared", ] @@ -2813,7 +2812,7 @@ checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3097,7 +3096,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] diff --git a/src/api/account.rs b/src/api/account.rs deleted file mode 100644 index 53fabac..0000000 --- a/src/api/account.rs +++ /dev/null @@ -1,37 +0,0 @@ -use reqwest::{Client, Error}; -use std::env; - -use super::{build_agent_header, check_response_update_header}; - -pub async fn initialize_user(access_token: &str) -> Result { - let url = format!("{}/users", build_api_url()); - - let client = Client::new(); - let resp = client - .post(url) - .header("Authorization", format!("Bearer {}", access_token)) - .header("agent", build_agent_header()) - .send() - .await?; - - check_response_update_header(&resp)?; - let response = resp.json::().await?; - - let userid = response - .as_object() - .and_then(|o| o.get("userId")) - .and_then(|x| x.as_str()) - .unwrap_or_default() - .to_owned(); - - Ok(userid) -} - -fn build_api_url() -> String { - format!("{}/mgmt/account", get_base_url()) -} - -fn get_base_url() -> String { - let api_base_url = "https://console.us1.demeter.run".into(); - env::var("API_BASE_URL").unwrap_or(api_base_url) -} diff --git a/src/api/format.rs b/src/api/format.rs deleted file mode 100644 index 6f058ea..0000000 --- a/src/api/format.rs +++ /dev/null @@ -1,40 +0,0 @@ -use colored::*; -use semver::Version; - -const VERSION: &str = env!("CARGO_PKG_VERSION"); - -pub fn format_new_cli_version_available(version: &str) { - let message = generate_update_message(version); - println!("{}", message); -} - -fn generate_update_message(latest: &str) -> String { - let current_version = Version::parse(VERSION).expect("Invalid current version format"); - let latest_version = Version::parse(latest).expect("Invalid latest version format"); - - let update_type = if latest_version.major > current_version.major { - "major" - } else if latest_version.minor > current_version.minor { - "minor" - } else if latest_version.patch > current_version.patch { - "patch" - } else { - "prerelease" - }; - - let type_colored = match update_type { - "major" => update_type.red(), - "minor" => update_type.yellow(), - _ => update_type.green(), - }; - - let old_version_colored = VERSION.red(); - let latest_version_colored = latest.green(); - let update_command = "https://docs.demeter.run/guides/cli"; - let update_command_colored = update_command.cyan(); - - format!( - "\nNew {} version of dmtr available! {} -> {}\nVisit {} to update!\n", - type_colored, old_version_colored, latest_version_colored, update_command_colored - ) -} diff --git a/src/api/mod.rs b/src/api/mod.rs deleted file mode 100644 index d6ec1b2..0000000 --- a/src/api/mod.rs +++ /dev/null @@ -1,160 +0,0 @@ -use indexmap::IndexMap; -use reqwest::Error; -use serde::{Deserialize, Serialize}; -use std::{collections::HashMap, env}; - -use self::format::format_new_cli_version_available; - -pub mod account; -mod format; - -const VERSION: &str = env!("CARGO_PKG_VERSION"); - -#[derive(Deserialize, Serialize, Debug, Clone)] -pub struct PortInfo { - pub id: String, - pub kind: String, - pub key: String, - pub name: String, - pub network: String, - pub tier: String, - pub version: String, - pub instance: Instance, -} - -#[derive(Deserialize, Serialize, Debug, Clone)] -pub struct PortInfoList { - pub auth_token: Option, - pub id: Option, - pub kind: Option, - pub key: Option, - pub name: Option, - pub network: Option, - pub port: Option, - pub tier: Option, - pub version: Option, - pub instance: Option, -} - -#[derive(Deserialize, Serialize, Debug, Clone)] -#[serde(untagged)] // Allows for different shapes of the "instance" object -pub enum Instance { - Postgres(PostgresPortInstance), - Http(HttpPortInstance), - Node(NodePortInstance), -} - -#[derive(Deserialize, Serialize, Debug, Clone)] -pub struct PostgresPortInstance { - pub hostname: String, - pub database: String, - pub port: u16, - pub username: String, - pub password: String, - #[serde(rename = "connectionString")] - pub connection_string: String, -} - -#[derive(Deserialize, Serialize, Debug, Clone)] -pub struct HttpPortInstance { - #[serde(rename = "apiKey")] - pub api_key: String, - pub endpoint: String, - #[serde(rename = "authenticatedEndpoint")] - pub authenticated_endpoint: String, -} - -#[derive(Deserialize, Serialize, Debug, Clone)] -pub struct NodePortInstance { - #[serde(rename = "apiKey")] - pub api_key: String, - #[serde(rename = "authenticatedEndpoint")] - pub authenticated_endpoint: String, -} - -#[derive(Deserialize, Serialize, Debug, Clone)] -pub struct PortOptions { - pub networks: Vec, - pub versions: Option>>, - pub tiers: IndexMap, - pub kind: String, -} - -// inquire select requires a vector of strings, so we need to transform the -// values into a vector for the select prompt we -impl PortOptions { - // Network - pub fn get_networks(&self) -> Vec { - self.networks.clone() - } - - pub fn find_network_key_by_value(&self, network_value: &str) -> Option { - self.networks - .iter() - .find(|&network| network == network_value) - .cloned() - } - - // version - pub fn get_network_versions(&self, network: &str) -> Vec { - let mut version_options = Vec::new(); - if let Some(versions) = &self.versions { - if let Some(versions_map) = versions.get(network) { - for (_label, version) in versions_map { - version_options.push(version.to_string()); - } - } - } - version_options - } - - pub fn find_version_label_by_number( - &self, - network: &str, - selected_version: &str, - ) -> Option { - if let Some(versions) = &self.versions { - if let Some(version_map) = versions.get(network) { - for (label, _version) in version_map { - if selected_version.contains(label) { - return Some(label.clone()); - } - } - } - } - None - } - - // tiers - pub fn get_tiers(&self) -> Vec { - self.tiers.values().cloned().collect() - } - - // @TODO: find a cleaner way to do this - pub fn find_tier_key_by_value(&self, formatted_string: &str) -> Option { - // Check if tier name is included in the formatted string - self.tiers.iter().find_map(|(key, _value)| { - if formatted_string.contains(key) { - Some(key.clone()) - } else { - None - } - }) - } -} - -pub fn check_response_update_header(resp: &reqwest::Response) -> Result<&reqwest::Response, Error> { - let headers = resp.headers(); - let version = headers.get("dmtr-cli-update"); - if let Some(version) = version { - let version = version.to_str().unwrap(); - if version != VERSION { - format_new_cli_version_available(version); - } - } - Ok(resp) -} - -pub fn build_agent_header() -> String { - format!("dmtr-cli/{}", VERSION) -} diff --git a/src/context.rs b/src/context.rs index efe8920..596a6f7 100644 --- a/src/context.rs +++ b/src/context.rs @@ -3,6 +3,8 @@ use std::collections::HashMap; use miette::{Context as MietteContext, IntoDiagnostic}; use serde::{Deserialize, Serialize}; +use crate::rpc; + #[derive(Debug, Serialize, Deserialize, Clone, Default)] pub struct Config { pub contexts: HashMap, @@ -12,24 +14,21 @@ pub struct Config { #[derive(Serialize, Deserialize, Clone, Debug)] pub struct Context { pub project: Project, - pub cloud: Cloud, - pub operator: Operator, pub auth: Auth, } impl Context { - pub fn ephemeral(id: &str, namespace: &str, api_key: &str) -> Self { - let project = crate::context::Project::new(id, namespace, None); + pub async fn ephemeral(id: &str, api_key: &str) -> miette::Result { + let project = rpc::projects::find_by_id( + rpc::auth::Credential::Secret((id.into(), api_key.into())), + id, + ) + .await?; + + let project = crate::context::Project::new(id, &project.namespace, Some(project.name)); let auth = crate::context::Auth::api_key(api_key); - let cloud = crate::context::Cloud::default(); - let operator = crate::context::Operator::default(); - Self { - project, - auth, - cloud, - operator, - } + Ok(Self { project, auth }) } } @@ -67,36 +66,6 @@ impl Auth { } } -const DEFAULT_CLOUD: &str = "cloud0.txpipe.io"; - -#[derive(Serialize, Deserialize, Clone, Debug)] -pub struct Cloud { - pub name: String, -} - -impl Default for Cloud { - fn default() -> Self { - Self { - name: DEFAULT_CLOUD.to_string(), - } - } -} - -#[derive(Serialize, Deserialize, Clone, Debug)] -pub struct Operator { - pub name: String, - pub entrypoint: String, -} - -impl Default for Operator { - fn default() -> Self { - Self { - name: "TxPipe".to_owned(), - entrypoint: "us1.demeter.run".to_owned(), - } - } -} - pub fn load_config(dirs: &crate::dirs::Dirs) -> miette::Result { let location = dirs.root_dir().join("config.toml"); @@ -174,7 +143,7 @@ pub fn load_context_by_name( ) -> miette::Result> { let mut config = load_config(dirs)?; let out = config.contexts.remove(name); - return Ok(out); + Ok(out) } pub fn load_default_context(dirs: &crate::dirs::Dirs) -> miette::Result> { @@ -188,22 +157,19 @@ pub fn load_default_context(dirs: &crate::dirs::Dirs) -> miette::Result, + +pub async fn infer_context( name: Option<&str>, - namespace: Option<&str>, + project_id: Option<&str>, api_key: Option<&str>, dirs: &crate::dirs::Dirs, ) -> miette::Result> { - match (id, name, namespace, api_key) { - (Some(id), None, Some(ns), Some(ak)) => Ok(Some(Context::ephemeral(id, ns, ak))), - (None, None, None, None) => load_default_context(dirs), - (None, Some(context), None, None) => load_context_by_name(context, dirs), - (None, None, None, Some(_)) => Err(miette::miette!("missing namespace or id value")), - (Some(_), None, Some(_), None) => Err(miette::miette!("missing api key value")), - (..) => Err(miette::miette!( - "conflicting values, specify either a context or namespace" - )), + match (name, project_id, api_key) { + (None, Some(id), Some(ak)) => Ok(Some(Context::ephemeral(id, ak).await?)), + (None, None, Some(_)) => Err(miette::miette!("missing project id value")), + (None, Some(_), None) => Err(miette::miette!("missing api key value")), + (Some(context), _, _) => load_context_by_name(context, dirs), + _ => load_default_context(dirs), } } diff --git a/src/dirs.rs b/src/dirs.rs index 2977dc6..1393808 100644 --- a/src/dirs.rs +++ b/src/dirs.rs @@ -19,6 +19,7 @@ pub fn ensure_root_dir(explicit: Option<&Path>) -> miette::Result { Ok(defined) } +#[derive(Debug)] pub struct Dirs { root_dir: PathBuf, } diff --git a/src/init/login.rs b/src/init/login.rs index ee9f2e4..ab2c67c 100644 --- a/src/init/login.rs +++ b/src/init/login.rs @@ -1,10 +1,8 @@ -use miette::{bail, Context as _, IntoDiagnostic as _}; +use miette::bail; use reqwest::StatusCode; use serde::{Deserialize, Serialize}; use std::{collections::HashMap, time::Duration}; -use crate::api; - async fn find_login_url() -> (String, String) { let mut params = HashMap::new(); params.insert("client_id", "gpJ63MG5g1V1PKufM9WHGjjeAe7yCT8L"); @@ -96,11 +94,6 @@ pub async fn run() -> miette::Result { let (status, access_token) = poll_token(&device_code).await; if status.is_success() { - api::account::initialize_user(&access_token) - .await - .into_diagnostic() - .context("initializing user")?; - println!("login successful!"); return Ok(access_token); } diff --git a/src/init/manual.rs b/src/init/manual.rs index 960fa93..5fe68d3 100644 --- a/src/init/manual.rs +++ b/src/init/manual.rs @@ -1,14 +1,11 @@ use miette::IntoDiagnostic; -pub async fn run( - id: &str, - namespace: &str, - api_key: &str, - dirs: &crate::dirs::Dirs, -) -> miette::Result<()> { +use crate::context::Context; + +pub async fn run(context: &Context, dirs: &crate::dirs::Dirs) -> miette::Result<()> { println!("Setting up context for:\n"); - println!(" Namespace: {}", namespace); - println!(" API key: {}\n", api_key); + println!(" Project: {}", context.project.namespace); + println!(" API key: {}\n", context.auth.token); let is_default = inquire::Confirm::new("use as default context?") .with_help_message( @@ -17,11 +14,12 @@ pub async fn run( .prompt() .into_diagnostic()?; - let dto = crate::context::Context::ephemeral(id, namespace, api_key); - - let namespace = dto.project.namespace.clone(); - - crate::context::overwrite_context(&namespace, dto, is_default, dirs)?; + crate::context::overwrite_context( + &context.project.namespace, + context.clone(), + is_default, + dirs, + )?; Ok(()) } diff --git a/src/init/mod.rs b/src/init/mod.rs index 010049b..71585aa 100644 --- a/src/init/mod.rs +++ b/src/init/mod.rs @@ -1,21 +1,10 @@ -use crate::context::Context; +use crate::context::{load_config, Context}; use clap::Parser; use miette::{Context as _, IntoDiagnostic as _}; use std::fmt::Display; #[derive(Parser, Debug)] -pub struct Args { - /// Project ID we are currently working on - #[arg(short, long, global = true, env = "DMTR_PROJECT_ID")] - id: Option, - /// Name of the namespace we're working on - #[arg(short, long, global = true, env = "DMTR_NAMESPACE")] - namespace: Option, - - /// The api key to use as authentication - #[arg(short, long, global = true, env = "DMTR_API_KEY")] - api_key: Option, -} +pub struct Args {} mod apikey; mod login; @@ -49,8 +38,6 @@ pub async fn import_context(dirs: &crate::dirs::Dirs) -> miette::Result let ctx = crate::context::Context { project: crate::context::Project::new(&project.id, &project.namespace, Some(project.name)), auth: crate::context::Auth::api_key(&api_key), - cloud: crate::context::Cloud::default(), - operator: crate::context::Operator::default(), }; crate::context::overwrite_context(&project.namespace, ctx.clone(), false, dirs)?; @@ -82,15 +69,7 @@ async fn define_context(dirs: &crate::dirs::Dirs) -> miette::Result { } } -pub async fn run(args: Args, dirs: &crate::dirs::Dirs) -> miette::Result<()> { - if args.namespace.is_some() && args.api_key.is_some() { - let id = args.id.unwrap(); - let namespace = args.namespace.unwrap(); - let api_key = args.api_key.unwrap(); - manual::run(&id, &namespace, &api_key, dirs).await?; - return Ok(()); - }; - +pub async fn run(_args: Args, cli: &crate::Cli) -> miette::Result<()> { println!("Welcome to"); println!(include_str!("asciiart.txt")); println!("\n"); @@ -98,9 +77,18 @@ pub async fn run(args: Args, dirs: &crate::dirs::Dirs) -> miette::Result<()> { println!("Let's get started!"); println!("\n"); - let ctx = define_context(dirs).await?; + let config = load_config(&cli.dirs)?; + + if let Some(context) = cli.context.as_ref() { + if !config.contexts.contains_key(&context.project.namespace) { + manual::run(context, &cli.dirs).await?; + return Ok(()); + } + } + + let ctx = define_context(&cli.dirs).await?; - crate::context::set_default_context(&ctx.project.namespace, dirs)?; + crate::context::set_default_context(&ctx.project.namespace, &cli.dirs)?; println!( "You CLI is now configured to use context {}", diff --git a/src/main.rs b/src/main.rs index b0f3bb8..2c58595 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,14 +4,12 @@ use std::path::PathBuf; use tracing::Level; use tracing_subscriber::{layer::SubscriberExt as _, util::SubscriberInitExt as _}; -mod api; mod context; mod dirs; mod init; mod pages; mod ports; mod rpc; -mod utils; extern crate core; @@ -22,18 +20,15 @@ pub struct Args { #[command(subcommand)] command: Commands, - #[arg(short, long, global = true, env = "DMTR_ID")] - id: Option, - - /// Name of the namespace we're working on - #[arg(short, long, global = true, env = "DMTR_NAMESPACE")] - namespace: Option, + /// Project ID uuid + #[arg(short, long, global = true, env = "DMTR_PROJECT_ID")] + project_id: Option, /// The api key to use as authentication #[arg(short, long, global = true, env = "DMTR_API_KEY")] api_key: Option, - /// Name of the context we're working on + /// Name of the context #[arg(short, long, global = true, env = "DMTR_CONTEXT")] context: Option, @@ -62,6 +57,7 @@ pub enum Commands { Ports(ports::Args), } +#[derive(Debug)] pub struct Cli { pub dirs: dirs::Dirs, pub context: Option, @@ -79,23 +75,22 @@ async fn main() -> miette::Result<()> { let context = context::infer_context( args.context.as_deref(), - args.id.as_deref(), - args.namespace.as_deref(), + args.project_id.as_deref(), args.api_key.as_deref(), &dirs, - )?; + ) + .await?; let cli = Cli { context, dirs }; if args.verbose { tracing_subscriber::registry() - //.with(tracing_subscriber::filter::LevelFilter::INFO) .with(tracing_subscriber::filter::Targets::default().with_target("dmtr", Level::DEBUG)) .init(); } match args.command { - Commands::Init(args) => init::run(args, &cli.dirs).await, + Commands::Init(args) => init::run(args, &cli).await, Commands::Pages(args) => pages::run(args, &cli).await, Commands::Ports(args) => ports::run(args, &cli).await, } diff --git a/src/pages/deploy.rs b/src/pages/deploy.rs index ed354a2..00ba1e3 100644 --- a/src/pages/deploy.rs +++ b/src/pages/deploy.rs @@ -1,3 +1,4 @@ +use base64::prelude::*; use miette::IntoDiagnostic; use ocipkg::{image::Builder, ImageName}; use std::path::{Path, PathBuf}; @@ -55,7 +56,7 @@ pub async fn run(args: Args, cli: &crate::Cli) -> miette::Result<()> { let _ = builder.into_inner(); let registry_url = name.registry_url().into_diagnostic()?; - let registry_auth = base64::encode(&args.registry_auth); + let registry_auth = BASE64_STANDARD.encode(&args.registry_auth); let mut new_auth = ocipkg::distribution::StoredAuth::default(); new_auth.insert(registry_url.domain().unwrap(), registry_auth); diff --git a/src/ports/create.rs b/src/ports/create.rs index 9075d39..fa30895 100644 --- a/src/ports/create.rs +++ b/src/ports/create.rs @@ -1,11 +1,7 @@ use clap::Parser; use miette::IntoDiagnostic; -use crate::{ - context::extract_context_data, - rpc, - utils::{get_spec_from_crd, KnownField}, -}; +use crate::{context::extract_context_data, rpc}; #[derive(Parser)] pub struct Args {} @@ -13,54 +9,40 @@ pub struct Args {} pub async fn run(_args: Args, cli: &crate::Cli) -> miette::Result<()> { let (api_key, id, _) = extract_context_data(cli); - let crds = rpc::metadata::find().await?; + let metadata = rpc::metadata::find().await?; - let kinds = crds + let resouce_kinds = metadata .iter() - .map(|x| x.spec.names.kind.clone()) + .map(|m| m.crd.spec.names.kind.clone()) .collect::>(); - let kind = inquire::Select::new("Choose the port kind", kinds.clone()) - .with_page_size(kinds.len()) - .prompt() - .into_diagnostic()?; - - let crd_selected = crds.iter().find(|crd| crd.spec.names.kind == kind).unwrap(); - let spec = get_spec_from_crd(crd_selected).unwrap(); + let kind_selected = + inquire::Select::new("What resource do want to create?", resouce_kinds.clone()) + .with_page_size(resouce_kinds.len()) + .prompt() + .into_diagnostic()?; - let mut payload = serde_json::Map::default(); - - for (field, value) in spec { - let is_nullable = value.nullable.unwrap_or_default(); + let resource_metadata = metadata + .iter() + .find(|m| m.crd.spec.names.kind == kind_selected) + .unwrap(); - if !is_nullable { - if let Ok(known_field) = field.parse::() { - match known_field { - KnownField::Network => { - let network_options = vec!["mainnet", "preprod", "preview"]; + let resource_options = resource_metadata + .options + .iter() + .map(|o| o.description.clone()) + .collect::>(); - let selected_network = - inquire::Select::new("Choose the network", network_options) - .prompt() - .into_diagnostic()?; - payload.insert( - field.to_string(), - serde_json::Value::String(selected_network.into()), - ); - } - KnownField::OperatorVersion => { - payload.insert(field.to_string(), serde_json::Value::String("1".into())); - } - } - continue; - } + let option_selected = inquire::Select::new("Select an option", resource_options.clone()) + .with_page_size(resource_options.len()) + .prompt() + .into_diagnostic()?; - let value = inquire::Text::new(&format!("Fill out the {field}")) - .prompt() - .into_diagnostic()?; - payload.insert(field.to_string(), serde_json::Value::String(value)); - } - } + let resource_option_selected = resource_metadata + .options + .iter() + .find(|r| r.description == option_selected) + .unwrap(); let confirm = inquire::Confirm::new("Do you want to proceed?") .prompt() @@ -71,8 +53,8 @@ pub async fn run(_args: Args, cli: &crate::Cli) -> miette::Result<()> { return Ok(()); } - let spec = serde_json::Value::Object(payload); - let result = rpc::resources::create(&api_key, &id, &kind, &spec.to_string()).await?; + let spec = resource_option_selected.spec.to_string(); + let result = rpc::resources::create(&api_key, &id, &kind_selected, &spec).await?; println!("Port {}({}) created", result.kind, result.id); diff --git a/src/ports/delete.rs b/src/ports/delete.rs index 85a201c..7d8ab45 100644 --- a/src/ports/delete.rs +++ b/src/ports/delete.rs @@ -5,13 +5,8 @@ use crate::{context::extract_context_data, rpc}; #[derive(Parser)] pub struct Args { - /// the instance in kind/id format. e.g. kupo/mainnet-222222 - instance: String, -} - -fn get_instance_parts(instance: &str) -> (String, String) { - let parts: Vec<&str> = instance.split('/').collect(); - (parts[0].to_string(), parts[1].to_string()) + /// the resource uuid + id: String, } pub async fn run(args: Args, cli: &crate::Cli) -> miette::Result<()> { @@ -22,7 +17,7 @@ pub async fn run(args: Args, cli: &crate::Cli) -> miette::Result<()> { let msg = format!( "You are about to delete {}. This action cannot be undone. Do you want to proceed?", - args.instance + args.id ); let confirm = inquire::Confirm::new(&msg).prompt().into_diagnostic()?; @@ -34,13 +29,10 @@ pub async fn run(args: Args, cli: &crate::Cli) -> miette::Result<()> { let (api_key, project_id, _) = extract_context_data(cli); - // parse args - let (_, id) = get_instance_parts(&args.instance); - - rpc::resources::delete(&api_key, &project_id, &id) + rpc::resources::delete(&api_key, &project_id, &args.id) .await .unwrap(); - println!("Successfully deleted port: {}", args.instance); + println!("Successfully deleted port: {}", args.id); Ok(()) } diff --git a/src/ports/format.rs b/src/ports/format.rs index 69ba9cb..011d8e1 100644 --- a/src/ports/format.rs +++ b/src/ports/format.rs @@ -1,81 +1,80 @@ -use crate::api::{Instance, PortInfo}; -use colored::*; use comfy_table::modifiers::UTF8_ROUND_CORNERS; use comfy_table::presets::UTF8_FULL; use comfy_table::{ContentArrangement, Table}; use dmtri::demeter::ops::v1alpha::Resource; +use miette::IntoDiagnostic; -pub fn pretty_print_port(port: PortInfo) { - let mut lines = vec![port.name.clone()]; - lines.push("".to_string()); // Empty line for spacing - lines.push(format!("ID: {}/{}", port.kind, port.id)); - - // TODO: for rpc calls we need to deserialize the port.data to get the version - // and tier? - match &port.instance { - Instance::Postgres(instance) => { - lines.push(format!("Network: {}", port.network)); - lines.push(format!("Hostname: {}", instance.hostname)); - lines.push(format!("Database: {}", instance.database)); - lines.push(format!("Port: {}", instance.port)); - lines.push(format!("Username: {}", instance.username)); - lines.push(format!("Password: {}", instance.password)); - lines.push(format!("Connection String: {}", instance.connection_string)); - lines.push(format!("Tier: {}", port.tier)); - } - Instance::Http(instance) => { - lines.push(format!("Network: {}", port.network)); - lines.push(format!("API Key: {}", instance.api_key)); - lines.push(format!("Endpoint: {}", instance.endpoint)); - lines.push(format!( - "Authenticated Endpoint: {}", - instance.authenticated_endpoint - )); - lines.push(format!("Tier: {}", port.tier)); - } - Instance::Node(instance) => { - lines.push(format!("Network: {}", port.network)); - lines.push(format!( - "Authenticated Endpoint: {}", - instance.authenticated_endpoint - )); - lines.push(format!("Tier: {}", port.tier)); - } - } +pub fn pretty_print_resource_table(resources: Vec) { + let mut table = Table::new(); - for line in lines.iter().enumerate() { - let (index, content) = line; + table + .load_preset(UTF8_FULL) + .apply_modifier(UTF8_ROUND_CORNERS) + .set_content_arrangement(ContentArrangement::Dynamic) + .set_header(vec!["ID", "Kind", "Name", "Created At"]); - // let mut padded_line = format!("{: <1$}", content, box_width - margin); - if index == 0 { - // print empty line - println!("\n"); - println!("{}", content.color(Color::Green).bold()); - } else { - println!("{}", content); - } + for resource in resources { + table.add_row(vec![ + resource.id, + resource.name, + resource.kind, + resource.created_at, + ]); } - println!(); // Optional: empty line for spacing between entries + println!("{table}"); } -pub fn pretty_print_ports_table(ports: Vec) { +pub fn pretty_print_resouce_detail_table(resources: Vec) -> miette::Result<()> { let mut table = Table::new(); + let Some(first_resource) = resources.first() else { + return Ok(()); + }; + + let annotations = serde_json::from_str::( + &first_resource.annotations.clone().unwrap_or_default(), + ) + .into_diagnostic()?; + let mut annotations_headers: Vec = annotations + .as_array() + .unwrap() + .iter() + .map(|v| v.get("label").unwrap().as_str().unwrap_or_default().into()) + .collect(); + + let mut headers = vec![String::from("Name")]; + headers.append(&mut annotations_headers); + table .load_preset(UTF8_FULL) .apply_modifier(UTF8_ROUND_CORNERS) .set_content_arrangement(ContentArrangement::Dynamic) - .set_header(vec!["Instance", "Spec", "Created At"]); + .set_header(headers); - for port in ports { - let instance = format_instance(&port.id, &port.kind); - table.add_row(vec![instance, port.spec.clone(), port.created_at.clone()]); + for resource in resources { + let mut values: Vec = vec![resource.name]; + + if let Some(annotations) = resource.annotations { + let annotations: serde_json::Value = + serde_json::from_str(&annotations).into_diagnostic()?; + + for value in annotations.as_array().unwrap().iter() { + values.push( + value + .get("value") + .unwrap() + .as_str() + .unwrap_or_default() + .into(), + ); + } + } + + table.add_row(values); } println!("{table}"); -} -fn format_instance(id: &str, kind: &str) -> String { - format!("{}/{}", kind, id) + Ok(()) } diff --git a/src/ports/list.rs b/src/ports/list.rs index 4de36c7..183681d 100644 --- a/src/ports/list.rs +++ b/src/ports/list.rs @@ -5,7 +5,7 @@ use crate::{ rpc::{self}, }; -use super::format::pretty_print_ports_table; +use super::format::pretty_print_resource_table; #[derive(Parser)] pub struct Args {} @@ -24,7 +24,7 @@ pub async fn run(cli: &crate::Cli) -> miette::Result<()> { return Ok(()); } - pretty_print_ports_table(response); + pretty_print_resource_table(response); Ok(()) } diff --git a/src/ports/mod.rs b/src/ports/mod.rs index 45be928..c0b1751 100644 --- a/src/ports/mod.rs +++ b/src/ports/mod.rs @@ -32,7 +32,7 @@ pub enum Commands { pub async fn run(args: Args, cli: &crate::Cli) -> miette::Result<()> { match args.command { - Commands::List(_x) => list::run(cli).await, + Commands::List(_) => list::run(cli).await, Commands::Show(x) => show::run(x, cli).await, Commands::Create(x) => create::run(x, cli).await, Commands::Delete(x) => delete::run(x, cli).await, diff --git a/src/ports/show.rs b/src/ports/show.rs index f90bf39..8513c97 100644 --- a/src/ports/show.rs +++ b/src/ports/show.rs @@ -2,17 +2,12 @@ use clap::Parser; use crate::{context::extract_context_data, rpc}; -use super::format::pretty_print_ports_table; +use super::format::pretty_print_resouce_detail_table; #[derive(Parser)] pub struct Args { - /// the instance in kind/id format. e.g. kupo/mainnet-222222 - instance: String, -} - -fn get_instance_parts(instance: &str) -> (String, String) { - let parts: Vec<&str> = instance.split('/').collect(); - (parts[0].to_string(), parts[1].to_string()) + /// the resource uuid + id: String, } pub async fn run(args: Args, cli: &crate::Cli) -> miette::Result<()> { @@ -21,17 +16,14 @@ pub async fn run(args: Args, cli: &crate::Cli) -> miette::Result<()> { .as_ref() .ok_or(miette::miette!("can't list ports without a context"))?; - let (_, resource_id) = get_instance_parts(&args.instance); let (api_key, project_id, _) = extract_context_data(cli); - let response = rpc::resources::find_by_id(&api_key, &project_id, &resource_id).await?; + let resouces = rpc::resources::find_by_id(&api_key, &project_id, &args.id).await?; - if response.is_empty() { + if resouces.is_empty() { println!("No ports found"); return Ok(()); } - // TODO: replace this method with the one bellow to show the port details - // pretty_print_port(resource); - pretty_print_ports_table(response); + pretty_print_resouce_detail_table(resouces)?; Ok(()) } diff --git a/src/ports/tunnel.rs b/src/ports/tunnel.rs index cee0c39..38e70af 100644 --- a/src/ports/tunnel.rs +++ b/src/ports/tunnel.rs @@ -1,4 +1,4 @@ -use crate::{api::PortInfoList, context::extract_context_data, rpc}; +use crate::{context::extract_context_data, rpc}; use clap::Parser; use colored::Colorize; use dmtri::demeter::ops::v1alpha::Resource; @@ -68,10 +68,6 @@ async fn connect_remote<'a>( sref.set_keepalive(true).unwrap(); } - // remote - // .set_linger(Some(Duration::from_secs(6000000))) - // .unwrap(); - let certs = rustls_native_certs::load_native_certs() .into_diagnostic() .context("error loading TLS certificates")?; @@ -107,15 +103,13 @@ async fn connect_remote<'a>( fn define_socket_path( explicit: Option, - port: &PortInfoList, + name: &str, dirs: &crate::dirs::Dirs, ctx: &crate::context::Context, ) -> miette::Result { - let default = dirs.ensure_tmp_dir(&ctx.project.namespace)?.join(format!( - "{}-{}.socket", - port.network.as_ref().unwrap(), - port.port.as_ref().unwrap() - )); + let default = dirs + .ensure_tmp_dir(&ctx.project.namespace)? + .join(format!("{name}.socket")); let path = explicit.to_owned().unwrap_or(default); @@ -170,7 +164,6 @@ fn get_instance_parts(instance: &str) -> (String, String) { async fn define_port(explicit: Option, cli: &crate::Cli) -> miette::Result { let (api_key, id, _) = extract_context_data(cli); let response = rpc::resources::find(&api_key, &id).await?; - println!("{:?}", response); if response.is_empty() { bail!("you don't have any cardano-node ports, run dmtrctl ports create"); } @@ -185,17 +178,6 @@ async fn define_port(explicit: Option, cli: &crate::Cli) -> miette::Resu bail!("you don't have any cardano-node ports, run dmtrctl ports create"); } - // let available: Vec<_> = api::get::>(cli, &format!("ports/{}", - // CARDANO_NODE_KIND)) .await - // .into_diagnostic()? - // .into_iter() - // .map(NodeOption) - // .collect(); - // - // if available.is_empty() { - // bail!("you don't have any cardano-node ports, run dmtrctl ports create"); - // } - if let Some(explicit) = explicit { let (kind, id) = get_instance_parts(&explicit); @@ -250,29 +232,24 @@ impl ClientCounter { } } -const CARDANO_NODE_KIND: &str = "cardano-node"; +const CARDANO_NODE_KIND: &str = "CardanoNodePort"; -// #[instrument("connect", skip_all)] pub async fn run(args: Args, cli: &crate::Cli) -> miette::Result<()> { let ctx = cli .context .as_ref() .ok_or(miette::miette!("missing context"))?; - let port_info = define_port(args.port, cli).await?; + let resource = define_port(args.port, cli).await?; - let spec = serde_json::from_str::(&port_info.spec) + let spec: serde_json::Value = serde_json::from_str(&resource.spec) .into_diagnostic() - .context("error parsing port spec")?; + .context("error parsing resource spec")?; - let auth_token = spec.auth_token.as_ref().unwrap(); - let hostname = format!("{}.cnode-m1.demeter.run", auth_token).to_string(); - // let hostname = match &port_info.instance { - // Instance::Node(x) => &x.authenticated_endpoint, - // _ => bail!("invalid port instance, only kind cardano-node support - // tunnels"), }; + let auth_token = spec.get("authToken").unwrap().as_str().unwrap(); + let hostname = format!("{}.cnode-m1.demeter.run", auth_token); - let socket_path = define_socket_path(args.socket, &spec, &cli.dirs, ctx) + let socket_path = define_socket_path(args.socket, &resource.name, &cli.dirs, ctx) .context("error defining unix socket path")?; debug!(path = ?socket_path, "socket path defined"); diff --git a/src/rpc/metadata/mod.rs b/src/rpc/metadata/mod.rs index b3d97e3..69760cd 100644 --- a/src/rpc/metadata/mod.rs +++ b/src/rpc/metadata/mod.rs @@ -1,11 +1,12 @@ use dmtri::demeter::ops::v1alpha as proto; use k8s_openapi::apiextensions_apiserver::pkg::apis::apiextensions::v1::CustomResourceDefinition; use miette::IntoDiagnostic; +use serde::Deserialize; use tonic::transport::Channel; use super::get_base_url; -pub async fn find() -> miette::Result> { +pub async fn find() -> miette::Result> { let rpc_url = get_base_url(); let channel = Channel::builder(rpc_url.parse().into_diagnostic()?) .connect() @@ -18,11 +19,27 @@ pub async fn find() -> miette::Result> { let response = client.fetch_metadata(request).await.into_diagnostic()?; let records = response.into_inner().records; - let crds: Vec = records + let metadata: Vec = records .iter() - .map(|json| serde_json::from_str(json)) - .collect::>() - .into_diagnostic()?; + .map(|m| { + Ok(ResourceMetadata { + options: serde_json::from_str(&m.options).into_diagnostic()?, + crd: serde_json::from_str(&m.crd).into_diagnostic()?, + }) + }) + .collect::>>()?; + + Ok(metadata) +} + +#[derive(Debug, Deserialize)] +pub struct ResourceMetadataOption { + pub description: String, + pub spec: serde_json::Value, +} - Ok(crds) +#[derive(Debug, Deserialize)] +pub struct ResourceMetadata { + pub options: Vec, + pub crd: CustomResourceDefinition, } diff --git a/src/rpc/projects/mod.rs b/src/rpc/projects/mod.rs index 6911264..a56fea1 100644 --- a/src/rpc/projects/mod.rs +++ b/src/rpc/projects/mod.rs @@ -29,6 +29,30 @@ pub async fn find(access_token: &str) -> miette::Result> { Ok(records) } +pub async fn find_by_id(credential: auth::Credential, id: &str) -> miette::Result { + let interceptor = auth::interceptor(credential).await; + + let rpc_url = get_base_url(); + let channel = Channel::builder(rpc_url.parse().into_diagnostic()?) + .connect() + .await + .into_diagnostic()?; + + let mut client = + proto::project_service_client::ProjectServiceClient::with_interceptor(channel, interceptor); + + let request = tonic::Request::new(proto::FetchProjectByIdRequest { id: id.into() }); + + let response = client + .fetch_project_by_id(request) + .await + .into_diagnostic()?; + + let record = &response.into_inner().records[0]; + + Ok(record.clone()) +} + pub async fn create_project(access_token: &str, name: &str) -> miette::Result { let credential = auth::Credential::Auth0(access_token.to_owned()); let interceptor = auth::interceptor(credential).await; diff --git a/src/utils.rs b/src/utils.rs deleted file mode 100644 index c98f23a..0000000 --- a/src/utils.rs +++ /dev/null @@ -1,30 +0,0 @@ -use std::{collections::BTreeMap, str::FromStr}; - -use k8s_openapi::apiextensions_apiserver::pkg::apis::apiextensions::v1::{ - CustomResourceDefinition, JSONSchemaProps, -}; - -pub fn get_spec_from_crd( - crd: &CustomResourceDefinition, -) -> Option> { - let version = crd.spec.versions.last()?; - let schema = version.schema.clone()?.open_api_v3_schema?.properties?; - schema.get("spec")?.properties.clone() -} - -pub enum KnownField { - Network, - OperatorVersion, -} - -impl FromStr for KnownField { - type Err = miette::Error; - - fn from_str(s: &str) -> std::result::Result { - match s { - "network" => Ok(Self::Network), - "operatorVersion" => Ok(Self::OperatorVersion), - _ => Err(miette::Error::msg("field isnt known")), - } - } -}