From 994d4c5495aff95cb2e467d2fe330ed7be04f9b9 Mon Sep 17 00:00:00 2001 From: Hans Larsen Date: Thu, 12 Sep 2019 16:14:10 -0700 Subject: [PATCH 1/6] feat: add canister subcommand It can install a canister for now. --- dfx/src/commands/canister/install.rs | 21 ++++++++++++ dfx/src/commands/canister/mod.rs | 48 ++++++++++++++++++++++++++++ dfx/src/commands/mod.rs | 2 ++ 3 files changed, 71 insertions(+) create mode 100644 dfx/src/commands/canister/install.rs create mode 100644 dfx/src/commands/canister/mod.rs diff --git a/dfx/src/commands/canister/install.rs b/dfx/src/commands/canister/install.rs new file mode 100644 index 0000000000..937f0d771a --- /dev/null +++ b/dfx/src/commands/canister/install.rs @@ -0,0 +1,21 @@ +use crate::lib::env::{ClientEnv, ProjectConfigEnv}; +use crate::lib::error::DfxResult; +use clap::{App, Arg, ArgMatches, SubCommand}; + +pub fn construct() -> App<'static, 'static> { + SubCommand::with_name("install") + .about("Install a canister. Will build it") + .arg(Arg::with_name("canister").help("The canister name to build.")) +} + +pub fn exec(env: &T, _args: &ArgMatches<'_>) -> DfxResult +where + T: ClientEnv + ProjectConfigEnv, +{ + // Read the config. + let _config = env.get_config().unwrap(); + + println!("INSTALL!"); + + Ok(()) +} diff --git a/dfx/src/commands/canister/mod.rs b/dfx/src/commands/canister/mod.rs new file mode 100644 index 0000000000..cbe2915583 --- /dev/null +++ b/dfx/src/commands/canister/mod.rs @@ -0,0 +1,48 @@ +use crate::commands::CliCommand; +use crate::lib::env::{ClientEnv, ProjectConfigEnv}; +use crate::lib::error::{DfxError, DfxResult}; +use clap::{App, ArgMatches, SubCommand}; + +mod install; + +fn builtins() -> Vec> +where + T: ClientEnv + ProjectConfigEnv, +{ + vec![CliCommand::new(install::construct(), install::exec)] +} + +pub fn construct() -> App<'static, 'static> +where + T: ClientEnv + ProjectConfigEnv, +{ + SubCommand::with_name("canister") + .about("Manage canisters from a network.") + .subcommands( + builtins::() + .into_iter() + .map(|x| x.get_subcommand().clone()), + ) +} + +pub fn exec(env: &T, args: &ArgMatches<'_>) -> DfxResult +where + T: ClientEnv + ProjectConfigEnv, +{ + let subcommand = args.subcommand(); + + if let (name, Some(subcommand_args)) = subcommand { + match builtins().into_iter().find(|x| name == x.get_name()) { + Some(cmd) => cmd.execute(env, subcommand_args), + None => Err(DfxError::UnknownCommand(format!( + "Command {} not found.", + name + ))), + } + } else { + construct::().write_help(&mut std::io::stderr())?; + println!(); + println!(); + Ok(()) + } +} diff --git a/dfx/src/commands/mod.rs b/dfx/src/commands/mod.rs index 0f30848654..6059f23e9b 100644 --- a/dfx/src/commands/mod.rs +++ b/dfx/src/commands/mod.rs @@ -6,6 +6,7 @@ use clap::ArgMatches; mod build; mod call; +mod canister; mod new; mod start; @@ -41,6 +42,7 @@ where vec![ CliCommand::new(build::construct(), build::exec), CliCommand::new(call::construct(), call::exec), + CliCommand::new(canister::construct::(), canister::exec), CliCommand::new(new::construct(), new::exec), CliCommand::new(start::construct(), start::exec), ] From c1dd7d83a0e182faf821c68d3e7a281f1ae54d19 Mon Sep 17 00:00:00 2001 From: Hans Larsen Date: Thu, 12 Sep 2019 16:41:35 -0700 Subject: [PATCH 2/6] refactor: rename Request/Response for read endpoint --- dfx/src/commands/call.rs | 10 +++++----- dfx/src/lib/api_client.rs | 39 +++++++++++++++++++++------------------ dfx/src/lib/error.rs | 4 ++-- 3 files changed, 28 insertions(+), 25 deletions(-) diff --git a/dfx/src/commands/call.rs b/dfx/src/commands/call.rs index ac8bf27318..065a19c1fa 100644 --- a/dfx/src/commands/call.rs +++ b/dfx/src/commands/call.rs @@ -39,21 +39,21 @@ where }, ) .and_then(|r| match r { - Response::Accepted => { - println!("Accepted"); + ReadResponse::Pending => { + println!("Pending"); ok(()) } - Response::Replied { + ReadResponse::Replied { reply: QueryResponseReply { arg: Blob(blob) }, } => { println!("{}", String::from_utf8_lossy(&blob)); ok(()) } - Response::Rejected { + ReadResponse::Rejected { reject_code, reject_message, } => err(DfxError::ClientError(reject_code, reject_message)), - Response::Unknown => err(DfxError::Unknown("Unknown response".to_owned())), + ReadResponse::Unknown => err(DfxError::Unknown("Unknown response".to_owned())), }); let mut runtime = Runtime::new().expect("Unable to create a runtime"); diff --git a/dfx/src/lib/api_client.rs b/dfx/src/lib/api_client.rs index 6db8449f6c..7c0bf8f1bc 100644 --- a/dfx/src/lib/api_client.rs +++ b/dfx/src/lib/api_client.rs @@ -40,11 +40,11 @@ pub struct ClientConfig { pub url: String, } -/// Request payloads +/// Request payloads for the /api/v1/read endpoint. #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] #[serde(tag = "request_type")] -enum Request { +enum ReadRequest { Query { #[serde(flatten)] request: CanisterQueryCall, @@ -55,13 +55,13 @@ enum Request { #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] #[serde(tag = "status")] -pub enum Response { - Accepted, +pub enum ReadResponse { + Pending, Replied { reply: A, }, Rejected { - reject_code: RejectCode, + reject_code: ReadRejectCode, reject_message: String, }, Unknown, @@ -70,7 +70,7 @@ pub enum Response { /// Response reject codes #[derive(Debug, PartialEq, Eq, Serialize_repr, Deserialize_repr)] #[repr(u8)] -pub enum RejectCode { +pub enum ReadRejectCode { SysFatal = 1, SysTransient = 2, DestinationInvalid = 3, @@ -80,7 +80,10 @@ pub enum RejectCode { /// A read request. Intended to remain private in favor of exposing specialized /// functions like `query` instead. -fn read(client: Client, request: Request) -> impl Future, Error = DfxError> +fn read( + client: Client, + request: ReadRequest, +) -> impl Future, Error = DfxError> where A: serde::de::DeserializeOwned, { @@ -128,8 +131,8 @@ pub fn ping(client: Client) -> impl Future { pub fn query( client: Client, request: CanisterQueryCall, -) -> impl Future, Error = DfxError> { - read(client, Request::Query { request }) +) -> impl Future, Error = DfxError> { + read(client, ReadRequest::Query { request }) } /// A canister query call request payload @@ -161,7 +164,7 @@ mod tests { let method_name = "main".to_string(); let arg = Blob(vec![]); - let request = Request::Query { + let request = ReadRequest::Query { request: CanisterQueryCall { canister_id, method_name: method_name.clone(), @@ -219,10 +222,10 @@ mod tests { .collect(), ); - let actual: Response = + let actual: ReadResponse = serde_cbor::from_slice(&serde_cbor::to_vec(&response).unwrap()).unwrap(); - let expected = Response::Replied { + let expected = ReadResponse::Replied { reply: QueryResponseReply { arg: Blob(arg.clone()), }, @@ -235,7 +238,7 @@ mod tests { fn query_response_replied() { let _ = env_logger::try_init(); - let response = Response::Replied { + let response = ReadResponse::Replied { reply: QueryResponseReply { arg: Blob(Vec::from("Hello World")), }, @@ -293,11 +296,11 @@ mod tests { .collect(), ); - let actual: Response = + let actual: ReadResponse = serde_cbor::from_slice(&serde_cbor::to_vec(&response).unwrap()).unwrap(); - let expected: Response = Response::Rejected { - reject_code: RejectCode::SysFatal, + let expected: ReadResponse = ReadResponse::Rejected { + reject_code: ReadRejectCode::SysFatal, reject_message: reject_message.clone(), }; @@ -308,8 +311,8 @@ mod tests { fn query_response_rejected() { let _ = env_logger::try_init(); - let response = Response::Rejected { - reject_code: RejectCode::SysFatal, + let response = ReadResponse::Rejected { + reject_code: ReadRejectCode::SysFatal, reject_message: "Fatal error".to_string(), }; diff --git a/dfx/src/lib/error.rs b/dfx/src/lib/error.rs index b11fe150e6..e23db6ee58 100644 --- a/dfx/src/lib/error.rs +++ b/dfx/src/lib/error.rs @@ -1,4 +1,4 @@ -use crate::lib::api_client::RejectCode; +use crate::lib::api_client::ReadRejectCode; #[derive(Debug)] pub enum BuildErrorKind { @@ -24,7 +24,7 @@ pub enum DfxError { // Cannot create a new project because the directory already exists. ProjectExists(), - ClientError(RejectCode, String), + ClientError(ReadRejectCode, String), Unknown(String), } From 70f7acd229a147978352b257981b600f00c9621c Mon Sep 17 00:00:00 2001 From: Hans Larsen Date: Mon, 16 Sep 2019 13:22:08 -0700 Subject: [PATCH 3/6] tool: add client timestamp to version when running in debug --- dfx/shell.nix | 3 +++ dfx/src/lib/env.rs | 23 ++++++++++++++++++++--- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/dfx/shell.nix b/dfx/shell.nix index fca7b566a7..638ec1c8aa 100644 --- a/dfx/shell.nix +++ b/dfx/shell.nix @@ -7,5 +7,8 @@ pkgs.mkCiShell { inputsFrom = [ dfx ]; + shellHook = '' + export DFX_TIMESTAMP_DEBUG_MODE_ONLY=$(date +%s) + ''; DFX_ASSETS = dfx.DFX_ASSETS; } diff --git a/dfx/src/lib/env.rs b/dfx/src/lib/env.rs index 3ee1627e27..6744ed9fe9 100644 --- a/dfx/src/lib/env.rs +++ b/dfx/src/lib/env.rs @@ -174,8 +174,25 @@ impl VersionEnv for GlobalEnvironment { impl GlobalEnvironment { pub fn from_current_dir() -> DfxResult { - Ok(GlobalEnvironment { - version: DFX_VERSION.to_owned(), - }) + #[cfg(debug_assertions)] + { + // In debug, add a timestamp of the compilation at the end of version to ensure all + // debug runs are unique (and cached uniquely). + Ok(GlobalEnvironment { + version: format!( + "{}-{:?}", + DFX_VERSION.to_owned(), + std::env::var_os("DFX_TIMESTAMP_DEBUG_MODE_ONLY") + .unwrap_or_else(|| std::ffi::OsString::from("local-debug")) + ), + }) + } + + #[cfg(not(debug_assertions))] + { + Ok(GlobalEnvironment { + version: DFX_VERSION.to_owned(), + }) + } } } From 10d796f685e24cc4b9fb46dfa66b5a3a755e94f7 Mon Sep 17 00:00:00 2001 From: Hans Larsen Date: Thu, 12 Sep 2019 17:10:05 -0700 Subject: [PATCH 4/6] feat: add canister install command --- dfx/src/commands/canister/install.rs | 41 ++++++- dfx/src/config/cache.rs | 23 ++-- dfx/src/config/mod.rs | 29 ++++- dfx/src/lib/api_client.rs | 157 ++++++++++++++++++++++++++- dfx/src/lib/env.rs | 27 +---- 5 files changed, 233 insertions(+), 44 deletions(-) diff --git a/dfx/src/commands/canister/install.rs b/dfx/src/commands/canister/install.rs index 937f0d771a..0b74554a28 100644 --- a/dfx/src/commands/canister/install.rs +++ b/dfx/src/commands/canister/install.rs @@ -1,21 +1,52 @@ +use crate::lib::api_client::{install_code, Blob}; use crate::lib::env::{ClientEnv, ProjectConfigEnv}; use crate::lib::error::DfxResult; use clap::{App, Arg, ArgMatches, SubCommand}; +use std::path::PathBuf; +use tokio::runtime::Runtime; + +fn is_number(v: String) -> Result<(), String> { + v.parse::() + .map_err(|_| String::from("The value must be a number.")) + .map(|_| ()) +} pub fn construct() -> App<'static, 'static> { SubCommand::with_name("install") - .about("Install a canister. Will build it") - .arg(Arg::with_name("canister").help("The canister name to build.")) + .about("Install a canister.") + .arg( + Arg::with_name("canister") + .takes_value(true) + .help("The canister ID (a number).") + .required(true) + .validator(is_number), + ) + .arg( + Arg::with_name("wasm") + .help("The wasm file to use.") + .required(true), + ) } -pub fn exec(env: &T, _args: &ArgMatches<'_>) -> DfxResult +pub fn exec(env: &T, args: &ArgMatches<'_>) -> DfxResult where T: ClientEnv + ProjectConfigEnv, { // Read the config. - let _config = env.get_config().unwrap(); + let config = env.get_config().unwrap(); + let project_root = config.get_path().parent().unwrap(); + + let canister_id = args.value_of("canister").unwrap().parse::()?; + let wasm_path = args.value_of("wasm").unwrap(); + let wasm_path = PathBuf::from(project_root).join(wasm_path); + + let wasm = std::fs::read(wasm_path)?; + let client = env.get_client(); + + let install = install_code(client, canister_id, Blob(wasm), None); - println!("INSTALL!"); + let mut runtime = Runtime::new().expect("Unable to create a runtime"); + runtime.block_on(install)?; Ok(()) } diff --git a/dfx/src/config/cache.rs b/dfx/src/config/cache.rs index 5015fcbf9d..3b0aae54cc 100644 --- a/dfx/src/config/cache.rs +++ b/dfx/src/config/cache.rs @@ -1,6 +1,7 @@ use std::io::{Error, ErrorKind, Result}; use std::path::PathBuf; +use crate::config::dfx_version; use crate::util; pub fn get_bin_cache_root() -> Result { @@ -31,14 +32,7 @@ pub fn get_bin_cache_root() -> Result { pub fn get_bin_cache(v: &str) -> Result { let root = get_bin_cache_root()?; - - match v { - "0.2.0" => Ok(root.join("0.2.0")), - v => Err(Error::new( - ErrorKind::Other, - format!("Unknown version: {}", v), - )), - } + Ok(root.join(v)) } pub fn is_version_installed(v: &str) -> Result { @@ -60,15 +54,14 @@ pub fn install_version(v: &str) -> Result { return Ok(p); } - match v { - "0.2.0" => { - util::assets::binary_cache()?.unpack(p.as_path())?; - Ok(p) - } - v => Err(Error::new( + if v == dfx_version() { + util::assets::binary_cache()?.unpack(p.as_path())?; + Ok(p) + } else { + Err(Error::new( ErrorKind::Other, format!("Unknown version: {}", v), - )), + )) } } diff --git a/dfx/src/config/mod.rs b/dfx/src/config/mod.rs index 95e1aa2713..070c152a76 100644 --- a/dfx/src/config/mod.rs +++ b/dfx/src/config/mod.rs @@ -1,4 +1,31 @@ pub mod cache; pub mod dfinity; -pub const DFX_VERSION: &str = env!("CARGO_PKG_VERSION"); +static mut DFX_VERSION: Option = None; +/// Returns the version of DFX that was built. +/// In debug, add a timestamp of the upstream compilation at the end of version to ensure all +/// debug runs are unique (and cached uniquely). +/// That timestamp is taken from the DFX_TIMESTAMP_DEBUG_MODE_ONLY env var that is set in +/// Nix. +pub fn dfx_version() -> &'static str { + unsafe { + match &DFX_VERSION { + Some(x) => x.as_str(), + None => { + let version = env!("CARGO_PKG_VERSION"); + + #[cfg(debug_assertions)] + { + DFX_VERSION = Some(format!( + "{}-{}", + version, + std::env::var("DFX_TIMESTAMP_DEBUG_MODE_ONLY") + .unwrap_or_else(|_| "local-debug".to_owned()) + )); + } + + dfx_version() + } + } + } +} diff --git a/dfx/src/lib/api_client.rs b/dfx/src/lib/api_client.rs index 7c0bf8f1bc..4f7b1d2ff8 100644 --- a/dfx/src/lib/api_client.rs +++ b/dfx/src/lib/api_client.rs @@ -12,6 +12,9 @@ use serde_repr::{Deserialize_repr, Serialize_repr}; // bytestring pub struct Blob(#[serde(with = "serde_bytes")] pub Vec); +const HTTP_ENDPOINT_READ: &str = "/api/v1/read"; +const HTTP_ENDPOINT_SUBMIT: &str = "/api/v1/submit"; + type CanisterId = u64; #[derive(Clone)] @@ -51,7 +54,7 @@ enum ReadRequest { }, } -/// Response payloads +/// Response payloads for the /api/v1/read endpoint. #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] #[serde(tag = "status")] @@ -78,6 +81,18 @@ pub enum ReadRejectCode { CanisterError = 5, } +/// Request payloads for the /api/v1/submit endpoint. +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +#[serde(tag = "request_type")] +enum SubmitRequest { + InstallCode { + canister_id: CanisterId, + module: Blob, + arg: Blob, + }, +} + /// A read request. Intended to remain private in favor of exposing specialized /// functions like `query` instead. fn read( @@ -123,6 +138,29 @@ pub fn ping(client: Client) -> impl Future { }) } +/// A submit request. Intended to remain private in favor of exposing specialized +/// functions like `install_code` instead. +fn submit( + client: Client, + request: SubmitRequest, +) -> impl Future { + let endpoint = format!("{}/api/v1/submit", client.url); + let parsed = reqwest::Url::parse(&endpoint).map_err(DfxError::Url); + result(parsed).and_then(move |url| { + let mut http_request = reqwest::r#async::Request::new(reqwest::Method::POST, url); + let headers = http_request.headers_mut(); + headers.insert( + reqwest::header::CONTENT_TYPE, + "application/cbor".parse().unwrap(), + ); + let body = http_request.body_mut(); + body.get_or_insert(reqwest::r#async::Body::from( + serde_cbor::to_vec(&request).unwrap(), + )); + client.execute(http_request).map_err(DfxError::Reqwest) + }) +} + /// Canister query call /// /// Canister methods that do not change the canister state in a meaningful way @@ -135,6 +173,31 @@ pub fn query( read(client, ReadRequest::Query { request }) } +/// Canister Install call +pub fn install_code( + client: Client, + canister_id: CanisterId, + module: Blob, + arg: Option, +) -> impl Future { + submit( + client, + SubmitRequest::InstallCode { + canister_id, + module, + arg: arg.unwrap_or_else(|| Blob(vec![])), + }, + ) + .and_then(|response| { + result( + response + .error_for_status() + .map(|_| ()) + .map_err(DfxError::from), + ) + }) +} + /// A canister query call request payload #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct CanisterQueryCall { @@ -345,4 +408,96 @@ mod tests { Err(e) => assert!(false, format!("{:#?}", e)), } } + + #[test] + fn install_code_request_serialization() { + use serde_cbor::Value; + use std::convert::TryInto; + + let canister_id = 1; + let module = Blob(vec![1]); + let arg = Blob(vec![2]); + + let request = SubmitRequest::InstallCode { + canister_id, + module, + arg, + }; + + let actual: Value = serde_cbor::from_slice(&serde_cbor::to_vec(&request).unwrap()).unwrap(); + + let expected = Value::Map( + vec![ + ( + Value::Text("request_type".to_string()), + Value::Text("install_code".to_string()), + ), + ( + Value::Text("canister_id".to_string()), + Value::Integer(canister_id.try_into().unwrap()), + ), + (Value::Text("module".to_string()), Value::Bytes(vec![1])), + (Value::Text("arg".to_string()), Value::Bytes(vec![2])), + ] + .into_iter() + .collect(), + ); + + assert_eq!(actual, expected); + } + + #[test] + fn install_code_response_replied() { + let _ = env_logger::try_init(); + + let _m = mock("POST", "/api/v1/submit") + .with_status(200) + .with_header("content-type", "application/cbor") + .create(); + + let client = Client::new(ClientConfig { + url: mockito::server_url(), + }); + + let future = install_code(client, 1, Blob(vec![1]), None); + + let mut runtime = tokio::runtime::Runtime::new().expect("Unable to create a runtime"); + let result = runtime.block_on(future); + + _m.assert(); + + match result { + Ok(()) => {} + Err(e) => assert!(false, format!("{:#?}", e)), + } + } + + #[test] + fn install_code_response_rejected() { + let _ = env_logger::try_init(); + + let _m = mock("POST", "/api/v1/submit") + .with_status(400) + .with_header("content-type", "application/cbor") + .create(); + + let client = Client::new(ClientConfig { + url: mockito::server_url(), + }); + + let future = install_code(client, 1, Blob(vec![1]), None); + + let mut runtime = tokio::runtime::Runtime::new().expect("Unable to create a runtime"); + let result = runtime.block_on(future); + + _m.assert(); + + match result { + Ok(()) => assert!(false, "Install succeeded."), + Err(e) => match e { + DfxError::Reqwest(_err) => (), + _ => assert!(false, format!("{:#?}", e)), + }, + } + } } diff --git a/dfx/src/lib/env.rs b/dfx/src/lib/env.rs index 6744ed9fe9..82e77645b8 100644 --- a/dfx/src/lib/env.rs +++ b/dfx/src/lib/env.rs @@ -1,5 +1,5 @@ use crate::config::dfinity::Config; -use crate::config::{cache, DFX_VERSION}; +use crate::config::{cache, dfx_version}; use crate::lib::api_client::{Client, ClientConfig}; use crate::lib::error::DfxResult; use std::cell::RefCell; @@ -116,7 +116,7 @@ impl InProjectEnvironment { version: config .get_config() .get_dfx() - .unwrap_or_else(|| DFX_VERSION.to_owned()), + .unwrap_or_else(|| dfx_version().to_owned()), config, client: RefCell::new(None), }) @@ -174,25 +174,8 @@ impl VersionEnv for GlobalEnvironment { impl GlobalEnvironment { pub fn from_current_dir() -> DfxResult { - #[cfg(debug_assertions)] - { - // In debug, add a timestamp of the compilation at the end of version to ensure all - // debug runs are unique (and cached uniquely). - Ok(GlobalEnvironment { - version: format!( - "{}-{:?}", - DFX_VERSION.to_owned(), - std::env::var_os("DFX_TIMESTAMP_DEBUG_MODE_ONLY") - .unwrap_or_else(|| std::ffi::OsString::from("local-debug")) - ), - }) - } - - #[cfg(not(debug_assertions))] - { - Ok(GlobalEnvironment { - version: DFX_VERSION.to_owned(), - }) - } + Ok(GlobalEnvironment { + version: dfx_version().to_owned(), + }) } } From 37de2d57d6d5a1cd4c37a5f3d6333104d3552512 Mon Sep 17 00:00:00 2001 From: Hans Larsen Date: Tue, 17 Sep 2019 20:52:17 -0700 Subject: [PATCH 5/6] refactor: use URLs instead of passing strings around --- dfx/src/lib/api_client.rs | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/dfx/src/lib/api_client.rs b/dfx/src/lib/api_client.rs index 4f7b1d2ff8..d2a3c5f886 100644 --- a/dfx/src/lib/api_client.rs +++ b/dfx/src/lib/api_client.rs @@ -12,22 +12,22 @@ use serde_repr::{Deserialize_repr, Serialize_repr}; // bytestring pub struct Blob(#[serde(with = "serde_bytes")] pub Vec); -const HTTP_ENDPOINT_READ: &str = "/api/v1/read"; -const HTTP_ENDPOINT_SUBMIT: &str = "/api/v1/submit"; - type CanisterId = u64; #[derive(Clone)] pub struct Client { client: ReqwestClient, - url: String, + url: reqwest::Url, } impl Client { pub fn new(config: ClientConfig) -> Client { Client { client: ReqwestClient::new(), - url: config.url, + url: reqwest::Url::parse(config.url.as_str()) + .expect("Invalid client URL.") + .join("api/v1/") + .unwrap(), } } @@ -102,9 +102,7 @@ fn read( where A: serde::de::DeserializeOwned, { - let endpoint = format!("{}/api/v1/read", client.url); - let parsed = reqwest::Url::parse(&endpoint).map_err(DfxError::Url); - result(parsed) + result(client.url.join("read").map_err(DfxError::Url)) .and_then(move |url| { let mut http_request = reqwest::r#async::Request::new(reqwest::Method::POST, url); let headers = http_request.headers_mut(); @@ -127,8 +125,7 @@ where /// Ping a client and return ok if the client is started. pub fn ping(client: Client) -> impl Future { - let parsed = reqwest::Url::parse(&client.url).map_err(DfxError::Url); - result(parsed).and_then(move |url| { + ok(client.url.clone()).and_then(move |url| { let http_request = reqwest::r#async::Request::new(reqwest::Method::GET, url); client @@ -144,9 +141,7 @@ fn submit( client: Client, request: SubmitRequest, ) -> impl Future { - let endpoint = format!("{}/api/v1/submit", client.url); - let parsed = reqwest::Url::parse(&endpoint).map_err(DfxError::Url); - result(parsed).and_then(move |url| { + result(client.url.join("submit").map_err(DfxError::Url)).and_then(move |url| { let mut http_request = reqwest::r#async::Request::new(reqwest::Method::POST, url); let headers = http_request.headers_mut(); headers.insert( From d616773a1ded6fdef59fc903c505059ed1ba70e3 Mon Sep 17 00:00:00 2001 From: Hans Larsen Date: Tue, 17 Sep 2019 20:58:52 -0700 Subject: [PATCH 6/6] test: fix the build test temp_dir() returns the root temp directory and not a new one. --- dfx/Cargo.lock | 1 + dfx/Cargo.toml | 1 + dfx/src/commands/build.rs | 3 ++- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/dfx/Cargo.lock b/dfx/Cargo.lock index 946bdfddec..2a51b03ecf 100644 --- a/dfx/Cargo.lock +++ b/dfx/Cargo.lock @@ -321,6 +321,7 @@ dependencies = [ "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", "serde_repr 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "tar 0.4.26 (registry+https://github.com/rust-lang/crates.io-index)", + "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", "wabt 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/dfx/Cargo.toml b/dfx/Cargo.toml index ceaef74b4e..b2262b5d4c 100644 --- a/dfx/Cargo.toml +++ b/dfx/Cargo.toml @@ -32,3 +32,4 @@ wabt = "0.9.2" [dev-dependencies] env_logger = "0.6" mockito = "0.20.0" +tempfile = "3.1.0" diff --git a/dfx/src/commands/build.rs b/dfx/src/commands/build.rs index db6584caa2..02c3d45491 100644 --- a/dfx/src/commands/build.rs +++ b/dfx/src/commands/build.rs @@ -180,7 +180,8 @@ mod tests { let env = TestEnv {}; let wat = r#"(module )"#; - let temp_path = temp_dir(); + let temp_dir = tempfile::tempdir().unwrap(); + let temp_path = temp_dir.into_path(); let input_path = temp_path.join("input.wat"); let output_path = temp_path.join("output.wasm");