diff --git a/Cargo.lock b/Cargo.lock index 3dc5f9d0aa..281c2ff6ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -119,7 +119,6 @@ dependencies = [ "backtrace", "buildstructor 0.1.12", "bytes", - "cargo-scaffold 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "clap 3.1.9", "deadpool", "derivative", @@ -168,7 +167,6 @@ dependencies = [ "thiserror", "tokio", "tokio-util 0.7.1", - "toml", "tonic", "tower", "tower-http", @@ -264,13 +262,14 @@ dependencies = [ [[package]] name = "apollo-router-scaffold" -version = "0.9.1" +version = "0.9.2" dependencies = [ "anyhow", - "cargo-scaffold 0.6.1", + "cargo-scaffold", "clap 3.1.9", "regex", "str_inflector", + "tempfile", "toml", ] @@ -794,32 +793,9 @@ dependencies = [ [[package]] name = "cargo-scaffold" -version = "0.6.1" -dependencies = [ - "anyhow", - "buildstructor 0.2.0", - "cargo", - "clap 2.34.0", - "console 0.12.0", - "dialoguer", - "git2", - "globset", - "handlebars", - "handlebars_misc_helpers", - "indicatif", - "md5", - "serde", - "shell-words", - "structopt", - "toml", - "walkdir", -] - -[[package]] -name = "cargo-scaffold" -version = "0.6.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbd3c374744f9566885fa4a842bf08c48064b990876b8bfff294df0c3890b3be" +checksum = "eb8e978a56f9ae1ff74f724ecab75d7b15d004a6e881f810d64a592d1563e941" dependencies = [ "anyhow", "buildstructor 0.2.0", diff --git a/apollo-router-scaffold/Cargo.toml b/apollo-router-scaffold/Cargo.toml index 34236c00c2..8a0ae92fb7 100644 --- a/apollo-router-scaffold/Cargo.toml +++ b/apollo-router-scaffold/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "apollo-router-scaffold" -version = "0.9.1" +version = "0.9.2" authors = ["Apollo Graph, Inc. "] edition = "2021" license = "Elastic-2.0" @@ -9,7 +9,9 @@ publish = false [dependencies] clap = { version = "3.1.8", features = ["derive"] } anyhow = "1.0.56" -cargo-scaffold = {path="../../cargo-scaffold"} +cargo-scaffold = "0.7.0" regex = "1" str_inflector = "0.12.0" toml = "0.5.8" +[dev-dependencies] +tempfile = "3.3.0" diff --git a/apollo-router-scaffold/src/lib.rs b/apollo-router-scaffold/src/lib.rs index f767fde9e3..4359fce096 100644 --- a/apollo-router-scaffold/src/lib.rs +++ b/apollo-router-scaffold/src/lib.rs @@ -20,3 +20,107 @@ impl RouterAction { } } } + +#[cfg(test)] +mod test { + use anyhow::Result; + use cargo_scaffold::{Opts, ScaffoldDescription}; + use inflector::Inflector; + use std::collections::BTreeMap; + use std::env; + use std::path::PathBuf; + use std::process::Command; + use tempfile::TempDir; + + #[test] + fn test_scaffold() -> Result<()> { + let temp_dir = tempfile::Builder::new() + .prefix("router_scaffold") + .tempdir()?; + let current_dir = env::current_dir()?; + // Scaffold the main project + let opts = Opts::builder() + .project_name("temp") + .target_dir(temp_dir.path()) + .template_path("templates/base") + .force(true) + .build(); + ScaffoldDescription::new(opts)?.scaffold_with_parameters(BTreeMap::from([( + "integration_test".to_string(), + toml::Value::String( + current_dir + .to_str() + .expect("current dir must be convertable to string") + .to_string(), + ), + )]))?; + test_build(&temp_dir)?; + + // Scaffold one of each type of plugin + scaffold_plugin(¤t_dir, &temp_dir, "basic")?; + scaffold_plugin(¤t_dir, &temp_dir, "auth")?; + scaffold_plugin(¤t_dir, &temp_dir, "tracing")?; + std::fs::write( + temp_dir.path().join("src/plugins/mod.rs"), + "mod auth;\nmod basic;\nmod tracing;\n", + )?; + test_build(&temp_dir)?; + + drop(temp_dir); + Ok(()) + } + + fn scaffold_plugin(current_dir: &PathBuf, dir: &TempDir, plugin_type: &str) -> Result<()> { + let opts = Opts::builder() + .project_name(plugin_type) + .target_dir(dir.path()) + .append(true) + .template_path("templates/plugin") + .build(); + ScaffoldDescription::new(opts)?.scaffold_with_parameters(BTreeMap::from([ + ( + format!("type_{}", plugin_type), + toml::Value::String(plugin_type.to_string()), + ), + ( + "snake_name".to_string(), + toml::Value::String(plugin_type.to_snake_case()), + ), + ( + "pascal_name".to_string(), + toml::Value::String(plugin_type.to_pascal_case()), + ), + ( + "project_name".to_string(), + toml::Value::String("acme".to_string()), + ), + ( + "integration_test".to_string(), + toml::Value::String( + current_dir + .to_str() + .expect("current dir must be convertable to string") + .to_string(), + ), + ), + ]))?; + Ok(()) + } + + fn test_build(dir: &TempDir) -> Result<()> { + let output = Command::new("cargo") + .args(["test"]) + .current_dir(dir) + .output()?; + if !output.status.success() { + eprintln!("failed to build scaffolded project"); + eprintln!("{}", String::from_utf8(output.stdout)?); + eprintln!("{}", String::from_utf8(output.stderr)?); + panic!( + "build failed with exit code {}", + output.status.code().unwrap_or_default() + ); + } + Ok(()) + } +} diff --git a/apollo-router-scaffold/src/plugin.rs b/apollo-router-scaffold/src/plugin.rs index f7f372ca26..213d47d172 100644 --- a/apollo-router-scaffold/src/plugin.rs +++ b/apollo-router-scaffold/src/plugin.rs @@ -3,20 +3,25 @@ use cargo_scaffold::ScaffoldDescription; use clap::Subcommand; use inflector::Inflector; use regex::Regex; +use std::fs; use std::path::{Path, PathBuf}; use toml::Value; #[derive(Subcommand, Debug)] pub enum PluginAction { - /// Add a plugin + /// Add a plugin. Create { - /// The name of the plugin you want to add + /// The name of the plugin you want to add. name: String, + + /// Optional override of the scaffold template path. + #[clap(long)] + template_override: Option, }, - /// Remove a plugin + /// Remove a plugin. Remove { - /// The name of the plugin you want to remove + /// The name of the plugin you want to remove. name: String, }, } @@ -24,23 +29,35 @@ pub enum PluginAction { impl PluginAction { pub fn execute(&self) -> Result<()> { match self { - PluginAction::Create { name } => create_plugin(name), + PluginAction::Create { + name, + template_override, + } => create_plugin(name, template_override), PluginAction::Remove { name } => remove_plugin(name), } } } -fn create_plugin(name: &str) -> Result<()> { +fn create_plugin(name: &str, template_path: &Option) -> Result<()> { let plugin_path = plugin_path(&name); if plugin_path.exists() { return Err(anyhow::anyhow!("plugin '{}' already exists", name)); } + let cargo_toml = fs::read_to_string("Cargo.toml")?.parse::()?; + let project_name = cargo_toml + .get("package") + .unwrap_or(&toml::Value::String("default".to_string())) + .get("name") + .map(|n| n.to_string().to_snake_case()) + .unwrap_or_else(|| "default".to_string()); + let opts = cargo_scaffold::Opts::builder() - .template_path(PathBuf::from( - "https://github.com/Cosmian/mpc_rust_template.git", - )) - .template_commit(format!("v{}", std::env!("CARGO_PKG_VERSION"))) + .template_path(template_path.as_ref().unwrap_or(&PathBuf::from( + "https://github.com/apollographql/router.git", + ))) + .git_ref(format!("v{}", std::env!("CARGO_PKG_VERSION"))) + .repository_template_path("apollo-router-scaffold/templates/plugin") .target_dir(".") .project_name(name) .append(true) @@ -55,6 +72,10 @@ fn create_plugin(name: &str) -> Result<()> { "snake_name".to_string(), Value::String(name.to_snake_case()), ); + params.insert( + "project_name".to_string(), + Value::String(project_name.to_snake_case()), + ); params.insert( format!( diff --git a/apollo-router-scaffold/templates/base/Cargo.toml b/apollo-router-scaffold/templates/base/Cargo.toml index 454967f3f0..03cb676f9c 100644 --- a/apollo-router-scaffold/templates/base/Cargo.toml +++ b/apollo-router-scaffold/templates/base/Cargo.toml @@ -12,8 +12,8 @@ members = [ [dependencies] anyhow = "1.0.55" {{#if integration_test}} -apollo-router = {path ="../apollo-router" } -apollo-router-core = { path="../apollo-router-core" } +apollo-router = {path ="{{integration_test}}/../apollo-router" } +apollo-router-core = { path="{{integration_test}}/../apollo-router-core" } {{else}} apollo-router = { git="https://github.com/apollographql/router.git", tag="0.9.2" } apollo-router-core = { git="https://github.com/apollographql/router.git", tag="0.9.2" } diff --git a/apollo-router-scaffold/templates/base/xtask/Cargo.toml b/apollo-router-scaffold/templates/base/xtask/Cargo.toml index 8295c88472..29469685fa 100644 --- a/apollo-router-scaffold/templates/base/xtask/Cargo.toml +++ b/apollo-router-scaffold/templates/base/xtask/Cargo.toml @@ -6,6 +6,11 @@ version = "0.1.0" [dependencies] # This dependency should stay in line with your router version + +{{#if integration_test}} +apollo-router-scaffold = {path ="{{integration_test}}/../apollo-router-scaffold" } +{{else}} apollo-router-scaffold = { git="https://github.com/apollographql/router.git", tag="0.9.2"} +{{/if}} anyhow = "1.0.56" clap = "3.1.8" diff --git a/apollo-router-scaffold/templates/plugin/.scaffold.toml b/apollo-router-scaffold/templates/plugin/.scaffold.toml index cff13c47fc..038d3c71c4 100644 --- a/apollo-router-scaffold/templates/plugin/.scaffold.toml +++ b/apollo-router-scaffold/templates/plugin/.scaffold.toml @@ -8,8 +8,12 @@ exclude = [ ] notes = """ -Creating {{name}} plugin. -Remember to add it to your router.yaml to use the plugin! +Created {{project_name}}.{{snake_name}} plugin. +To use the plugin add it to router.yaml: + +plugins: + {{project_name}}.{{snake_name}}: + # Plugin configuration """ [parameters] diff --git a/apollo-router-scaffold/templates/plugin/src/plugins/{{snake_name}}.rs b/apollo-router-scaffold/templates/plugin/src/plugins/{{snake_name}}.rs index ebae1039ab..25d922a4a1 100644 --- a/apollo-router-scaffold/templates/plugin/src/plugins/{{snake_name}}.rs +++ b/apollo-router-scaffold/templates/plugin/src/plugins/{{snake_name}}.rs @@ -155,7 +155,7 @@ impl Plugin for {{pascal_name}} { // This macro allows us to use it in our plugin registry! // register_plugin takes a group name, and a plugin name. -register_plugin!("example", "{{snake_name}}", {{pascal_name}}); +register_plugin!("{{project_name}}", "{{snake_name}}", {{pascal_name}}); #[cfg(test)] mod tests { @@ -169,7 +169,7 @@ mod tests { #[tokio::test] async fn plugin_registered() { apollo_router_core::plugins() - .get("example.{{snake_name}}") + .get("{{project_name}}.{{snake_name}}") .expect("Plugin not found") .create_instance(&serde_json::json!({"message" : "Starting my plugin"})) .await diff --git a/apollo-router/Cargo.toml b/apollo-router/Cargo.toml index f2be9ba24c..fe98bd5702 100644 --- a/apollo-router/Cargo.toml +++ b/apollo-router/Cargo.toml @@ -102,7 +102,6 @@ uname = "0.1.1" uname = "0.1.1" [dev-dependencies] -cargo-scaffold = "0.6.1" insta = "1.12.0" str_inflector = "0.12.0" jsonpath_lib = "0.3.0" @@ -117,7 +116,6 @@ test-log = { version = "0.2.10", default-features = false, features = [ "trace", ] } test-span = "0.6" -toml = "0.5.8" tower-test = "0.4.0" tracing-subscriber = { version = "0.3", default-features = false, features = [ "env-filter", diff --git a/apollo-router/tests/scaffold.rs b/apollo-router/tests/scaffold.rs deleted file mode 100644 index b40596d225..0000000000 --- a/apollo-router/tests/scaffold.rs +++ /dev/null @@ -1,86 +0,0 @@ -use cargo_scaffold::{Opts, ScaffoldDescription}; -use inflector::Inflector; -use std::collections::BTreeMap; -use std::fs; -use std::process::Command; -use tower::BoxError; - -mod common; - -struct CleanUp; -impl Drop for CleanUp { - fn drop(&mut self) { - let _ = fs::remove_dir_all("../temp"); - } -} -#[test] -fn test_scaffold() -> Result<(), BoxError> { - let cleanup = CleanUp {}; - - // Scaffold the main project - let opts = Opts::builder() - .project_name("temp") - .target_dir("../temp") - .template_path("../scaffold") - .force(true) - .build(); - ScaffoldDescription::new(opts)?.scaffold_with_parameters(BTreeMap::from([( - "integration_test".to_string(), - toml::Value::Boolean(true), - )]))?; - test_build()?; - - // Scaffold one of each type of plugin - scaffold_plugin("basic")?; - scaffold_plugin("auth")?; - scaffold_plugin("tracing")?; - std::fs::write( - "../temp/src/plugins/mod.rs", - "mod auth;\nmod basic;\nmod tracing;\n", - )?; - test_build()?; - - drop(cleanup); - Ok(()) -} - -fn scaffold_plugin(plugin_type: &str) -> Result<(), BoxError> { - let opts = Opts::builder() - .project_name(plugin_type) - .target_dir("../temp") - .append(true) - .template_path("../temp/scaffold/plugin") - .build(); - ScaffoldDescription::new(opts)?.scaffold_with_parameters(BTreeMap::from([ - ( - format!("type_{}", plugin_type), - toml::Value::String(plugin_type.to_string()), - ), - ( - "snake_name".to_string(), - toml::Value::String(plugin_type.to_snake_case()), - ), - ( - "pascal_name".to_string(), - toml::Value::String(plugin_type.to_pascal_case()), - ), - ]))?; - Ok(()) -} - -fn test_build() -> Result<(), BoxError> { - let output = Command::new("cargo") - .args(["test"]) - .current_dir("../temp") - .output()?; - if !output.status.success() { - eprintln!("failed to build scaffolded project"); - eprintln!("{}", String::from_utf8(output.stdout)?); - eprintln!("{}", String::from_utf8(output.stderr)?); - panic!( - "build failed with exit code {}", - output.status.code().unwrap_or_default() - ); - } - Ok(()) -}