Skip to content

Commit

Permalink
polishing CLI UX (#144)
Browse files Browse the repository at this point in the history
create-moose-app creates the project in a new directory
link to console if data comes from curl
shows help if command is missing
spinners around tasks that take a while
  • Loading branch information
phiSgr authored Jan 23, 2024
1 parent 200e300 commit 3ba1947
Show file tree
Hide file tree
Showing 15 changed files with 167 additions and 37 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ $ npm install -g @514labs/moose-cli

## Config file

The config file is located in `~/.igloo-config.toml`
The config file is located in `~/.igloo/config.toml`

You can create one with the following content

Expand All @@ -36,7 +36,7 @@ You can create one with the following content

# Coming soon wall on all the CLI commands as we build the MVP.
# if you want to try features as they are built, set this to false
coming_soon_wall=true
coming_soon_wall=false
```

## Versioning
Expand Down
6 changes: 6 additions & 0 deletions apps/create-moose-app/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#!/usr/bin/env node

import { spawnSync } from "child_process";
import { mkdirSync } from "fs";

/**
* Returns the executable path which is located inside `node_modules`
Expand Down Expand Up @@ -36,8 +37,13 @@ function getExePath() {
*/
function run() {
const args = process.argv.slice(2);
const name = args[0];
if (name !== undefined) {
mkdirSync(name);
}
const processResult = spawnSync(getExePath(), ["init"].concat(args), {
stdio: "inherit",
cwd: name,
});
process.exit(processResult.status ?? 0);
}
Expand Down
46 changes: 46 additions & 0 deletions apps/igloo-kit-cli/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions apps/igloo-kit-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ hyper-util = { version = "0.1", features = ["full"] }
http-body-util = "0.1"
lazy_static = "1.4.0"
anyhow = "1.0"
spinners = "4.1.1"

[dev-dependencies]
clickhouse = { version = "0.11.5", features = ["uuid", "test-util"] }
Expand Down
21 changes: 11 additions & 10 deletions apps/igloo-kit-cli/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use self::{
RunMode,
},
};
use crate::cli::settings::init_config_file;
use crate::project::Project;
use clap::Parser;
use commands::Commands;
Expand All @@ -27,7 +28,7 @@ use settings::{read_settings, Settings};
use std::path::Path;

#[derive(Parser)]
#[command(author, version, about, long_about = None)]
#[command(author, version, about, long_about = None, arg_required_else_help(true))]
struct Cli {
/// Optional name to operate on
name: Option<String>,
Expand All @@ -42,17 +43,17 @@ struct Cli {
debug: bool,

#[command(subcommand)]
command: Option<Commands>,
command: Commands,
}

async fn top_command_handler(settings: Settings, commands: &Option<Commands>) {
async fn top_command_handler(settings: Settings, commands: &Commands) {
if !settings.features.coming_soon_wall {
match commands {
Some(Commands::Init {
Commands::Init {
name,
language,
location,
}) => {
} => {
info!(
"Running init command with name: {}, language: {}, location: {}",
name, language, location
Expand All @@ -74,7 +75,7 @@ async fn top_command_handler(settings: Settings, commands: &Option<Commands>) {
.write_to_file()
.expect("Failed to write project to file");
}
Some(Commands::Dev {}) => {
Commands::Dev {} => {
info!("Running dev command");

let project = Project::load_from_current_dir()
Expand All @@ -91,17 +92,17 @@ async fn top_command_handler(settings: Settings, commands: &Option<Commands>) {

routines::start_development_mode(&project).await.unwrap();
}
Some(Commands::Update {}) => {
Commands::Update {} => {
// This command may not be needed if we have incredible automation
todo!("Will update the project's underlying infrastructure based on any added objects")
}
Some(Commands::Stop {}) => {
Commands::Stop {} => {
let mut controller = RoutineController::new();
let run_mode = RunMode::Explicit {};
controller.add_routine(Box::new(StopLocalInfrastructure::new(run_mode)));
controller.run_routines(run_mode);
}
Some(Commands::Clean {}) => {
Commands::Clean {} => {
let run_mode = RunMode::Explicit {};
let project = Project::load_from_current_dir()
.expect("No project found, please run `igloo init` to create a project");
Expand All @@ -110,7 +111,6 @@ async fn top_command_handler(settings: Settings, commands: &Option<Commands>) {
controller.add_routine(Box::new(CleanProject::new(project, run_mode)));
controller.run_routines(run_mode);
}
None => {}
}
} else {
show_message!(MessageType::Banner, Message {
Expand All @@ -122,6 +122,7 @@ async fn top_command_handler(settings: Settings, commands: &Option<Commands>) {

pub async fn cli_run() {
setup_user_directory().expect("Failed to setup igloo user directory");
init_config_file().unwrap();

let config = read_settings().unwrap();
setup_logging(config.logger.clone()).expect("Failed to setup logging");
Expand Down
15 changes: 13 additions & 2 deletions apps/igloo-kit-cli/src/cli/display.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use console::style;
use lazy_static::lazy_static;
use spinners::{Spinner, Spinners};
use std::sync::{Arc, RwLock};

/// # Display Module
Expand All @@ -14,7 +15,7 @@ use std::sync::{Arc, RwLock};
/// MessageType::Info,
/// Message {
/// action: "Loading Config".to_string(),
/// details: "Reading configuration from ~/.igloo-config.toml".to_string(),
/// details: "Reading configuration from ~/.igloo/config.toml".to_string(),
/// });
/// ```
///
Expand All @@ -31,7 +32,7 @@ use std::sync::{Arc, RwLock};
/// ```
/// Message {
/// action: "Loading Config".to_string(),
/// details: "Reading configuration from ~/.igloo-config.toml".to_string(),
/// details: "Reading configuration from ~/.igloo/config.toml".to_string(),
/// }
/// ```
///
Expand Down Expand Up @@ -176,3 +177,13 @@ macro_rules! show_message {
};
};
}

