Skip to content

Commit

Permalink
feat(cli): handle tRPC errors (#236)
Browse files Browse the repository at this point in the history
* feat(cli): handle tRPC errors

* chore: add changeset

* refactor(cli): use anyhow Result instead of io
  • Loading branch information
QuiiBz committed Nov 4, 2022
1 parent f5c17e1 commit 6b44882
Show file tree
Hide file tree
Showing 11 changed files with 236 additions and 253 deletions.
5 changes: 5 additions & 0 deletions .changeset/slow-eggs-return.md
@@ -0,0 +1,5 @@
---
'@lagon/cli': patch
---

Properly handle tRPC errors
10 changes: 4 additions & 6 deletions packages/cli/src/commands/build.rs
@@ -1,14 +1,12 @@
use std::{fs, io, path::PathBuf};
use std::{fs, path::PathBuf};

use anyhow::Result;

use crate::utils::{
bundle_function, debug, print_progress, success, validate_code_file, validate_public_dir,
};

pub fn build(
file: PathBuf,
client: Option<PathBuf>,
public_dir: Option<PathBuf>,
) -> io::Result<()> {
pub fn build(file: PathBuf, client: Option<PathBuf>, public_dir: Option<PathBuf>) -> Result<()> {
validate_code_file(&file)?;

let client = match client {
Expand Down
183 changes: 89 additions & 94 deletions packages/cli/src/commands/deploy.rs
@@ -1,9 +1,9 @@
use std::{
fmt::{Display, Formatter},
io::{self, Error, ErrorKind},
path::PathBuf,
};

use anyhow::{anyhow, Result};
use dialoguer::{Confirm, Input, Select};
use serde::{Deserialize, Serialize};

Expand Down Expand Up @@ -58,12 +58,11 @@ pub async fn deploy(
client: Option<PathBuf>,
public_dir: Option<PathBuf>,
_force: bool,
) -> io::Result<()> {
) -> Result<()> {
let config = Config::new()?;

if config.token.is_none() {
return Err(Error::new(
ErrorKind::Other,
return Err(anyhow!(
"You are not logged in. Please login with `lagon login`",
));
}
Expand All @@ -79,97 +78,93 @@ pub async fn deploy(
};

let public_dir = validate_public_dir(public_dir)?;
let function_config = get_function_config()?;

if function_config.is_none() {
println!("{}", debug("No deployment config found..."));
println!();

let trpc_client = TrpcClient::new(&config);
let response = trpc_client
.query::<(), OrganizationsResponse>("organizationsList", None)
.await
.unwrap();
let organizations = response.result.data;

let index = Select::new().items(&organizations).default(0).interact()?;
let organization = &organizations[index];

match Confirm::new()
.with_prompt(info("Link to an existing Function?"))
.interact()?
{
true => {
let response = trpc_client
.query::<(), FunctionsResponse>("functionsList", None)
.await
.unwrap();

let index = Select::new()
.items(&response.result.data)
.default(0)
.interact()?;
let function = &response.result.data[index];

write_function_config(DeploymentConfig {
function_id: function.id.clone(),
organization_id: organization.id.clone(),
})?;

create_deployment(function.id.clone(), &file, &client, &public_dir, &config)
.await?;
}
false => {
let name = Input::<String>::new()
.with_prompt(info("What is the name of this new Function?"))
.interact_text()?;

println!();
let message = format!("Creating Function {}...", name);
let end_progress = print_progress(&message);

let response = trpc_client
.mutation::<CreateFunctionRequest, CreateFunctionResponse>(
"functionCreate",
CreateFunctionRequest {
name,
domains: Vec::new(),
env: Vec::new(),
cron: None,
},
)
.await
.unwrap();

end_progress();

write_function_config(DeploymentConfig {
function_id: response.result.data.id.clone(),
organization_id: organization.id.clone(),
})?;

create_deployment(
response.result.data.id,
&file,
&client,
&public_dir,
&config,
)
match get_function_config()? {
None => {
println!("{}", debug("No deployment config found..."));
println!();

let trpc_client = TrpcClient::new(&config);
let response = trpc_client
.query::<(), OrganizationsResponse>("organizationsList", None)
.await?;
}
};
let organizations = response.result.data;

let index = Select::new().items(&organizations).default(0).interact()?;
let organization = &organizations[index];

match Confirm::new()
.with_prompt(info("Link to an existing Function?"))
.interact()?
{
true => {
let response = trpc_client
.query::<(), FunctionsResponse>("functionsList", None)
.await?;

let index = Select::new()
.items(&response.result.data)
.default(0)
.interact()?;
let function = &response.result.data[index];

write_function_config(DeploymentConfig {
function_id: function.id.clone(),
organization_id: organization.id.clone(),
})?;

create_deployment(function.id.clone(), &file, &client, &public_dir, &config)
.await?;
}
false => {
let name = Input::<String>::new()
.with_prompt(info("What is the name of this new Function?"))
.interact_text()?;

println!();
let message = format!("Creating Function {}...", name);
let end_progress = print_progress(&message);

let response = trpc_client
.mutation::<CreateFunctionRequest, CreateFunctionResponse>(
"functionCreate",
CreateFunctionRequest {
name,
domains: Vec::new(),
env: Vec::new(),
cron: None,
},
)
.await?;

end_progress();

write_function_config(DeploymentConfig {
function_id: response.result.data.id.clone(),
organization_id: organization.id.clone(),
})?;

create_deployment(
response.result.data.id,
&file,
&client,
&public_dir,
&config,
)
.await?;
}
};

return Ok(());
Ok(())
}
Some(function_config) => {
create_deployment(
function_config.function_id,
&file,
&client,
&public_dir,
&config,
)
.await
}
}

let function_config = function_config.unwrap();

create_deployment(
function_config.function_id,
&file,
&client,
&public_dir,
&config,
)
.await
}
48 changes: 21 additions & 27 deletions packages/cli/src/commands/dev.rs
@@ -1,4 +1,4 @@
use anyhow::Result;
use anyhow::{Error, Result};
use chrono::offset::Local;
use colored::Colorize;
use envfile::EnvFile;
Expand All @@ -15,16 +15,16 @@ use std::collections::HashMap;
use std::convert::Infallible;
use std::fs;
use std::path::Path;
use std::path::PathBuf;
use std::sync::Arc;
use std::time::Duration;
use std::{io, path::PathBuf};
use tokio::sync::Mutex;

use crate::utils::{
bundle_function, info, input, success, validate_code_file, validate_public_dir, FileCursor,
};

fn parse_environment_variables(env: Option<PathBuf>) -> io::Result<HashMap<String, String>> {
fn parse_environment_variables(env: Option<PathBuf>) -> Result<HashMap<String, String>> {
let mut environment_variables = HashMap::new();

if let Some(path) = env {
Expand Down Expand Up @@ -88,7 +88,9 @@ async fn handle_request(
body: Bytes::from(asset.1.get_ref().to_vec()),
};

tx.send_async(RunResult::Response(response)).await.unwrap();
tx.send_async(RunResult::Response(response))
.await
.unwrap_or(());
} else {
let maybe_request = Request::from_hyper(req).await;

Expand All @@ -105,14 +107,14 @@ async fn handle_request(
request.add_header("X-Forwarded-For".into(), ip);

let mut isolate = Isolate::new(
IsolateOptions::new(String::from_utf8(index.get_ref().to_vec()).unwrap())
IsolateOptions::new(String::from_utf8(index.get_ref().to_vec())?)
.with_environment_variables(environment_variables),
);

isolate.run(request, tx).await;
}

let result = rx.recv_async().await.unwrap();
let result = rx.recv_async().await?;

match result {
RunResult::Stream(stream_result) => {
Expand All @@ -123,11 +125,11 @@ async fn handle_request(

match stream_result {
StreamResult::Start(response) => {
response_tx.send_async(response).await.unwrap();
response_tx.send_async(response).await.unwrap_or(());
}
StreamResult::Data(bytes) => {
let bytes = Bytes::from(bytes);
stream_tx.send_async(Ok(bytes)).await.unwrap();
stream_tx.send_async(Ok(bytes)).await.unwrap_or(());
}
StreamResult::Done => panic!("Got a stream done without data"),
}
Expand All @@ -136,18 +138,18 @@ async fn handle_request(
while let Ok(RunResult::Stream(stream_result)) = rx.recv_async().await {
match stream_result {
StreamResult::Start(response) => {
response_tx.send_async(response).await.unwrap();
response_tx.send_async(response).await.unwrap_or(());
}
StreamResult::Data(bytes) => {
let bytes = Bytes::from(bytes);
stream_tx.send_async(Ok(bytes)).await.unwrap();
stream_tx.send_async(Ok(bytes)).await.unwrap_or(());
}
_ => {}
}
}
});

let response = response_rx.recv_async().await.unwrap();
let response = response_rx.recv_async().await?;
let hyper_response = Builder::try_from(&response)?.body(body)?;

Ok(hyper_response)
Expand All @@ -157,16 +159,12 @@ async fn handle_request(

Ok(hyper_response)
}
RunResult::Error(error) => Ok(HyperResponse::builder()
.status(500)
.body(error.into())
.unwrap()),
RunResult::Error(error) => Ok(HyperResponse::builder().status(500).body(error.into())?),
RunResult::Timeout => Ok(HyperResponse::new("Timeouted".into())),
RunResult::MemoryLimit => Ok(HyperResponse::new("MemoryLimited".into())),
RunResult::NotFound => Ok(HyperResponse::builder()
.status(404)
.body("Deployment not found".into())
.unwrap()),
.body("Deployment not found".into())?),
}
}

Expand All @@ -177,7 +175,7 @@ pub async fn dev(
port: Option<u16>,
hostname: Option<String>,
env: Option<PathBuf>,
) -> io::Result<()> {
) -> Result<()> {
validate_code_file(&file)?;

let client = match client {
Expand All @@ -199,8 +197,7 @@ pub async fn dev(
hostname.unwrap_or_else(|| "127.0.0.1".into()),
port.unwrap_or(1234)
)
.parse()
.unwrap();
.parse()?;

let server_content = content.clone();
let environment_variables = parse_environment_variables(env)?;
Expand Down Expand Up @@ -228,14 +225,11 @@ pub async fn dev(
let mut watcher = RecommendedWatcher::new(
tx,
Config::default().with_poll_interval(Duration::from_secs(1)),
)
.unwrap();
)?;

let path = fs::canonicalize(&file)?;

watcher
.watch(path.parent().unwrap(), RecursiveMode::Recursive)
.unwrap();
watcher.watch(path.parent().unwrap(), RecursiveMode::Recursive)?;

let watcher_content = content.clone();

Expand All @@ -252,7 +246,7 @@ pub async fn dev(
*content.lock().await = (index, assets);
}

Ok::<(), io::Error>(())
Ok::<(), Error>(())
});

println!();
Expand All @@ -261,7 +255,7 @@ pub async fn dev(
println!(" {} http://{}", "➤".black(), format!("{}", addr).blue());
println!();

server.await.unwrap();
server.await?;
runtime.dispose();

Ok(())
Expand Down

0 comments on commit 6b44882

Please sign in to comment.