From 7a214fb7bc18dd0cb1d88decfc44d666870fb0ca Mon Sep 17 00:00:00 2001 From: bryn Date: Tue, 17 May 2022 12:22:47 +0200 Subject: [PATCH] Introduce scaffolding for users trying to use the router as a library. Users can in a single command scaffold a project. Once they have a project they can scaffold three different types of plugin. --- .tool-versions | 2 +- Cargo.lock | 901 +++++++++++++++++- Cargo.toml | 1 + ...ugin__utils__test__test__test_harness.snap | 51 + apollo-router-scaffold/Cargo.toml | 17 + apollo-router-scaffold/src/lib.rs | 126 +++ apollo-router-scaffold/src/plugin.rs | 145 +++ .../templates/base/.cargo/config | 3 + .../templates/base/.gitignore | 2 + .../templates/base/.scaffold.toml | 27 + .../templates/base/.tool-versions | 3 + .../templates/base/Cargo.toml | 32 + .../templates/base/README.md | 83 ++ .../templates/base/router.yaml | 2 + .../templates/base/src/main.rs | 7 + .../templates/base/src/plugins/mod.rs | 1 + .../templates/base/xtask/Cargo.toml | 16 + .../templates/base/xtask/src/main.rs | 38 + .../templates/plugin/.scaffold.toml | 25 + .../templates/plugin/src/plugins/mod.rs | 0 .../plugin/src/plugins/{{snake_name}}.rs | 207 ++++ docs/source/config.json | 1 + .../source/customizations/native-tutorial.mdx | 115 +++ 23 files changed, 1781 insertions(+), 24 deletions(-) create mode 100644 apollo-router-core/src/plugin/utils/test/snapshots/apollo_router_core__plugin__utils__test__test__test_harness.snap create mode 100644 apollo-router-scaffold/Cargo.toml create mode 100644 apollo-router-scaffold/src/lib.rs create mode 100644 apollo-router-scaffold/src/plugin.rs create mode 100644 apollo-router-scaffold/templates/base/.cargo/config create mode 100644 apollo-router-scaffold/templates/base/.gitignore create mode 100644 apollo-router-scaffold/templates/base/.scaffold.toml create mode 100644 apollo-router-scaffold/templates/base/.tool-versions create mode 100644 apollo-router-scaffold/templates/base/Cargo.toml create mode 100644 apollo-router-scaffold/templates/base/README.md create mode 100644 apollo-router-scaffold/templates/base/router.yaml create mode 100644 apollo-router-scaffold/templates/base/src/main.rs create mode 100644 apollo-router-scaffold/templates/base/src/plugins/mod.rs create mode 100644 apollo-router-scaffold/templates/base/xtask/Cargo.toml create mode 100644 apollo-router-scaffold/templates/base/xtask/src/main.rs create mode 100644 apollo-router-scaffold/templates/plugin/.scaffold.toml create mode 100644 apollo-router-scaffold/templates/plugin/src/plugins/mod.rs create mode 100644 apollo-router-scaffold/templates/plugin/src/plugins/{{snake_name}}.rs create mode 100644 docs/source/customizations/native-tutorial.mdx diff --git a/.tool-versions b/.tool-versions index c92b876d7c..3a16caf6b8 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,3 +1,3 @@ -rust 1.60.0 +rust 1.61.0 nodejs 16.9.1 diff --git a/Cargo.lock b/Cargo.lock index a55089cdf3..e175583534 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,16 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "Inflector" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" +dependencies = [ + "lazy_static", + "regex", +] + [[package]] name = "add-timestamp-header" version = "0.1.0" @@ -107,7 +117,7 @@ dependencies = [ "atty", "axum", "backtrace", - "buildstructor", + "buildstructor 0.1.12", "bytes", "clap 3.1.9", "deadpool", @@ -199,13 +209,13 @@ dependencies = [ "async-trait", "atty", "axum", - "buildstructor", + "buildstructor 0.1.12", "bytes", "dashmap 5.2.0", "derivative", "displaydoc", "futures", - "hex", + "hex 0.4.3", "http", "http-body", "hyper", @@ -249,6 +259,19 @@ dependencies = [ "urlencoding", ] +[[package]] +name = "apollo-router-scaffold" +version = "0.9.2" +dependencies = [ + "anyhow", + "cargo-scaffold", + "clap 3.1.9", + "regex", + "str_inflector", + "tempfile", + "toml", +] + [[package]] name = "apollo-smith" version = "0.1.2" @@ -306,6 +329,12 @@ dependencies = [ "derive_arbitrary", ] +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + [[package]] name = "ascii" version = "0.9.3" @@ -400,6 +429,21 @@ dependencies = [ "syn", ] +[[package]] +name = "attohttpc" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8bda305457262b339322106c776e3fd21df860018e566eb6a5b1aa4b6ae02d" +dependencies = [ + "flate2", + "http", + "log", + "native-tls", + "openssl", + "url", + "wildmatch", +] + [[package]] name = "atty" version = "0.2.14" @@ -537,13 +581,34 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitmaps" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "031043d04099746d8db04daf1fa424b2bc8bd69d92b25962dcde24da39ab64a2" +dependencies = [ + "typenum", +] + +[[package]] +name = "block-buffer" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +dependencies = [ + "block-padding", + "byte-tools", + "byteorder", + "generic-array 0.12.4", +] + [[package]] name = "block-buffer" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" dependencies = [ - "generic-array", + "generic-array 0.14.5", ] [[package]] @@ -552,7 +617,16 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" dependencies = [ - "generic-array", + "generic-array 0.14.5", +] + +[[package]] +name = "block-padding" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" +dependencies = [ + "byte-tools", ] [[package]] @@ -582,12 +656,33 @@ dependencies = [ "try_match", ] +[[package]] +name = "buildstructor" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68af4c5e75a5ff25e046b770f10920925b4794f6ff7d166f4bf4cc3bad4cd728" +dependencies = [ + "lazy_static", + "proc-macro2", + "quote", + "str_inflector", + "syn", + "thiserror", + "try_match", +] + [[package]] name = "bumpalo" version = "3.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899" +[[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" + [[package]] name = "bytecount" version = "0.6.2" @@ -606,6 +701,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" +[[package]] +name = "bytesize" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c58ec36aac5066d5ca17df51b3e70279f5670a72102f5752cb7e7c856adfc70" + [[package]] name = "cache-padded" version = "1.2.0" @@ -621,6 +722,65 @@ dependencies = [ "serde", ] +[[package]] +name = "cargo" +version = "0.61.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f76f22dfcbc8e5aaa4e150373354723efe22b6b2280805f1fb6b1363005e7bab" +dependencies = [ + "anyhow", + "atty", + "bytesize", + "cargo-platform", + "cargo-util", + "clap 3.1.9", + "crates-io", + "crossbeam-utils 0.8.8", + "curl", + "curl-sys", + "env_logger", + "filetime", + "flate2", + "fwdansi", + "git2", + "git2-curl", + "glob", + "hex 0.4.3", + "home", + "humantime", + "ignore", + "im-rc", + "itertools", + "jobserver", + "lazy_static", + "lazycell", + "libc", + "libgit2-sys", + "log", + "memchr", + "num_cpus", + "opener", + "os_info", + "percent-encoding", + "rustc-workspace-hack", + "rustfix", + "semver", + "serde", + "serde_ignored", + "serde_json", + "shell-escape", + "strip-ansi-escapes", + "tar", + "tempfile", + "termcolor", + "toml_edit", + "unicode-width", + "unicode-xid", + "url", + "walkdir", + "winapi 0.3.9", +] + [[package]] name = "cargo-platform" version = "0.1.2" @@ -630,6 +790,53 @@ dependencies = [ "serde", ] +[[package]] +name = "cargo-scaffold" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb8e978a56f9ae1ff74f724ecab75d7b15d004a6e881f810d64a592d1563e941" +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-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a51c783163bdf4549820b80968d386c94ed45ed23819c93f59cca7ebd97fe0eb" +dependencies = [ + "anyhow", + "core-foundation", + "crypto-hash", + "filetime", + "hex 0.4.3", + "jobserver", + "libc", + "log", + "miow 0.3.7", + "same-file", + "shell-escape", + "tempfile", + "walkdir", + "winapi 0.3.9", +] + [[package]] name = "cargo_metadata" version = "0.14.2" @@ -657,6 +864,9 @@ name = "cc" version = "1.0.73" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" +dependencies = [ + "jobserver", +] [[package]] name = "cfg-if" @@ -676,9 +886,13 @@ version = "2.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" dependencies = [ + "ansi_term", + "atty", "bitflags", + "strsim 0.8.0", "textwrap 0.11.0", "unicode-width", + "vec_map", ] [[package]] @@ -687,11 +901,14 @@ version = "3.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6aad2534fad53df1cc12519c5cda696dd3e20e6118a027e24054aea14a0bdcbe" dependencies = [ + "atty", "bitflags", "clap_derive", "clap_lex", "indexmap", "lazy_static", + "strsim 0.10.0", + "termcolor", "textwrap 0.15.0", ] @@ -742,6 +959,34 @@ dependencies = [ "unreachable", ] +[[package]] +name = "combine" +version = "4.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a604e93b79d1808327a6fca85a6f2d69de66461e7620f5a4cbf5fb4d1d7c948" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "commoncrypto" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d056a8586ba25a1e4d61cb090900e495952c7886786fc55f909ab2f819b69007" +dependencies = [ + "commoncrypto-sys", +] + +[[package]] +name = "commoncrypto-sys" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fed34f46747aa73dfaa578069fd8279d2818ade2b55f38f22a9401c7f4083e2" +dependencies = [ + "libc", +] + [[package]] name = "compose-your-graphql-router" version = "0.1.0" @@ -764,6 +1009,40 @@ dependencies = [ "cache-padded", ] +[[package]] +name = "console" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c0994e656bba7b922d8dd1245db90672ffb701e684e45be58f20719d69abc5a" +dependencies = [ + "encode_unicode", + "lazy_static", + "libc", + "regex", + "terminal_size", + "termios", + "unicode-width", + "winapi 0.3.9", + "winapi-util", +] + +[[package]] +name = "console" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b1aacfaffdbff75be81c15a399b4bedf78aaefe840e8af1d299ac2ade885d2" +dependencies = [ + "encode_unicode", + "lazy_static", + "libc", + "regex", + "terminal_size", + "termios", + "unicode-width", + "winapi 0.3.9", + "winapi-util", +] + [[package]] name = "console" version = "0.15.0" @@ -773,7 +1052,9 @@ dependencies = [ "encode_unicode", "libc", "once_cell", + "regex", "terminal_size", + "unicode-width", "winapi 0.3.9", ] @@ -846,6 +1127,20 @@ dependencies = [ "libc", ] +[[package]] +name = "crates-io" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4a87459133b2e708195eaab34be55039bc30e0d120658bd40794bb00b6328d" +dependencies = [ + "anyhow", + "curl", + "percent-encoding", + "serde", + "serde_json", + "url", +] + [[package]] name = "crc32fast" version = "1.3.2" @@ -970,7 +1265,7 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f83bd3bb4314701c568e340cd8cf78c975aa0ca79e03d3f6d1677d5b0c9c0c03" dependencies = [ - "generic-array", + "generic-array 0.14.5", "rand_core", "subtle", ] @@ -981,7 +1276,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03c6a1d5fa1de37e071642dfa44ec552ca5b299adb128fab16138e24b548fd21" dependencies = [ - "generic-array", + "generic-array 0.14.5", "rand_core", "subtle", "zeroize", @@ -993,17 +1288,29 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8" dependencies = [ - "generic-array", + "generic-array 0.14.5", "typenum", ] +[[package]] +name = "crypto-hash" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a77162240fd97248d19a564a565eb563a3f592b386e4136fb300909e67dddca" +dependencies = [ + "commoncrypto", + "hex 0.3.2", + "openssl", + "winapi 0.3.9", +] + [[package]] name = "crypto-mac" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" dependencies = [ - "generic-array", + "generic-array 0.14.5", "subtle", ] @@ -1035,6 +1342,37 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3b7eb4404b8195a9abb6356f4ac07d8ba267045c8d6d220ac4dc992e6cc75df" +[[package]] +name = "curl" +version = "0.4.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d855aeef205b43f65a5001e0997d81f8efca7badad4fad7d897aa7f0d0651f" +dependencies = [ + "curl-sys", + "libc", + "openssl-probe", + "openssl-sys", + "schannel", + "socket2", + "winapi 0.3.9", +] + +[[package]] +name = "curl-sys" +version = "0.4.55+curl-7.83.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23734ec77368ec583c2e61dd3f0b0e5c98b93abe6d2a004ca06b91dd7e3e2762" +dependencies = [ + "cc", + "libc", + "libnghttp2-sys", + "libz-sys", + "openssl-sys", + "pkg-config", + "vcpkg", + "winapi 0.3.9", +] + [[package]] name = "daggy" version = "0.8.0" @@ -1176,19 +1514,39 @@ dependencies = [ "syn", ] +[[package]] +name = "dialoguer" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4aa86af7b19b40ef9cbef761ed411a49f0afa06b7b6dcd3dfe2f96a3c546138" +dependencies = [ + "console 0.11.3", + "lazy_static", + "tempfile", +] + [[package]] name = "difflib" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +dependencies = [ + "generic-array 0.12.4", +] + [[package]] name = "digest" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" dependencies = [ - "generic-array", + "generic-array 0.14.5", ] [[package]] @@ -1288,7 +1646,7 @@ dependencies = [ "crypto-bigint 0.3.2", "der 0.5.1", "ff", - "generic-array", + "generic-array 0.14.5", "group", "pem-rfc7468 0.3.1", "rand_core", @@ -1312,6 +1670,15 @@ dependencies = [ "cfg-if 1.0.0", ] +[[package]] +name = "enquote" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06c36cb11dbde389f4096111698d8b567c0720e3452fd5ac3e6b4e47e1939932" +dependencies = [ + "thiserror", +] + [[package]] name = "env_logger" version = "0.9.0" @@ -1372,6 +1739,12 @@ dependencies = [ "synstructure", ] +[[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" + [[package]] name = "fancy-regex" version = "0.8.0" @@ -1428,6 +1801,7 @@ dependencies = [ "cfg-if 1.0.0", "crc32fast", "libc", + "libz-sys", "miniz_oxide", ] @@ -1661,6 +2035,25 @@ dependencies = [ "slab", ] +[[package]] +name = "fwdansi" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c1f5787fe85505d1f7777268db5103d80a7a374d2316a7ce262e57baf8f208" +dependencies = [ + "memchr", + "termcolor", +] + +[[package]] +name = "generic-array" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" +dependencies = [ + "typenum", +] + [[package]] name = "generic-array" version = "0.14.5" @@ -1688,12 +2081,52 @@ version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4" +[[package]] +name = "git2" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0155506aab710a86160ddb504a480d2964d7ab5b9e62419be69e0032bc5931c" +dependencies = [ + "bitflags", + "libc", + "libgit2-sys", + "log", + "openssl-probe", + "openssl-sys", + "url", +] + +[[package]] +name = "git2-curl" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ee51709364c341fbb6fe2a385a290fb9196753bdde2fc45447d27cd31b11b13" +dependencies = [ + "curl", + "git2", + "log", + "url", +] + [[package]] name = "glob" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" +[[package]] +name = "globset" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10463d9ff00a2a068db14231982f5132edebad0d7660cd956a1c30292dbcbfbd" +dependencies = [ + "aho-corasick", + "bstr", + "fnv", + "log", + "regex", +] + [[package]] name = "graphql-introspection-query" version = "0.2.0" @@ -1709,7 +2142,7 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5613c31f18676f164112732202124f373bb2103ff017b3b85ca954ea6a66ada" dependencies = [ - "combine", + "combine 3.8.1", "failure", ] @@ -1788,6 +2221,40 @@ version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" +[[package]] +name = "handlebars" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d113a9853e5accd30f43003560b5563ffbb007e3f325e8b103fa0d0029c6e6df" +dependencies = [ + "log", + "pest", + "pest_derive", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "handlebars_misc_helpers" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8988751386607e16876907136f6f005a2254f936f41dccf541ea3b5d3474e8ea" +dependencies = [ + "Inflector", + "attohttpc", + "enquote", + "handlebars", + "jsonnet-rs", + "lazy_static", + "log", + "serde", + "serde_json", + "serde_yaml", + "thiserror", + "toml", +] + [[package]] name = "hashbrown" version = "0.11.2" @@ -1824,7 +2291,7 @@ dependencies = [ "http", "httpdate", "mime", - "sha-1", + "sha-1 0.10.0", ] [[package]] @@ -1876,6 +2343,12 @@ dependencies = [ "libc", ] +[[package]] +name = "hex" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77" + [[package]] name = "hex" version = "0.4.3" @@ -1916,6 +2389,15 @@ dependencies = [ "digest 0.9.0", ] +[[package]] +name = "home" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2456aef2e6b6a9784192ae780c0f15bc57df0e918585282325e8c8ac27737654" +dependencies = [ + "winapi 0.3.9", +] + [[package]] name = "hotwatch" version = "0.4.6" @@ -2047,14 +2529,46 @@ dependencies = [ ] [[package]] -name = "idna" -version = "0.2.3" +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "ignore" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "713f1b139373f96a2e0ce3ac931cd01ee973c3c5dd7c40c0c2efe96ad2b6751d" +dependencies = [ + "crossbeam-utils 0.8.8", + "globset", + "lazy_static", + "log", + "memchr", + "regex", + "same-file", + "thread_local", + "walkdir", + "winapi-util", +] + +[[package]] +name = "im-rc" +version = "15.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +checksum = "af1955a75fa080c677d3972822ec4bad316169ab1cfc6c257a942c2265dbe5fe" dependencies = [ - "matches", - "unicode-bidi", - "unicode-normalization", + "bitmaps", + "rand_core", + "rand_xoshiro", + "sized-chunks", + "typenum", + "version_check", ] [[package]] @@ -2086,6 +2600,18 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "indicatif" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7baab56125e25686df467fe470785512329883aab42696d661247aca2a2896e4" +dependencies = [ + "console 0.15.0", + "lazy_static", + "number_prefix", + "regex", +] + [[package]] name = "inotify" version = "0.7.1" @@ -2112,7 +2638,7 @@ version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "689960f187c43c01650c805fb6bc6f55ab944499d86d4ffe9474ad78991d8e94" dependencies = [ - "console", + "console 0.15.0", "once_cell", "serde", "serde_json", @@ -2186,6 +2712,15 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" +[[package]] +name = "jobserver" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa" +dependencies = [ + "libc", +] + [[package]] name = "js-sys" version = "0.3.57" @@ -2195,6 +2730,26 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "jsonnet-rs" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b396bdfc3ae94965555c9ec3efa2214e5f3921545a943ef9fc9873ff5e4723" +dependencies = [ + "jsonnet-sys", + "libc", +] + +[[package]] +name = "jsonnet-sys" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02347cbdf34234f30f0d92f0f8f678a15883bd5e1e4b3def4ed4a14f691d9add" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "jsonpath_lib" version = "0.3.0" @@ -2241,7 +2796,7 @@ dependencies = [ "apollo-router", "apollo-router-core", "async-trait", - "hex", + "hex 0.4.3", "http", "jwt-simple", "schemars", @@ -2299,6 +2854,15 @@ dependencies = [ "winapi-build", ] +[[package]] +name = "kstring" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b310ccceade8121d7d77fee406160e457c2f4e7c7982d589da3499bc7ea4526" +dependencies = [ + "serde", +] + [[package]] name = "launchpad" version = "0.1.0" @@ -2349,12 +2913,62 @@ dependencies = [ "once_cell", ] +[[package]] +name = "libgit2-sys" +version = "0.13.4+1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0fa6563431ede25f5cc7f6d803c6afbc1c5d3ad3d4925d12c882bf2b526f5d1" +dependencies = [ + "cc", + "libc", + "libssh2-sys", + "libz-sys", + "openssl-sys", + "pkg-config", +] + [[package]] name = "libm" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33a33a362ce288760ec6a508b94caaec573ae7d3bbbd91b87aa0bad4456839db" +[[package]] +name = "libnghttp2-sys" +version = "0.1.7+1.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57ed28aba195b38d5ff02b9170cbff627e336a20925e43b4945390401c5dc93f" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "libssh2-sys" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b094a36eb4b8b8c8a7b4b8ae43b2944502be3e59cd87687595cf6b0a71b3f4ca" +dependencies = [ + "cc", + "libc", + "libz-sys", + "openssl-sys", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "libz-sys" +version = "1.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e7e15d7610cce1d9752e137625f14e61a28cd45929b6e12e47b50fe154ee2e" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "linked-hash-map" version = "0.5.4" @@ -2435,6 +3049,12 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" +[[package]] +name = "md5" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" + [[package]] name = "memchr" version = "2.4.1" @@ -2831,6 +3451,12 @@ dependencies = [ "libc", ] +[[package]] +name = "number_prefix" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17b02fc0ff9a9e4b35b3342880f48e896ebf69f2967921fe8646bf5b7125956a" + [[package]] name = "object" version = "0.28.3" @@ -2865,12 +3491,28 @@ dependencies = [ "tower", ] +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" + [[package]] name = "opaque-debug" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "opener" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea3ebcd72a54701f56345f16785a6d3ac2df7e986d273eb4395c0b01db17952" +dependencies = [ + "bstr", + "winapi 0.3.9", +] + [[package]] name = "openssl" version = "0.10.38" @@ -3047,6 +3689,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "os_info" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04304d855bb5385d4b595edf0147b8e281871766b75dd4c87b2bdf3c9e5c2d19" +dependencies = [ + "log", + "serde", + "winapi 0.3.9", +] + [[package]] name = "os_str_bytes" version = "6.0.0" @@ -3155,6 +3808,49 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +[[package]] +name = "pest" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" +dependencies = [ + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pest_meta" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d" +dependencies = [ + "maplit", + "pest", + "sha-1 0.8.2", +] + [[package]] name = "petgraph" version = "0.6.0" @@ -3522,6 +4218,15 @@ dependencies = [ "getrandom", ] +[[package]] +name = "rand_xoshiro" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" +dependencies = [ + "rand_core", +] + [[package]] name = "raw-cpuid" version = "10.3.0" @@ -3839,6 +4544,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustc-workspace-hack" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc71d2faa173b74b232dedc235e3ee1696581bb132fc116fa3626d6151a1a8fb" + [[package]] name = "rustc_version" version = "0.4.0" @@ -3848,6 +4559,18 @@ dependencies = [ "semver", ] +[[package]] +name = "rustfix" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0be05fc0675ef4f47119dc39cfc46636bb77d4fc4ef1bd851b9c3f7697f32a" +dependencies = [ + "anyhow", + "log", + "serde", + "serde_json", +] + [[package]] name = "rustls" version = "0.19.1" @@ -4001,7 +4724,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08da66b8b0965a5555b6bd6639e68ccba85e1e2506f5fbb089e93f8a04e1a2d1" dependencies = [ "der 0.5.1", - "generic-array", + "generic-array 0.14.5", "pkcs8 0.8.0", "subtle", "zeroize", @@ -4089,6 +4812,15 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_ignored" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1940036ca2411651a40012009d062087dfe62817b2191a03750fb569e11fa633" +dependencies = [ + "serde", +] + [[package]] name = "serde_json" version = "1.0.79" @@ -4163,6 +4895,18 @@ dependencies = [ "yaml-rust", ] +[[package]] +name = "sha-1" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" +dependencies = [ + "block-buffer 0.7.3", + "digest 0.8.1", + "fake-simd", + "opaque-debug 0.2.3", +] + [[package]] name = "sha-1" version = "0.10.0" @@ -4184,7 +4928,7 @@ dependencies = [ "cfg-if 1.0.0", "cpufeatures", "digest 0.9.0", - "opaque-debug", + "opaque-debug 0.3.0", ] [[package]] @@ -4207,6 +4951,18 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shell-escape" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45bb67a18fa91266cc7807181f62f9178a6873bfad7dc788c42e6430db40184f" + +[[package]] +name = "shell-words" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" + [[package]] name = "signal-hook-registry" version = "1.4.0" @@ -4232,6 +4988,16 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e24979f63a11545f5f2c60141afe249d4f19f84581ea2138065e400941d83d3" +[[package]] +name = "sized-chunks" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16d69225bde7a69b235da73377861095455d298f2b970996eec25ddbb42b3d1e" +dependencies = [ + "bitmaps", + "typenum", +] + [[package]] name = "skeptic" version = "0.13.7" @@ -4339,6 +5105,27 @@ dependencies = [ "regex", ] +[[package]] +name = "strip-ansi-escapes" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "011cbb39cf7c1f62871aea3cc46e5817b0937b49e9447370c93cacbe93a766d8" +dependencies = [ + "vte", +] + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "structopt" version = "0.3.26" @@ -4508,6 +5295,15 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "termios" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "411c5bf740737c7918b8b1fe232dca4dc9f8e754b8ad5e20966814001ed0ac6b" +dependencies = [ + "libc", +] + [[package]] name = "termtree" version = "0.2.4" @@ -4807,6 +5603,20 @@ version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" dependencies = [ + "indexmap", + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "744e9ed5b352340aa47ce033716991b5589e23781acb97cad37d4ea70560f55b" +dependencies = [ + "combine 4.6.4", + "indexmap", + "itertools", + "kstring", "serde", ] @@ -5109,6 +5919,12 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" +[[package]] +name = "ucd-trie" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" + [[package]] name = "uname" version = "0.1.1" @@ -5203,6 +6019,12 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68b90931029ab9b034b300b797048cf23723400aa757e8a2bfb9d748102f9821" +[[package]] +name = "utf8parse" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372" + [[package]] name = "uuid" version = "0.8.2" @@ -5248,6 +6070,12 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + [[package]] name = "version_check" version = "0.9.4" @@ -5260,6 +6088,27 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" +[[package]] +name = "vte" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cbce692ab4ca2f1f3047fcf732430249c0e971bfdd2b234cf2c47ad93af5983" +dependencies = [ + "arrayvec", + "utf8parse", + "vte_generate_state_changes", +] + +[[package]] +name = "vte_generate_state_changes" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d257817081c7dffcdbab24b9e62d2def62e2ff7d00b1c20062551e6cccc145ff" +dependencies = [ + "proc-macro2", + "quote", +] + [[package]] name = "waker-fn" version = "1.1.0" @@ -5424,6 +6273,12 @@ dependencies = [ "libc", ] +[[package]] +name = "wildmatch" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f44b95f62d34113cf558c93511ac93027e03e9c29a60dd0fd70e6e025c7270a" + [[package]] name = "winapi" version = "0.2.8" diff --git a/Cargo.toml b/Cargo.toml index 934f16563d..b77f58665a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ members = [ "apollo-router", "apollo-router-core", "apollo-router-benchmarks", + "apollo-router-scaffold", "examples/add-timestamp-header", "examples/async-auth", "examples/cookies-to-headers", diff --git a/apollo-router-core/src/plugin/utils/test/snapshots/apollo_router_core__plugin__utils__test__test__test_harness.snap b/apollo-router-core/src/plugin/utils/test/snapshots/apollo_router_core__plugin__utils__test__test__test_harness.snap new file mode 100644 index 0000000000..7343b371cf --- /dev/null +++ b/apollo-router-core/src/plugin/utils/test/snapshots/apollo_router_core__plugin__utils__test__test__test_harness.snap @@ -0,0 +1,51 @@ +--- +source: apollo-router-core/src/plugin/utils/test/mod.rs +assertion_line: 226 +expression: graphql.data +--- +{ + "topProducts": [ + { + "name": "Table", + "reviews": [ + { + "author": { + "id": "1", + "name": "Ada Lovelace" + }, + "id": "1", + "product": { + "name": "Table" + } + }, + { + "author": { + "id": "2", + "name": "Alan Turing" + }, + "id": "4", + "product": { + "name": "Table" + } + } + ], + "upc": "1" + }, + { + "name": "Couch", + "reviews": [ + { + "author": { + "id": "1", + "name": "Ada Lovelace" + }, + "id": "2", + "product": { + "name": "Couch" + } + } + ], + "upc": "2" + } + ] +} diff --git a/apollo-router-scaffold/Cargo.toml b/apollo-router-scaffold/Cargo.toml new file mode 100644 index 0000000000..66aade0b45 --- /dev/null +++ b/apollo-router-scaffold/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "apollo-router-scaffold" +version = "0.9.2" +authors = ["Apollo Graph, Inc. "] +edition = "2021" +license = "Elastic-2.0" +publish = false + +[dependencies] +anyhow = "1.0.56" +clap = { version = "3.1.8", features = ["derive"] } +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 new file mode 100644 index 0000000000..4359fce096 --- /dev/null +++ b/apollo-router-scaffold/src/lib.rs @@ -0,0 +1,126 @@ +mod plugin; + +use crate::plugin::PluginAction; +use anyhow::Result; +use clap::Subcommand; + +#[derive(Subcommand, Debug)] +pub enum RouterAction { + /// Manage plugins + Plugin { + #[clap(subcommand)] + action: PluginAction, + }, +} + +impl RouterAction { + pub fn execute(&self) -> Result<()> { + match self { + RouterAction::Plugin { action } => action.execute(), + } + } +} + +#[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 new file mode 100644 index 0000000000..213d47d172 --- /dev/null +++ b/apollo-router-scaffold/src/plugin.rs @@ -0,0 +1,145 @@ +use anyhow::Result; +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. + Create { + /// 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 { + /// The name of the plugin you want to remove. + name: String, + }, +} + +impl PluginAction { + pub fn execute(&self) -> Result<()> { + match self { + PluginAction::Create { + name, + template_override, + } => create_plugin(name, template_override), + PluginAction::Remove { name } => remove_plugin(name), + } + } +} + +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(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) + .build(); + let desc = ScaffoldDescription::new(opts)?; + let mut params = desc.fetch_parameters_value()?; + params.insert( + "pascal_name".to_string(), + Value::String(name.to_pascal_case()), + ); + params.insert( + "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!( + "type_{}", + params + .get("type") + .expect("type must have been set") + .as_str() + .expect("type must be a string") + ), + Value::Boolean(true), + ); + + desc.scaffold_with_parameters(params)?; + + let mod_path = mod_path(); + let mut mod_rs = if mod_path.exists() { + std::fs::read_to_string(mod_path)? + } else { + "".to_string() + }; + + let snake_name = name.to_snake_case(); + let re = Regex::new(&format!(r"(?m)^mod {};$", snake_name)).unwrap(); + if let None = re.find(&mod_rs) { + mod_rs = format!("mod {};\n{}", snake_name, mod_rs); + } + + std::fs::write(mod_path, mod_rs)?; + + println!( + "Plugin created at '{}'.\nRemember to add the plugin to your router.yaml to activate it.", + plugin_path.display() + ); + Ok(()) +} + +fn remove_plugin(name: &str) -> Result<()> { + let plugin_path = plugin_path(&name); + let snake_name = name.to_snake_case(); + + std::fs::remove_file(&plugin_path)?; + + // Remove the mod; + let mod_path = mod_path(); + if Path::new(mod_path).exists() { + let mut mod_rs = std::fs::read_to_string(mod_path)?; + let re = Regex::new(&format!(r"(?m)^mod {};$", snake_name)).unwrap(); + mod_rs = re.replace(&mod_rs, "").to_string(); + + std::fs::write(mod_path, mod_rs)?; + } + + println!( + "Plugin removed at '{}'. This is a best effort, and you may need to edit some files manually.", + plugin_path.display() + ); + Ok(()) +} + +fn mod_path() -> &'static Path { + Path::new("src/plugins/mod.rs") +} + +fn plugin_path(name: &str) -> PathBuf { + format!("src/plugins/{}.rs", name.to_snake_case()).into() +} diff --git a/apollo-router-scaffold/templates/base/.cargo/config b/apollo-router-scaffold/templates/base/.cargo/config new file mode 100644 index 0000000000..24a9882b48 --- /dev/null +++ b/apollo-router-scaffold/templates/base/.cargo/config @@ -0,0 +1,3 @@ +[alias] +xtask = "run --package xtask --" +router = "run --package xtask -- router" diff --git a/apollo-router-scaffold/templates/base/.gitignore b/apollo-router-scaffold/templates/base/.gitignore new file mode 100644 index 0000000000..bba7b53950 --- /dev/null +++ b/apollo-router-scaffold/templates/base/.gitignore @@ -0,0 +1,2 @@ +/target/ +/.idea/ diff --git a/apollo-router-scaffold/templates/base/.scaffold.toml b/apollo-router-scaffold/templates/base/.scaffold.toml new file mode 100644 index 0000000000..066ecf86c4 --- /dev/null +++ b/apollo-router-scaffold/templates/base/.scaffold.toml @@ -0,0 +1,27 @@ +[template] +name = "apollo-router" +author = "Apollo" +version = "0.1.0" + +exclude = [ + "./target" +] + +disable_templating = [ + "./scaffold/**/*" +] + +# Notes to display at the end of the generation +notes = """ +Created new Apollo Router project '{{name}}'. + +> Note: The Apollo Router is made available under the Elastic License v2.0 (ELv2). +> Read [our licensing page](https://www.apollographql.com/docs/resources/elastic-license-v2-faq/) for more details. +""" + +# Parameters are basically all the variables needed to generate your template using templating. +# It will be displayed as prompt to interact with user (thanks to the message subfield). +# All the parameters will be available in your templates as variables (example: `{{description}}`). +[parameters] +# [parameters.name] is already reserved + diff --git a/apollo-router-scaffold/templates/base/.tool-versions b/apollo-router-scaffold/templates/base/.tool-versions new file mode 100644 index 0000000000..3a16caf6b8 --- /dev/null +++ b/apollo-router-scaffold/templates/base/.tool-versions @@ -0,0 +1,3 @@ +rust 1.61.0 + +nodejs 16.9.1 diff --git a/apollo-router-scaffold/templates/base/Cargo.toml b/apollo-router-scaffold/templates/base/Cargo.toml new file mode 100644 index 0000000000..725d5acc53 --- /dev/null +++ b/apollo-router-scaffold/templates/base/Cargo.toml @@ -0,0 +1,32 @@ +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[package] +name = "{{name}}" +version = "0.1.0" +edition = "2021" + +[workspace] +members = [ + "xtask", +] + +[[bin]] +name = "router" +path = "src/main.rs" + +[dependencies] +anyhow = "1.0.55" +{{#if integration_test}} +apollo-router = {path ="{{integration_test}}/../apollo-router" } +apollo-router-core = { path="{{integration_test}}/../apollo-router-core" } +{{else}} +# Note if you update these dependencies then also update xtask/Cargo.toml +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" } +{{/if}} +async-trait = "0.1.52" +schemars = "0.8.8" +serde = "1.0.136" +serde_json = "1.0.79" +tokio = { version = "1.17.0", features = ["full"] } +tower = { version = "0.4.12", features = ["full"] } +tracing = "0.1.34" diff --git a/apollo-router-scaffold/templates/base/README.md b/apollo-router-scaffold/templates/base/README.md new file mode 100644 index 0000000000..5fa2c3326e --- /dev/null +++ b/apollo-router-scaffold/templates/base/README.md @@ -0,0 +1,83 @@ +# Apollo Router project + +This generated project is set up to create a custom Apollo Router binary that may include plugins that you have written. + +> Note: The Apollo Router is made available under the Elastic License v2.0 (ELv2). +> Read [our licensing page](https://www.apollographql.com/docs/resources/elastic-license-v2-faq/) for more details. + +# Compile the router + +To create a debug build use the following command. +```bash +cargo build +``` +Your debug binary is now located in `target/debug/router` + +For production, you will want to create a release build. +```bash +cargo build --release +``` +Your release binary is now located in `target/release/router` + +# Run the Apollo Router + +1. Download the example schema + + ```bash + curl -sSL https://supergraph.demo.starstuff.dev/ > supergraph-schema.graphql + ``` + +2. Run the Apollo Router + + During development it is convenient to use `cargo run` to run the Apollo Router as it will + ```bash + cargo run -- --hot-reload --config router.yaml --supergraph supergraph-schema.graphql + ``` + +> If you are using managed federation you can set APOLLO_KEY and APOLLO_GRAPH_REF environment variables instead of specifying the supergraph as a file. + +# Create a plugin + +1. From within your project directory scaffold a new plugin + ```bash + cargo router plugin create hello_world + ``` +2. Select the type of plugin you want to scaffold: + ```bash + Select a plugin template: + > "basic" + "auth" + "tracing" + ``` + + The different templates are: + * basic - a barebones plugin. + * auth - a basic authentication plugin that could make an external call. + * tracing - a plugin that adds a custom span and a log message. + + Choose `basic`. + +4. Add the plugin to the `router.yaml` + ```yaml + plugins: + starstuff.hello_world: + message: "Starting my plugin" + ``` + +5. Run the Apollo Router and see your plugin start up + ```bash + cargo run -- --hot-reload --config router.yaml --supergraph supergraph-schema.graphql + ``` + + In your output you should see something like: + ```bash + 2022-05-21T09:16:33.160288Z INFO router::plugins::hello_world: Starting my plugin + ``` + +# Remove a plugin + +1. From within your project run the following command. It makes a best effort to remove the plugin, but your mileage may vary. + ```bash + cargo router plugin remove hello_world + ``` + diff --git a/apollo-router-scaffold/templates/base/router.yaml b/apollo-router-scaffold/templates/base/router.yaml new file mode 100644 index 0000000000..a4c05293c8 --- /dev/null +++ b/apollo-router-scaffold/templates/base/router.yaml @@ -0,0 +1,2 @@ +plugins: + # Add plugin configuration here diff --git a/apollo-router-scaffold/templates/base/src/main.rs b/apollo-router-scaffold/templates/base/src/main.rs new file mode 100644 index 0000000000..ca6699afe9 --- /dev/null +++ b/apollo-router-scaffold/templates/base/src/main.rs @@ -0,0 +1,7 @@ +mod plugins; + +use anyhow::Result; + +fn main() -> Result<()> { + apollo_router::main() +} diff --git a/apollo-router-scaffold/templates/base/src/plugins/mod.rs b/apollo-router-scaffold/templates/base/src/plugins/mod.rs new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/apollo-router-scaffold/templates/base/src/plugins/mod.rs @@ -0,0 +1 @@ + diff --git a/apollo-router-scaffold/templates/base/xtask/Cargo.toml b/apollo-router-scaffold/templates/base/xtask/Cargo.toml new file mode 100644 index 0000000000..29469685fa --- /dev/null +++ b/apollo-router-scaffold/templates/base/xtask/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "xtask" +edition = "2021" +publish = false +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/base/xtask/src/main.rs b/apollo-router-scaffold/templates/base/xtask/src/main.rs new file mode 100644 index 0000000000..211ded5255 --- /dev/null +++ b/apollo-router-scaffold/templates/base/xtask/src/main.rs @@ -0,0 +1,38 @@ +use anyhow::Result; +use apollo_router_scaffold::RouterAction; +use clap::Parser; +use clap::Subcommand; + +#[derive(Parser, Debug)] +struct Args { + #[clap(subcommand)] + action: Action, +} + +impl Args { + fn execute(&self) -> Result<()> { + self.action.execute() + } +} + +#[derive(Subcommand, Debug)] +enum Action { + /// Forward to router action + Router { + #[clap(subcommand)] + action: RouterAction, + }, +} + +impl Action { + fn execute(&self) -> Result<()> { + match self { + Action::Router { action } => action.execute(), + } + } +} + +fn main() -> Result<()> { + let args = Args::parse(); + args.execute() +} diff --git a/apollo-router-scaffold/templates/plugin/.scaffold.toml b/apollo-router-scaffold/templates/plugin/.scaffold.toml new file mode 100644 index 0000000000..09a3e558d6 --- /dev/null +++ b/apollo-router-scaffold/templates/plugin/.scaffold.toml @@ -0,0 +1,25 @@ +[template] +name = "apollo-router-plugins" +author = "Apollo" +version = "0.1.0" + +exclude = [ + "./target" +] + +notes = """ +Created new plugin {{project_name}}.{{snake_name}} +Source: src/plugins/{{snake_name}}.rs. + +To use the plugin add it to router.yaml: + +plugins: + {{project_name}}.{{snake_name}}: + # Plugin configuration +""" + +[parameters] +[parameters.type] +type = "select" +message = "Select a plugin template" +values = ["basic", "auth", "tracing"] diff --git a/apollo-router-scaffold/templates/plugin/src/plugins/mod.rs b/apollo-router-scaffold/templates/plugin/src/plugins/mod.rs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/apollo-router-scaffold/templates/plugin/src/plugins/{{snake_name}}.rs b/apollo-router-scaffold/templates/plugin/src/plugins/{{snake_name}}.rs new file mode 100644 index 0000000000..25d922a4a1 --- /dev/null +++ b/apollo-router-scaffold/templates/plugin/src/plugins/{{snake_name}}.rs @@ -0,0 +1,207 @@ +use apollo_router_core::plugin::Plugin; +{{#if type_basic}} +use apollo_router_core::{ + register_plugin, ExecutionRequest, ExecutionResponse, QueryPlannerRequest, + QueryPlannerResponse, RouterRequest, RouterResponse, SubgraphRequest, SubgraphResponse, +}; +{{/if}} +{{#if type_auth}} +use apollo_router_core::{ + register_plugin, RouterRequest, RouterResponse, +}; +use std::ops::ControlFlow; +use apollo_router_core::ServiceBuilderExt; +use tower::ServiceExt; +use tower::ServiceBuilder; +{{/if}} +{{#if type_tracing}} +use apollo_router_core::{ + register_plugin, RouterRequest, RouterResponse, +}; +use apollo_router_core::ServiceBuilderExt; +use tower::ServiceExt; +use tower::ServiceBuilder; +{{/if}} +use schemars::JsonSchema; +use serde::Deserialize; +use tower::util::BoxService; +use tower::BoxError; + +#[derive(Debug)] +struct {{pascal_name}} { + #[allow(dead_code)] + configuration: Conf, +} + +#[derive(Debug, Default, Deserialize, JsonSchema)] +struct Conf { + // Put your plugin configuration here. It will automatically be deserialized from JSON. + // Always put some sort of config here, even if it is just a bool to say that the plugin is enabled, + // otherwise the yaml to enable the plugin will be confusing. + message: String, +} +{{#if type_basic}} +// This is a bare bones plugin that can be duplicated when creating your own. +#[async_trait::async_trait] +impl Plugin for {{pascal_name}} { + type Config = Conf; + + async fn new(configuration: Self::Config) -> Result { + tracing::info!("{}", configuration.message); + Ok({{pascal_name}} { configuration }) + } + + // Delete this function if you are not customizing it. + fn router_service( + &mut self, + service: BoxService, + ) -> BoxService { + // Always use service builder to compose your plugins. + // It provides off the shelf building blocks for your plugin. + // + // ServiceBuilder::new() + // .service(service) + // .boxed() + + // Returning the original service means that we didn't add any extra functionality for at this point in the lifecycle. + service + } + + // Delete this function if you are not customizing it. + fn query_planning_service( + &mut self, + service: BoxService, + ) -> BoxService { + service + } + + // Delete this function if you are not customizing it. + fn execution_service( + &mut self, + service: BoxService, + ) -> BoxService { + service + } + + // Delete this function if you are not customizing it. + fn subgraph_service( + &mut self, + _name: &str, + service: BoxService, + ) -> BoxService { + service + } +} +{{/if}} +{{#if type_auth}} +// This plugin is a skeleton for doing authentication that requires a remote call. +#[async_trait::async_trait] +impl Plugin for {{pascal_name}} { + type Config = Conf; + + async fn new(configuration: Self::Config) -> Result { + tracing::info!("{}", configuration.message); + Ok({{pascal_name}} { configuration }) + } + + fn router_service( + &mut self, + service: BoxService, + ) -> BoxService { + + ServiceBuilder::new() + .checkpoint_async(|request : RouterRequest| Box::pin(async { + // Do some async call here to auth, and decide if to continue or not. + Ok(ControlFlow::Continue(request)) + })) + .buffered() + .service(service) + .boxed() + } +} +{{/if}} +{{#if type_tracing}} +// This plugin adds a span and an error to the logs. +#[async_trait::async_trait] +impl Plugin for {{pascal_name}} { + type Config = Conf; + + async fn new(configuration: Self::Config) -> Result { + tracing::info!("{}", configuration.message); + Ok({{pascal_name}} { configuration }) + } + + fn router_service( + &mut self, + service: BoxService, + ) -> BoxService { + + ServiceBuilder::new() + .instrument(|_request| { + // Optionally take information from the request and insert it into the span as attributes + // See https://docs.rs/tracing/latest/tracing/ for more information + tracing::info_span!("my_custom_span") + }) + .map_request(|request| { + // Add a log message, this will appear within the context of the current span + tracing::error!("error detected"); + request + }) + .service(service) + .boxed() + } +} +{{/if}} + +// This macro allows us to use it in our plugin registry! +// register_plugin takes a group name, and a plugin name. +register_plugin!("{{project_name}}", "{{snake_name}}", {{pascal_name}}); + +#[cfg(test)] +mod tests { + use super::{Conf, {{pascal_name}}}; + + use apollo_router_core::utils::test::IntoSchema::Canned; + use apollo_router_core::utils::test::PluginTestHarness; + use apollo_router_core::{Plugin, ResponseBody}; + use tower::BoxError; + + #[tokio::test] + async fn plugin_registered() { + apollo_router_core::plugins() + .get("{{project_name}}.{{snake_name}}") + .expect("Plugin not found") + .create_instance(&serde_json::json!({"message" : "Starting my plugin"})) + .await + .unwrap(); + } + + #[tokio::test] + async fn basic_test() -> Result<(), BoxError> { + // Define a configuration to use with our plugin + let conf = Conf { + message: "Starting my plugin".to_string(), + }; + + // Build an instance of our plugin to use in the test harness + let plugin = {{pascal_name}}::new(conf).await.expect("created plugin"); + + // Create the test harness. You can add mocks for individual services, or use prebuilt canned services. + let mut test_harness = PluginTestHarness::builder() + .plugin(plugin) + .schema(Canned) + .build() + .await?; + + // Send a request + let result = test_harness.call_canned().await?; + if let ResponseBody::GraphQL(graphql) = result.response.body() { + assert!(graphql.data.is_some()); + } else { + panic!("expected graphql response") + } + + Ok(()) + } +} + diff --git a/docs/source/config.json b/docs/source/config.json index 8bf10d5537..f63f754f72 100644 --- a/docs/source/config.json +++ b/docs/source/config.json @@ -41,6 +41,7 @@ "Customizations": { "Overview": "/customizations/overview", "Native Rust plugins": "/customizations/native", + "Native Rust plugins - Tutorial": "/customizations/native-tutorial", "Rhai scripts (experimental)": "/customizations/rhai" }, "Third-Party Support": { diff --git a/docs/source/customizations/native-tutorial.mdx b/docs/source/customizations/native-tutorial.mdx new file mode 100644 index 0000000000..9996ad02a0 --- /dev/null +++ b/docs/source/customizations/native-tutorial.mdx @@ -0,0 +1,115 @@ +--- +title: Creating a custom router binary +--- + +To create your own router binary with custom plugins you will need to use the router as a library. +This page will walk you through creating your own Router binary from scratch and creating a plugin. + + +> Note: The Apollo Router is made available under the Elastic License v2.0 (ELv2). +> Read [our licensing page](https://www.apollographql.com/docs/resources/elastic-license-v2-faq/) for more details. + +# Prerequisites + +To compile the Apollo Router you will need to have the following installed. + +* [rust 1.61.0](https://www.rust-lang.org/tools/install) +* nodejs 16.9.1 + +Once you have the basics installed, install `cargo-xtask` and `cargo-scaffold` +```bash +cargo install cargo-xtask +cargo install cargo-scaffold +``` + +# Create new project + +1. Use cargo scaffold to create the project. It will ask some questions about project. + + ```bash + cargo-scaffold scaffold https://github.com/apollographql/router.git -r /scaffold + ``` + You will be asked some questions about your project. For the purposes of this tutorial use `starstuff`. + +2. After the project has been created change to the `starstuff` directory + ```bash + cd starstuff + ``` + +# Compile the router + +To create a debug build use the following command. +```bash +cargo build +``` +Your debug binary is now located in `target/debug/router` + + +For production, you will want to create a release build. +```bash +cargo build --release +``` +Your release binary is now located in `target/release/router` + +# Run the Apollo Router + +1. Download the example schema + + ```bash + curl -sSL https://supergraph.demo.starstuff.dev/ > supergraph-schema.graphql + ``` + +2. Run the Apollo Router + + During development it is convenient to use `cargo run` to run the Apollo Router as it will + ```bash + cargo run -- --hot-reload --config router.yaml --supergraph supergraph-schema.graphql + ``` + +> If you are using managed federation you can set APOLLO_KEY and APOLLO_GRAPH_REF environment variables instead of specifying the supergraph as a file. + +# Create a plugin + +1. From within your project directory scaffold a new plugin + ```bash + cargo router plugin create hello_world + ``` +2. Select the type of plugin you want to scaffold: + ```bash + Select a plugin template: + > "basic" + "auth" + "tracing" + ``` + + The different templates are: + * basic - a barebones plugin. + * auth - a basic authentication plugin that could make an external call. + * tracing - a plugin that adds a custom span and a log message. + + Choose `basic`. + +4. Add the plugin to the `router.yaml` + ```yaml + plugins: + starstuff.hello_world: + message: "Starting my plugin" + ``` + +5. Run the Apollo Router and see your plugin start up + ```bash + cargo run -- --hot-reload --config router.yaml --supergraph supergraph-schema.graphql + ``` + + In your output you should see something like: + ```bash + 2022-05-21T09:16:33.160288Z INFO router::plugins::hello_world: Starting my plugin + ``` + +# Remove a plugin + +1. From within your project run the following command. It makes a best effort to remove the plugin, but your mileage may vary. + ```bash + cargo router plugin remove hello_world + ``` +