pub fn with_spinner<F, R>(message: &str, f: F) -> R
where
F: FnOnce() -> R,
{
let mut sp = Spinner::new(Spinners::Dots9, message.into());
let res = f();
sp.stop_with_newline();
res
}
27 changes: 24 additions & 3 deletions apps/igloo-kit-cli/src/cli/local_webserver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use crate::framework::controller::RouteMeta;
use crate::infrastructure::stream::redpanda;
use crate::infrastructure::stream::redpanda::ConfiguredProducer;

use crate::infrastructure::console::ConsoleConfig;
use crate::project::Project;
use http_body_util::BodyExt;
use http_body_util::Full;
Expand Down Expand Up @@ -64,6 +65,7 @@ impl Default for LocalWebserverConfig {
struct RouteService {
route_table: Arc<Mutex<HashMap<PathBuf, RouteMeta>>>,
configured_producer: Arc<Mutex<ConfiguredProducer>>,
console_config: ConsoleConfig,
}

impl Service<Request<Incoming>> for RouteService {
Expand All @@ -76,6 +78,7 @@ impl Service<Request<Incoming>> for RouteService {
req,
self.route_table.clone(),
self.configured_producer.clone(),
self.console_config.clone(),
))
}
}
Expand All @@ -100,6 +103,7 @@ async fn ingest_route(
route: PathBuf,
configured_producer: Arc<Mutex<ConfiguredProducer>>,
route_table: Arc<Mutex<HashMap<PathBuf, RouteMeta>>>,
console_config: ConsoleConfig,
) -> Result<Response<Full<Bytes>>, hyper::http::Error> {
show_message!(
MessageType::Info,
Expand All @@ -110,6 +114,15 @@ async fn ingest_route(
);

if route_table.lock().await.contains_key(&route) {
let is_curl = req.headers().get("User-Agent").map_or_else(
|| false,
|user_agent| {
user_agent
.to_str()
.map_or_else(|_| false, |s| s.starts_with("curl"))
},
);

let body = req.collect().await.unwrap().to_bytes().to_vec();

let guard = route_table.lock().await;
Expand All @@ -136,7 +149,12 @@ async fn ingest_route(
details: route.to_str().unwrap().to_string(),
}
);
Ok(Response::new(Full::new(Bytes::from("SUCCESS"))))
let response_bytes = if is_curl {
Bytes::from(format!("Success! Go to http://localhost:{}/infrastructure/views to view your data!", console_config.host_port))
} else {
Bytes::from("SUCCESS")
};
Ok(Response::new(Full::new(response_bytes)))
}
Err(e) => {
println!("Error: {:?}", e);
Expand All @@ -156,6 +174,7 @@ async fn router(
req: Request<hyper::body::Incoming>,
route_table: Arc<Mutex<HashMap<PathBuf, RouteMeta>>>,
configured_producer: Arc<Mutex<ConfiguredProducer>>,
console_config: ConsoleConfig,
) -> Result<Response<Full<Bytes>>, hyper::http::Error> {
debug!(
"HTTP Request Received: {:?}, with Route Table {:?}",
Expand All @@ -178,7 +197,7 @@ async fn router(

match (req.method(), &route_split[..]) {
(&hyper::Method::POST, ["ingest", _]) => {
ingest_route(req, route, configured_producer, route_table).await
ingest_route(req, route, configured_producer, route_table, console_config).await
}

(&hyper::Method::OPTIONS, _) => options_route(),
Expand Down Expand Up @@ -255,6 +274,7 @@ impl Webserver {

let route_table = route_table.clone();
let producer = producer.clone();
let console_config = project.console_config.clone();

// Spawn a tokio task to serve multiple connections concurrently
tokio::task::spawn(async move {
Expand All @@ -263,7 +283,8 @@ impl Webserver {
io,
RouteService {
route_table,
configured_producer: producer
configured_producer: producer,
console_config,
},
).await {
error!("server error: {}", e);
Expand Down
8 changes: 7 additions & 1 deletion apps/igloo-kit-cli/src/cli/routines.rs
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,13 @@ async fn initialize_project_state(
.await;

let route_table_clone = route_table.clone();
let _ = post_current_state_to_console(&configured_client, &producer, route_table_clone).await;
let _ = post_current_state_to_console(
&configured_client,
&producer,
route_table_clone,
project.console_config.clone(),
)
.await;

match crawl_result {
Ok(_) => {
Expand Down
Loading

0 comments on commit 3ba1947

Please sign in to comment.