From e52b1098af639696e85211c7270f3e6de37c9191 Mon Sep 17 00:00:00 2001 From: avery Date: Fri, 22 Jul 2022 10:03:37 -0500 Subject: [PATCH] feat: initial rover dev --- Cargo.lock | 320 ++++++++++++++---- Cargo.toml | 5 +- crates/rover-client/src/shared/graph_ref.rs | 11 + src/cli.rs | 14 +- src/command/config/whoami.rs | 2 +- src/command/dev.rs | 42 +++ src/command/graph/check.rs | 2 +- src/command/graph/delete.rs | 2 +- src/command/graph/fetch.rs | 2 +- src/command/graph/publish.rs | 2 +- src/command/mod.rs | 6 +- src/command/readme/fetch.rs | 2 +- src/command/readme/publish.rs | 2 +- src/command/subgraph/check.rs | 2 +- src/command/subgraph/delete.rs | 2 +- src/command/subgraph/dev.rs | 210 ++++++++++++ src/command/subgraph/fetch.rs | 2 +- src/command/subgraph/init.rs | 22 +- src/command/subgraph/list.rs | 2 +- src/command/subgraph/mod.rs | 10 +- src/command/subgraph/publish.rs | 10 +- src/command/supergraph/compose/do_compose.rs | 52 ++- src/command/supergraph/fetch.rs | 2 +- src/command/supergraph/resolve_config.rs | 9 +- src/dot_apollo/mod.rs | 13 +- .../project_types/multi_subgraph/multi.rs | 18 + .../project_types/multi_subgraph/subgraph.rs | 49 ++- src/options/compose.rs | 17 + src/options/graph.rs | 2 +- src/options/mod.rs | 2 + src/options/profile.rs | 9 +- src/options/schema.rs | 2 +- src/options/subgraph.rs | 4 +- src/utils/client.rs | 6 +- src/utils/parsers.rs | 2 +- 35 files changed, 710 insertions(+), 149 deletions(-) create mode 100644 src/command/dev.rs create mode 100644 src/command/subgraph/dev.rs create mode 100644 src/options/compose.rs diff --git a/Cargo.lock b/Cargo.lock index ec6eabdb1..df7f0097d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -47,7 +47,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" dependencies = [ - "winapi", + "winapi 0.3.9", ] [[package]] @@ -221,7 +221,7 @@ dependencies = [ "slab", "socket2", "waker-fn", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -250,13 +250,13 @@ checksum = "cf2c06e30a24e8c78a3987d07f0930edf76ef35e027e7bdb063fccafdad1f60c" dependencies = [ "async-io", "blocking", - "cfg-if", + "cfg-if 1.0.0", "event-listener", "futures-lite", "libc", "once_cell", "signal-hook", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -318,7 +318,7 @@ checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ "hermit-abi", "libc", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -346,7 +346,7 @@ checksum = "11a17d453482a265fd5f8479f2a3f405566e6ca627837aaddb85af8b1ab8ef61" dependencies = [ "addr2line", "cc", - "cfg-if", + "cfg-if 1.0.0", "libc", "miniz_oxide", "object", @@ -390,13 +390,13 @@ dependencies = [ "directories-next", "flate2", "reqwest", - "saucer", + "saucer 0.1.0 (git+https://github.com/EverlastingBugstopper/awc.git?branch=main)", "serial_test", "tar", "tempdir", "thiserror", "tracing", - "winapi", + "winapi 0.3.9", "winreg", ] @@ -592,6 +592,12 @@ dependencies = [ "jobserver", ] +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + [[package]] name = "cfg-if" version = "1.0.0" @@ -609,7 +615,7 @@ dependencies = [ "num-traits", "serde", "time", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -723,7 +729,7 @@ dependencies = [ "regex", "terminal_size", "unicode-width", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -738,7 +744,7 @@ dependencies = [ "regex", "terminal_size", "unicode-width", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -793,7 +799,7 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", ] [[package]] @@ -802,7 +808,7 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ae5588f6b3c3cb05239e90bd110f257254aecd01e4635400391aeae07497845" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "crossbeam-channel", "crossbeam-deque", "crossbeam-epoch", @@ -816,7 +822,7 @@ version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5aaa7bd5fb665c6864b5f963dd9097905c54125909c7aa94c9e18507cdbe6c53" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "crossbeam-utils", ] @@ -826,7 +832,7 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "crossbeam-epoch", "crossbeam-utils", ] @@ -838,7 +844,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1145cf131a2c6ba0615079ab6a638f7e1973ac9c2634fcbeaaad6114246efe8c" dependencies = [ "autocfg", - "cfg-if", + "cfg-if 1.0.0", "crossbeam-utils", "lazy_static", "memoffset", @@ -851,7 +857,7 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f25d8400f4a7a5778f0e4e52384a48cbd9b5c495d110786187fc750075277a2" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "crossbeam-utils", ] @@ -861,7 +867,7 @@ version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "lazy_static", ] @@ -874,11 +880,11 @@ dependencies = [ "bitflags", "crossterm_winapi", "libc", - "mio", + "mio 0.8.3", "parking_lot", "signal-hook", "signal-hook-mio", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -887,7 +893,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2ae1b35a484aa10e07fe0638d02301c5ad24de82d310ccbd2f3693da5f09bf1c" dependencies = [ - "winapi", + "winapi 0.3.9", ] [[package]] @@ -950,7 +956,7 @@ dependencies = [ "openssl-sys", "schannel", "socket2", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -966,7 +972,7 @@ dependencies = [ "openssl-sys", "pkg-config", "vcpkg", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -1043,7 +1049,7 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "339ee130d97a610ea5a5872d2bbb130fdf68884ff09d3028b81bec8a1ac23bbc" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "dirs-sys-next", ] @@ -1055,7 +1061,7 @@ checksum = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901" dependencies = [ "libc", "redox_users 0.3.5", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -1064,7 +1070,7 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "dirs-sys-next", ] @@ -1076,7 +1082,7 @@ checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" dependencies = [ "libc", "redox_users 0.4.3", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -1118,7 +1124,7 @@ version = "0.8.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", ] [[package]] @@ -1162,10 +1168,10 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0408e2626025178a6a7f7ffc05a25bc47103229f19c113755de7bf63816290c" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "libc", "redox_syscall 0.2.13", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -1224,6 +1230,25 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fsevent" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ab7d1bd1bd33cc98b0889831b72da23c0aa4df9cec7e0702f46ecea04b35db6" +dependencies = [ + "bitflags", + "fsevent-sys", +] + +[[package]] +name = "fsevent-sys" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f41b048a94555da0f42f1d632e2e19510084fb8e303b0daa2816e733fb3644a0" +dependencies = [ + "libc", +] + [[package]] name = "fsio" version = "0.3.1" @@ -1239,6 +1264,22 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" +[[package]] +name = "fuchsia-zircon" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" +dependencies = [ + "bitflags", + "fuchsia-zircon-sys", +] + +[[package]] +name = "fuchsia-zircon-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" + [[package]] name = "futures-channel" version = "0.3.21" @@ -1330,7 +1371,7 @@ version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "libc", "wasi 0.9.0+wasi-snapshot-preview1", ] @@ -1341,7 +1382,7 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "libc", "wasi 0.10.2+wasi-snapshot-preview1", ] @@ -1521,7 +1562,7 @@ version = "0.0.0" dependencies = [ "assert_fs", "directories-next", - "saucer", + "saucer 0.1.0 (git+https://github.com/EverlastingBugstopper/awc.git?branch=main)", "serde", "thiserror", "toml", @@ -1690,13 +1731,42 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05a0bd019339e5d968b37855180087b7b9d512c5046fbd244cf8c95687927d6e" +[[package]] +name = "inotify" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4816c66d2c8ae673df83366c18341538f234a26d65a9ecea5c348b453ac1d02f" +dependencies = [ + "bitflags", + "inotify-sys", + "libc", +] + +[[package]] +name = "inotify-sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" +dependencies = [ + "libc", +] + [[package]] name = "instant" version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", +] + +[[package]] +name = "iovec" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" +dependencies = [ + "libc", ] [[package]] @@ -1771,6 +1841,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] + [[package]] name = "kv-log-macro" version = "1.0.7" @@ -1928,7 +2008,7 @@ version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "value-bag", ] @@ -1977,6 +2057,25 @@ dependencies = [ "adler", ] +[[package]] +name = "mio" +version = "0.6.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4" +dependencies = [ + "cfg-if 0.1.10", + "fuchsia-zircon", + "fuchsia-zircon-sys", + "iovec", + "kernel32-sys", + "libc", + "log", + "miow", + "net2", + "slab", + "winapi 0.2.8", +] + [[package]] name = "mio" version = "0.8.3" @@ -1989,6 +2088,30 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "mio-extras" +version = "2.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52403fe290012ce777c4626790c8951324a2b9e3316b3143779c72b029742f19" +dependencies = [ + "lazycell", + "log", + "mio 0.6.23", + "slab", +] + +[[package]] +name = "miow" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d" +dependencies = [ + "kernel32-sys", + "net2", + "winapi 0.2.8", + "ws2_32-sys", +] + [[package]] name = "native-tls" version = "0.2.10" @@ -2007,6 +2130,17 @@ dependencies = [ "tempfile", ] +[[package]] +name = "net2" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "winapi 0.3.9", +] + [[package]] name = "new_debug_unreachable" version = "1.0.4" @@ -2019,6 +2153,24 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" +[[package]] +name = "notify" +version = "4.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae03c8c853dba7bfd23e571ff0cff7bc9dceb40a4cd684cd1681824183f45257" +dependencies = [ + "bitflags", + "filetime", + "fsevent", + "fsevent-sys", + "inotify", + "libc", + "mio 0.6.23", + "mio-extras", + "walkdir", + "winapi 0.3.9", +] + [[package]] name = "num-integer" version = "0.1.45" @@ -2076,7 +2228,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ea3ebcd72a54701f56345f16785a6d3ac2df7e986d273eb4395c0b01db17952" dependencies = [ "bstr", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -2086,7 +2238,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb81a6430ac911acb25fe5ac8f1d2af1b4ea8a4fdfda0f1ee4292af2e2d8eb0e" dependencies = [ "bitflags", - "cfg-if", + "cfg-if 1.0.0", "foreign-types", "libc", "once_cell", @@ -2142,7 +2294,7 @@ checksum = "0eca3ecae1481e12c3d9379ec541b238a16f0b75c9a409942daa8ec20dbfdb62" dependencies = [ "log", "serde", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -2166,7 +2318,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "628223faebab4e3e40667ee0b2336d34a5b960ff60ea743ddfdbcf7770bcfb66" dependencies = [ - "winapi", + "winapi 0.3.9", ] [[package]] @@ -2197,7 +2349,7 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "libc", "redox_syscall 0.2.13", "smallvec", @@ -2279,11 +2431,11 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "685404d509889fade3e86fe3a5803bca2ec09b0c0778d5ada6ec8bf7a8de5259" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "libc", "log", "wepoll-ffi", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -2406,7 +2558,7 @@ dependencies = [ "libc", "rand_core 0.3.1", "rdrand", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -2553,7 +2705,7 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" dependencies = [ - "winapi", + "winapi 0.3.9", ] [[package]] @@ -2601,7 +2753,7 @@ version = "1.0.3" dependencies = [ "backtrace", "os_type", - "saucer", + "saucer 0.1.0 (git+https://github.com/EverlastingBugstopper/awc.git?branch=main)", "serde", "termcolor", "toml", @@ -2632,6 +2784,7 @@ dependencies = [ "houston", "humantime", "lazycell", + "notify", "opener", "os_info", "predicates", @@ -2639,7 +2792,7 @@ dependencies = [ "reqwest", "robot-panic", "rover-client", - "saucer", + "saucer 0.1.0", "semver", "serde", "serde_json", @@ -2678,7 +2831,7 @@ dependencies = [ "prettytable-rs", "regex", "reqwest", - "saucer", + "saucer 0.1.0 (git+https://github.com/EverlastingBugstopper/awc.git?branch=main)", "semver", "serde", "serde_json", @@ -2745,6 +2898,17 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "saucer" +version = "0.1.0" +dependencies = [ + "anyhow", + "camino", + "clap", + "log", + "rayon", +] + [[package]] name = "saucer" version = "0.1.0" @@ -2929,7 +3093,7 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "cpufeatures", "digest", ] @@ -2960,7 +3124,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" dependencies = [ "libc", - "mio", + "mio 0.8.3", "signal-hook", ] @@ -3015,7 +3179,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" dependencies = [ "libc", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -3026,7 +3190,7 @@ dependencies = [ "ci_info", "git2", "reqwest", - "saucer", + "saucer 0.1.0 (git+https://github.com/EverlastingBugstopper/awc.git?branch=main)", "semver", "serde", "serde_json", @@ -3124,12 +3288,12 @@ version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "fastrand", "libc", "redox_syscall 0.2.13", "remove_dir_all", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -3140,7 +3304,7 @@ checksum = "edd106a334b7657c10b7c540a0106114feadeb4dc314513e97df481d5d966f42" dependencies = [ "byteorder", "dirs", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -3151,7 +3315,7 @@ checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" dependencies = [ "dirs-next", "rustversion", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -3161,7 +3325,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e4129646ca0ed8f45d09b929036bafad5377103edd06e50bf574b353d2b08d9" dependencies = [ "libc", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -3194,7 +3358,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df" dependencies = [ "libc", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -3259,7 +3423,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" dependencies = [ "libc", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -3295,14 +3459,14 @@ dependencies = [ "bytes", "libc", "memchr", - "mio", + "mio 0.8.3", "num_cpus", "once_cell", "pin-project-lite", "signal-hook-registry", "socket2", "tokio-macros", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -3387,7 +3551,7 @@ version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "log", "pin-project-lite", "tracing-attributes", @@ -3615,7 +3779,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" dependencies = [ "same-file", - "winapi", + "winapi 0.3.9", "winapi-util", ] @@ -3653,7 +3817,7 @@ version = "0.2.80" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27370197c907c55e3f1a9fbe26f44e937fe6451368324e009cba39e139dc08ad" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "wasm-bindgen-macro", ] @@ -3678,7 +3842,7 @@ version = "0.4.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f741de44b75e14c35df886aff5f1eb73aa114fa5d4d00dcd37b5e01259bf3b2" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "js-sys", "wasm-bindgen", "web-sys", @@ -3743,6 +3907,12 @@ dependencies = [ "libc", ] +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" + [[package]] name = "winapi" version = "0.3.9" @@ -3753,6 +3923,12 @@ dependencies = [ "winapi-x86_64-pc-windows-gnu", ] +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" + [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" @@ -3765,7 +3941,7 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" dependencies = [ - "winapi", + "winapi 0.3.9", ] [[package]] @@ -3823,7 +3999,17 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" dependencies = [ - "winapi", + "winapi 0.3.9", +] + +[[package]] +name = "ws2_32-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" +dependencies = [ + "winapi 0.2.8", + "winapi-build", ] [[package]] @@ -3853,7 +4039,7 @@ dependencies = [ "lazy_static", "regex", "reqwest", - "saucer", + "saucer 0.1.0 (git+https://github.com/EverlastingBugstopper/awc.git?branch=main)", "semver", "serde_json", "serde_json_traversal", diff --git a/Cargo.toml b/Cargo.toml index 6edbb107c..64a2fc47d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,8 +59,8 @@ timber = { path = "./crates/timber" } # git dependencies billboard = { git = "https://github.com/EverlastingBugstopper/billboard.git", branch = "main" } -saucer = { git = "https://github.com/EverlastingBugstopper/awc.git", branch = "main" } -# saucer = { path = "../awc/saucer" } +# saucer = { git = "https://github.com/EverlastingBugstopper/awc.git", branch = "main" } +saucer = { path = "../awc/saucer" } # crates.io dependencies ansi_term = "0.12" @@ -74,6 +74,7 @@ dialoguer = "0.10" heck = "0.4" humantime = "2.1.0" lazycell = "1" +notify = "4" opener = "0.5" os_info = "3.4" prettytable-rs = "0.8" diff --git a/crates/rover-client/src/shared/graph_ref.rs b/crates/rover-client/src/shared/graph_ref.rs index fb2d4f010..6089be503 100644 --- a/crates/rover-client/src/shared/graph_ref.rs +++ b/crates/rover-client/src/shared/graph_ref.rs @@ -12,6 +12,17 @@ pub struct GraphRef { pub variant: String, } +impl GraphRef { + pub fn new(name: String, variant: Option) -> Result { + let mut s = name; + if let Some(variant) = variant { + s.push('@'); + s.push_str(&variant); + }; + Ok(Self::from_str(&s)?) + } +} + impl fmt::Display for GraphRef { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}@{}", self.name, self.variant) diff --git a/src/cli.rs b/src/cli.rs index 7c7bc1099..092a90f23 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -181,6 +181,9 @@ impl Rover { match &self.command { Command::Config(command) => command.run(self.get_client_config()?), + Command::Dev(command) => { + command.run(self.get_install_override_path()?, self.get_client_config()?) + } Command::Fed2(command) => command.run(self.get_client_config()?), Command::Supergraph(command) => { command.run(self.get_install_override_path()?, self.get_client_config()?) @@ -190,9 +193,11 @@ impl Rover { command.run(self.get_client_config()?, self.get_git_context()?) } Command::Readme(command) => command.run(self.get_client_config()?), - Command::Subgraph(command) => { - command.run(self.get_client_config()?, self.get_git_context()?) - } + Command::Subgraph(command) => command.run( + self.get_install_override_path().ok().flatten(), + self.get_client_config()?, + self.get_git_context()?, + ), Command::Update(command) => { command.run(self.get_rover_config()?, self.get_reqwest_client()) } @@ -303,6 +308,9 @@ pub enum Command { /// Configuration profile commands Config(command::Config), + /// Develop a GraphQL API + Dev(command::Dev), + /// (deprecated) Federation 2 Alpha commands #[clap(setting(AppSettings::Hidden))] Fed2(command::Fed2), diff --git a/src/command/config/whoami.rs b/src/command/config/whoami.rs index b3f32716a..a2501f060 100644 --- a/src/command/config/whoami.rs +++ b/src/command/config/whoami.rs @@ -30,7 +30,7 @@ pub struct WhoAmI { impl WhoAmI { pub fn run(&self, client_config: StudioClientConfig) -> Result { - let client = client_config.get_authenticated_client(&self.profile.profile_name)?; + let client = client_config.get_authenticated_client(&self.profile)?; eprintln!("Checking identity of your API key against the registry."); let identity = who_am_i::run(ConfigWhoAmIInput {}, &client)?; diff --git a/src/command/dev.rs b/src/command/dev.rs new file mode 100644 index 000000000..57b04af58 --- /dev/null +++ b/src/command/dev.rs @@ -0,0 +1,42 @@ +use crate::command::subgraph::{Dev as SubgraphDev, SubgraphDevOpts}; +use crate::command::{subgraph, RoverOutput}; +use crate::utils::client::StudioClientConfig; +use crate::{Result, PKG_VERSION}; +use saucer::{clap, ArgEnum, Parser, Utf8PathBuf}; + +use calm_io::stderrln; +use serde::Serialize; +use std::env; + +#[derive(Debug, Serialize, Clone, Parser)] +pub struct Dev { + #[clap(flatten)] + opts: SubgraphDevOpts, +} + +impl Dev { + #[cfg(feature = "composition-js")] + pub fn run( + &self, + override_install_path: Option, + client_config: StudioClientConfig, + ) -> Result { + // TODO: ensure project is subgraph type before running + SubgraphDev { + opts: self.opts.clone(), + } + .run(override_install_path, client_config) + } + + #[cfg(not(feature = "composition-js"))] + pub fn run( + &self, + override_install_path: Option, + client_config: StudioClientConfig, + ) -> Result { + SubgraphDev { + opts: self.opts.clone(), + } + .run(override_install_path, client_config) + } +} diff --git a/src/command/graph/check.rs b/src/command/graph/check.rs index e5d9ca592..bb90d9656 100644 --- a/src/command/graph/check.rs +++ b/src/command/graph/check.rs @@ -31,7 +31,7 @@ impl Check { client_config: StudioClientConfig, git_context: GitContext, ) -> Result { - let client = client_config.get_authenticated_client(&self.profile.profile_name)?; + let client = client_config.get_authenticated_client(&self.profile)?; let proposed_schema = self .schema .read_file_descriptor("SDL", &mut std::io::stdin())?; diff --git a/src/command/graph/delete.rs b/src/command/graph/delete.rs index 08f14fdb4..23acbaba0 100644 --- a/src/command/graph/delete.rs +++ b/src/command/graph/delete.rs @@ -25,7 +25,7 @@ pub struct Delete { impl Delete { pub fn run(&self, client_config: StudioClientConfig) -> Result { - let client = client_config.get_authenticated_client(&self.profile.profile_name)?; + let client = client_config.get_authenticated_client(&self.profile)?; let graph_ref = self.graph.graph_ref.to_string(); eprintln!( diff --git a/src/command/graph/fetch.rs b/src/command/graph/fetch.rs index 91cec1e32..b34eae3af 100644 --- a/src/command/graph/fetch.rs +++ b/src/command/graph/fetch.rs @@ -20,7 +20,7 @@ pub struct Fetch { impl Fetch { pub fn run(&self, client_config: StudioClientConfig) -> Result { - let client = client_config.get_authenticated_client(&self.profile.profile_name)?; + let client = client_config.get_authenticated_client(&self.profile)?; let graph_ref = self.graph.graph_ref.to_string(); eprintln!( "Fetching SDL from {} using credentials from the {} profile.", diff --git a/src/command/graph/publish.rs b/src/command/graph/publish.rs index 541bf2edf..42737b1a6 100644 --- a/src/command/graph/publish.rs +++ b/src/command/graph/publish.rs @@ -29,7 +29,7 @@ impl Publish { client_config: StudioClientConfig, git_context: GitContext, ) -> Result { - let client = client_config.get_authenticated_client(&self.profile.profile_name)?; + let client = client_config.get_authenticated_client(&self.profile)?; let graph_ref = self.graph.graph_ref.to_string(); eprintln!( "Publishing SDL to {} using credentials from the {} profile.", diff --git a/src/command/mod.rs b/src/command/mod.rs index 18fbc1f66..2025d3fca 100644 --- a/src/command/mod.rs +++ b/src/command/mod.rs @@ -1,18 +1,20 @@ mod config; +mod dev; mod docs; mod explain; mod fed2; mod graph; mod info; -mod install; +pub(crate) mod install; mod readme; -mod subgraph; +pub(crate) mod subgraph; mod supergraph; mod update; pub(crate) mod output; pub use config::Config; +pub use dev::Dev; pub use docs::Docs; pub use explain::Explain; pub use fed2::Fed2; diff --git a/src/command/readme/fetch.rs b/src/command/readme/fetch.rs index a7e18aea8..d15bbcfd2 100644 --- a/src/command/readme/fetch.rs +++ b/src/command/readme/fetch.rs @@ -21,7 +21,7 @@ pub struct Fetch { impl Fetch { pub fn run(&self, client_config: StudioClientConfig) -> Result { - let client = client_config.get_authenticated_client(&self.profile.profile_name)?; + let client = client_config.get_authenticated_client(&self.profile)?; let graph_ref = self.graph.graph_ref.to_string(); eprintln!( diff --git a/src/command/readme/publish.rs b/src/command/readme/publish.rs index 5f3b37074..d20ac2f16 100644 --- a/src/command/readme/publish.rs +++ b/src/command/readme/publish.rs @@ -27,7 +27,7 @@ pub struct Publish { impl Publish { pub fn run(&self, client_config: StudioClientConfig) -> Result { - let client = client_config.get_authenticated_client(&self.profile.profile_name)?; + let client = client_config.get_authenticated_client(&self.profile)?; let graph_ref = self.graph.graph_ref.to_string(); eprintln!( "Publishing README for {} using credentials from the {} profile.", diff --git a/src/command/subgraph/check.rs b/src/command/subgraph/check.rs index bef4e9e55..405921bbc 100644 --- a/src/command/subgraph/check.rs +++ b/src/command/subgraph/check.rs @@ -34,7 +34,7 @@ impl Check { client_config: StudioClientConfig, git_context: GitContext, ) -> Result { - let client = client_config.get_authenticated_client(&self.profile.profile_name)?; + let client = client_config.get_authenticated_client(&self.profile)?; let proposed_schema = self .schema diff --git a/src/command/subgraph/delete.rs b/src/command/subgraph/delete.rs index fcbf6339b..a6408b8c6 100644 --- a/src/command/subgraph/delete.rs +++ b/src/command/subgraph/delete.rs @@ -29,7 +29,7 @@ pub struct Delete { impl Delete { pub fn run(&self, client_config: StudioClientConfig) -> Result { - let client = client_config.get_authenticated_client(&self.profile.profile_name)?; + let client = client_config.get_authenticated_client(&self.profile)?; eprintln!( "Checking for build errors resulting from deleting subgraph {} from {} using credentials from the {} profile.", Cyan.normal().paint(&self.subgraph.subgraph_name), diff --git a/src/command/subgraph/dev.rs b/src/command/subgraph/dev.rs new file mode 100644 index 000000000..d96790388 --- /dev/null +++ b/src/command/subgraph/dev.rs @@ -0,0 +1,210 @@ +use std::sync::mpsc::{Sender, SyncSender}; + +use ansi_term::Colour::{Cyan, Yellow}; +use apollo_federation_types::build::SubgraphDefinition; +use apollo_federation_types::config::{FederationVersion, SupergraphConfig}; +use dialoguer::Input; +use notify::{watcher, DebouncedEvent, RecursiveMode, Watcher}; +use saucer::{anyhow, clap, Parser, Process, Utf8PathBuf}; +use saucer::{Fs, Saucer}; +use serde::Serialize; +use tempdir::TempDir; + +use crate::command::supergraph::compose::Compose; +use crate::command::RoverOutput; +use crate::dot_apollo::{DotApollo, ProjectType}; +use crate::options::{ + ComposeOpts, GraphRefOpt, OptionalGraphRefOpt, OptionalSchemaOpt, OptionalSubgraphOpt, + ProfileOpt, SchemaOpt, SubgraphOpt, +}; +use crate::utils::client::StudioClientConfig; +use crate::{error::RoverError, Result}; + +use rover_client::shared::GraphRef; + +#[derive(Debug, Serialize, Parser)] +pub struct Dev { + #[clap(flatten)] + pub(crate) opts: SubgraphDevOpts, +} + +#[derive(Debug, Clone, Serialize, Parser)] +pub struct SubgraphDevOpts { + #[clap(flatten)] + graph: OptionalGraphRefOpt, + + #[clap(flatten)] + subgraph: OptionalSubgraphOpt, + + #[clap(flatten)] + compose_opts: ComposeOpts, + + #[clap(flatten)] + #[serde(skip_serializing)] + schema: OptionalSchemaOpt, + + /// Url of a running subgraph that a graph router can send operations to + /// (often a localhost endpoint). + #[clap(long)] + #[serde(skip_serializing)] + local_url: Option, +} + +impl Dev { + #[cfg(feature = "composition-js")] + pub fn run( + &self, + override_install_path: Option, + client_config: StudioClientConfig, + ) -> Result { + use apollo_federation_types::config::{FederationVersion, SupergraphConfig}; + use std::sync::mpsc::{channel, sync_channel}; + + let temp_dir = TempDir::new("subgraph")?; + let temp_path = Utf8PathBuf::try_from(temp_dir.into_path())?.join("supergraph.yaml"); + let compose_saucer = SaucerFactory::get_compose_saucer( + self.opts.compose_opts.clone(), + override_install_path, + client_config, + temp_path.clone(), + )?; + + compose_saucer.beam()?; + let (router_sender, router_receiver) = sync_channel(1); + let router_saucer = RouterSaucer::new(temp_path, router_sender); + router_saucer.beam()?; + let router_handle = match router_receiver.recv() { + Ok(s) => Ok(s), + Err(e) => Err(anyhow!("Could not start router {}", e)), + }; + eprintln!("router is running..."); + + Ok(RoverOutput::EmptySuccess) + } + + #[cfg(not(feature = "composition-js"))] + pub fn run(&self, client_config: StudioClientConfig) -> Result { + Err(RoverError::new(anyhow!( + "rover dev is not supported on this platform" + ))) + } +} + +#[cfg(feature = "composition-js")] +#[derive(Debug, Clone)] +pub struct RouterSaucer { + read_path: Utf8PathBuf, + router_handle: SyncSender, +} + +impl RouterSaucer { + fn new(read_path: Utf8PathBuf, sender: SyncSender) -> Self { + Self { + read_path, + router_handle: sender, + } + } +} + +impl Saucer for RouterSaucer { + fn description(&self) -> String { + "router".to_string() + } + + fn beam(&self) -> saucer::Result<()> { + eprintln!("starting router"); + let router_handle = std::process::Command::new("./.apollo/router") + .args(&["--supergraph", self.read_path.as_str(), "--hot-reload"]) + .spawn()?; + // Process::new( + // "./.apollo/router", + // &["--supergraph", self.read_path.as_str(), "--hot-reload"], + // ) + // .run("") + Ok(()) + } +} + +#[cfg(feature = "composition-js")] +#[derive(Debug, Clone)] +pub struct ComposeSaucer { + compose: Compose, + override_install_path: Option, + client_config: StudioClientConfig, + supergraph_config: SupergraphConfig, + write_path: Utf8PathBuf, +} + +impl ComposeSaucer { + fn new( + compose_opts: ComposeOpts, + override_install_path: Option, + client_config: StudioClientConfig, + subgraph_definitions: Vec, + write_path: Utf8PathBuf, + ) -> Self { + let mut supergraph_config = SupergraphConfig::from(subgraph_definitions); + supergraph_config.set_federation_version(FederationVersion::LatestFedTwo); + Self { + compose: Compose::new(compose_opts), + override_install_path, + client_config, + supergraph_config, + write_path, + } + } +} + +impl Saucer for ComposeSaucer { + fn description(&self) -> String { + "composition".to_string() + } + + fn beam(&self) -> saucer::Result<()> { + match self.compose.compose( + self.override_install_path.clone(), + self.client_config.clone(), + &mut self.supergraph_config.clone(), + ) { + Ok(build_result) => match &build_result { + RoverOutput::CompositionResult { + supergraph_sdl, + hints, + federation_version, + } => { + // let _ = build_result.print(); + Fs::write_file(&self.write_path, supergraph_sdl, "")?; + Ok(()) + } + _ => unreachable!(), + }, + Err(e) => Err(anyhow!("{}", e)), + } + } +} + +pub struct SaucerFactory {} + +impl SaucerFactory { + fn get_compose_saucer( + compose_opts: ComposeOpts, + override_install_path: Option, + client_config: StudioClientConfig, + write_path: Utf8PathBuf, + ) -> Result { + let maybe_multi_config = DotApollo::subgraph_from_yaml()?; + if let Some(multi_config) = maybe_multi_config { + let subgraphs = + multi_config.get_all_subgraphs(true, &client_config, &compose_opts.profile)?; + Ok(ComposeSaucer::new( + compose_opts.clone(), + override_install_path, + client_config, + subgraphs, + write_path, + )) + } else { + Err(RoverError::new(anyhow!("found no valid subgraph definitions in .apollo/config.yaml. please run `rover subgraph init` or run this command in a project with a .apollo directory"))) + } + } +} diff --git a/src/command/subgraph/fetch.rs b/src/command/subgraph/fetch.rs index cd4e7a93e..f2307ad40 100644 --- a/src/command/subgraph/fetch.rs +++ b/src/command/subgraph/fetch.rs @@ -23,7 +23,7 @@ pub struct Fetch { impl Fetch { pub fn run(&self, client_config: StudioClientConfig) -> Result { - let client = client_config.get_authenticated_client(&self.profile.profile_name)?; + let client = client_config.get_authenticated_client(&self.profile)?; let graph_ref = self.graph.graph_ref.to_string(); eprintln!( "Fetching SDL from {} (subgraph: {}) using credentials from the {} profile.", diff --git a/src/command/subgraph/init.rs b/src/command/subgraph/init.rs index 8ababd905..e31d71635 100644 --- a/src/command/subgraph/init.rs +++ b/src/command/subgraph/init.rs @@ -47,7 +47,7 @@ pub struct Init { #[clap(long, conflicts_with_all(&["schema-file", "schema-ref", "schema-ref-subgraph-name"]))] #[serde(skip_serializing)] - schema_url: Option, + schema_url: Option, #[clap(long, requires("schema-ref-subgraph-name"), conflicts_with_all(&["schema-file", "schema-url"]))] schema_ref: Option, @@ -105,14 +105,14 @@ impl Init { let selected = source_opts[i]; eprintln!("✅ selected {}...", selected); match selected { - local_file => { + "local file" => { let file: Utf8PathBuf = Input::new() .with_prompt("What is the path to your schema?") .interact_text()?; SubgraphConfig::from_file(file, local_endpoint, remote_endpoint) } - introspect => { - let subgraph_url: Url = Input::new() + "introspection url" => { + let subgraph_url: String = Input::new() .with_prompt("What is the endpoint to introspect?") .interact_text()?; SubgraphConfig::from_subgraph_introspect( @@ -121,7 +121,7 @@ impl Init { remote_endpoint, ) } - studio_subgraph => { + "apollo studio subgraph" => { let graphref: String = Input::new() .with_prompt("What is the Apollo Studio graphref?") .interact_text()?; @@ -165,22 +165,22 @@ impl Init { } } - fn get_local_endpoint(&self) -> Result { + fn get_local_endpoint(&self) -> Result { if let Some(local_endpoint) = &self.local_endpoint { - Ok(Url::parse(local_endpoint)?) + Ok(local_endpoint.to_string()) } else { let local_endpoint: String = Input::new() .with_prompt("What URL does your subgraph run on locally?") .default("http://localhost:4000/".to_string()) .interact_text()?; - Ok(Url::parse(&local_endpoint)?) + Ok(local_endpoint) } } - fn get_remote_endpoint(&self) -> Result> { + fn get_remote_endpoint(&self) -> Result> { if let Some(remote_endpoint) = &self.remote_endpoint { - Ok(Some(Url::parse(remote_endpoint)?)) + Ok(Some(remote_endpoint.to_string())) } else { let local_endpoint: String = Input::new() .with_prompt("What URL does your subgraph run on when it is deployed? (optional)") @@ -189,7 +189,7 @@ impl Init { if local_endpoint.is_empty() { Ok(None) } else { - Ok(Some(Url::parse(&local_endpoint)?)) + Ok(Some(local_endpoint.to_string())) } } } diff --git a/src/command/subgraph/list.rs b/src/command/subgraph/list.rs index 6fe94e9f1..04f7862fb 100644 --- a/src/command/subgraph/list.rs +++ b/src/command/subgraph/list.rs @@ -20,7 +20,7 @@ pub struct List { impl List { pub fn run(&self, client_config: StudioClientConfig) -> Result { - let client = client_config.get_authenticated_client(&self.profile.profile_name)?; + let client = client_config.get_authenticated_client(&self.profile)?; eprintln!( "Listing subgraphs for {} using credentials from the {} profile.", diff --git a/src/command/subgraph/mod.rs b/src/command/subgraph/mod.rs index af8867f9f..eaaa2d99c 100644 --- a/src/command/subgraph/mod.rs +++ b/src/command/subgraph/mod.rs @@ -1,12 +1,15 @@ mod check; mod delete; +mod dev; mod fetch; mod init; mod introspect; mod list; mod publish; -use saucer::{clap, Parser}; +pub use dev::{Dev, SubgraphDevOpts}; + +use saucer::{clap, Parser, Utf8PathBuf}; use serde::Serialize; use crate::command::RoverOutput; @@ -27,6 +30,9 @@ pub enum Command { /// against the federated graph in the Apollo graph registry Check(check::Check), + /// Extend a supergraph with one or more local subgraphs + Dev(dev::Dev), + /// Delete a subgraph from the Apollo registry and trigger composition in the graph router Delete(delete::Delete), @@ -49,11 +55,13 @@ pub enum Command { impl Subgraph { pub fn run( &self, + override_install_path: Option, client_config: StudioClientConfig, git_context: GitContext, ) -> Result { match &self.command { Command::Check(command) => command.run(client_config, git_context), + Command::Dev(command) => command.run(override_install_path, client_config), Command::Delete(command) => command.run(client_config), Command::Fetch(command) => command.run(client_config), Command::Init(command) => command.run(), diff --git a/src/command/subgraph/publish.rs b/src/command/subgraph/publish.rs index 0933da1fe..b4c24da3e 100644 --- a/src/command/subgraph/publish.rs +++ b/src/command/subgraph/publish.rs @@ -117,13 +117,9 @@ impl Publish { None } }; - let variant = self.graph.variant(); - let graph_ref = GraphRef { - name: graph_id, - variant: variant.unwrap_or("current".to_string()), - }; + let graph_ref = GraphRef::new(graph_id, variant)?; let schema = if let Some(schema) = self .schema @@ -134,13 +130,13 @@ impl Publish { if let Some(subgraph_config) = &maybe_subgraph_config { Ok(subgraph_config .schema - .resolve(&client_config, &self.profile.profile_name)?) + .resolve(&client_config, &self.profile)?) } else { Err(anyhow!("you must specify a schema to publish")) } }?; - let client = client_config.get_authenticated_client(&self.profile.profile_name)?; + let client = client_config.get_authenticated_client(&self.profile)?; eprintln!( "Publishing SDL to {} (subgraph: {}) using credentials from the {} profile.", Cyan.normal().paint(&graph_ref.to_string()), diff --git a/src/command/supergraph/compose/do_compose.rs b/src/command/supergraph/compose/do_compose.rs index 6f8d66647..c6fa801cf 100644 --- a/src/command/supergraph/compose/do_compose.rs +++ b/src/command/supergraph/compose/do_compose.rs @@ -7,10 +7,12 @@ use crate::{ RoverOutput, }, error::{RoverError, Suggestion}, - options::ProfileOpt, + options::ComposeOpts, Context, Result, }; +use apollo_federation_types::build::SubgraphDefinition; +use apollo_federation_types::config::SupergraphConfig; use apollo_federation_types::{build::BuildResult, config::FederationVersion}; use rover_client::RoverClientError; @@ -21,7 +23,7 @@ use tempdir::TempDir; use std::{fs::File, io::Write, process::Command, str}; -#[derive(Debug, Serialize, Parser)] +#[derive(Debug, Clone, Serialize, Parser)] pub struct Compose { /// The relative path to the supergraph configuration file. You can pass `-` to use stdin instead of a file. #[clap(long = "config")] @@ -29,18 +31,17 @@ pub struct Compose { supergraph_yaml: FileDescriptorType, #[clap(flatten)] - profile: ProfileOpt, - - /// Accept the elv2 license if you are using federation 2. Note that you only need to do this once per machine. - #[clap(long = "elv2-license", parse(from_str = license_accept), case_insensitive = true, env = "APOLLO_ELV2_LICENSE")] - elv2_license_accepted: Option, - - /// Skip the update check - #[clap(long = "skip-update")] - skip_update: bool, + opts: ComposeOpts, } impl Compose { + pub fn new(compose_opts: ComposeOpts) -> Self { + Self { + supergraph_yaml: FileDescriptorType::File("RAM".into()), + opts: compose_opts, + } + } + pub fn run( &self, override_install_path: Option, @@ -49,25 +50,35 @@ impl Compose { let mut supergraph_config = resolve_supergraph_yaml( &self.supergraph_yaml, client_config.clone(), - &self.profile.profile_name, + &self.opts.profile, )?; + self.compose(override_install_path, client_config, &mut supergraph_config) + } + + pub fn compose( + &self, + override_install_path: Option, + client_config: StudioClientConfig, + supergraph_config: &mut SupergraphConfig, + ) -> Result { // first, grab the _actual_ federation version from the config we just resolved // (this will always be `Some` as long as we have created with `resolve_supergraph_yaml` so it is safe to unwrap) let federation_version = supergraph_config.get_federation_version().unwrap(); + // and create our plugin that we may need to install from it let plugin = Plugin::Supergraph(federation_version.clone()); let plugin_name = plugin.get_name(); let install_command = Install { force: false, plugin: Some(plugin), - elv2_license_accepted: self.elv2_license_accepted, + elv2_license_accepted: self.opts.elv2_license_accepted, }; // maybe do the install, maybe find a pre-existing installation, maybe fail let exe = install_command.get_versioned_plugin( override_install_path, client_config, - self.skip_update, + self.opts.skip_update, )?; // _then_, overwrite the federation_version with _only_ the major version @@ -131,6 +142,7 @@ impl Compose { #[cfg(test)] mod tests { use super::*; + use crate::options::ProfileOpt; use assert_fs::TempDir; use houston as houston_config; use houston_config::Config; @@ -167,7 +179,9 @@ mod tests { assert!(resolve_supergraph_yaml( &FileDescriptorType::File(config_path), get_studio_config(), - "profile" + &ProfileOpt { + profile_name: "profile".to_string() + } ) .is_err()) } @@ -195,7 +209,9 @@ mod tests { assert!(resolve_supergraph_yaml( &FileDescriptorType::File(config_path), get_studio_config(), - "profile" + &ProfileOpt { + profile_name: "profile".to_string() + } ) .is_ok()) } @@ -226,7 +242,9 @@ mod tests { let subgraph_definitions = resolve_supergraph_yaml( &FileDescriptorType::File(config_path), get_studio_config(), - "profile", + &ProfileOpt { + profile_name: "profile".to_string(), + }, ) .unwrap() .get_subgraph_definitions() diff --git a/src/command/supergraph/fetch.rs b/src/command/supergraph/fetch.rs index 5425d615b..983daf606 100644 --- a/src/command/supergraph/fetch.rs +++ b/src/command/supergraph/fetch.rs @@ -22,7 +22,7 @@ pub struct Fetch { impl Fetch { pub fn run(&self, client_config: StudioClientConfig) -> Result { - let client = client_config.get_authenticated_client(&self.profile.profile_name)?; + let client = client_config.get_authenticated_client(&self.profile)?; let graph_ref = self.graph.graph_ref.to_string(); eprintln!( "Fetching supergraph SDL from {} using credentials from the {} profile.", diff --git a/src/command/supergraph/resolve_config.rs b/src/command/supergraph/resolve_config.rs index 1965ac7c0..e5ccd8d92 100644 --- a/src/command/supergraph/resolve_config.rs +++ b/src/command/supergraph/resolve_config.rs @@ -12,13 +12,16 @@ use rover_client::operations::subgraph::fetch::{self, SubgraphFetchInput}; use rover_client::operations::subgraph::introspect::{self, SubgraphIntrospectInput}; use rover_client::shared::GraphRef; -use crate::utils::{client::StudioClientConfig, parsers::FileDescriptorType}; use crate::{anyhow, error::RoverError, Result, Suggestion}; +use crate::{ + options::ProfileOpt, + utils::{client::StudioClientConfig, parsers::FileDescriptorType}, +}; pub(crate) fn resolve_supergraph_yaml( unresolved_supergraph_yaml: &FileDescriptorType, client_config: StudioClientConfig, - profile_name: &str, + profile_opt: &ProfileOpt, ) -> Result { let mut subgraph_definitions = Vec::new(); @@ -90,7 +93,7 @@ pub(crate) fn resolve_supergraph_yaml( } => { // given a graph_ref and subgraph, run subgraph fetch to // obtain SDL and add it to subgraph_definition. - let client = client_config.get_authenticated_client(profile_name)?; + let client = client_config.get_authenticated_client(&profile_opt)?; let result = fetch::run( SubgraphFetchInput { graph_ref: GraphRef::from_str(graph_ref)?, diff --git a/src/dot_apollo/mod.rs b/src/dot_apollo/mod.rs index 02eb14ad6..ace2904eb 100644 --- a/src/dot_apollo/mod.rs +++ b/src/dot_apollo/mod.rs @@ -1,13 +1,20 @@ mod project_types; +use apollo_federation_types::build::SubgraphDefinition; use chrono::Utc; pub(crate) use project_types::{MultiSubgraphConfig, ProjectType, SubgraphConfig}; -use saucer::{Context, Fs, Utf8PathBuf}; +use saucer::{anyhow, Context, Fs, Utf8PathBuf}; use serde::{Deserialize, Serialize}; -use std::{env, time::Instant}; +use rover_client::shared::GraphRef; -use crate::Result; +use std::env; + +use crate::{ + options::{OptionalGraphRefOpt, OptionalSchemaOpt, OptionalSubgraphOpt, ProfileOpt}, + utils::client::StudioClientConfig, + Result, +}; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct DotApollo { diff --git a/src/dot_apollo/project_types/multi_subgraph/multi.rs b/src/dot_apollo/project_types/multi_subgraph/multi.rs index 6a454bb93..665ba28b9 100644 --- a/src/dot_apollo/project_types/multi_subgraph/multi.rs +++ b/src/dot_apollo/project_types/multi_subgraph/multi.rs @@ -1,6 +1,9 @@ use std::collections::BTreeMap; +use crate::{cli, options::ProfileOpt, utils::client::StudioClientConfig}; + use super::{SchemaSource, SubgraphConfig}; +use apollo_federation_types::build::SubgraphDefinition; use buildstructor::buildstructor; use reqwest::Url; use saucer::{anyhow, Result, Utf8Path}; @@ -81,6 +84,21 @@ impl MultiSubgraphConfig { )) } } + + pub(crate) fn get_all_subgraphs( + &self, + dev: bool, + client_config: &StudioClientConfig, + profile_opt: &ProfileOpt, + ) -> Result> { + let mut defs = Vec::new(); + for (name, config) in &self.subgraphs { + let url = config.url(dev)?; + let sdl = config.sdl(client_config, profile_opt)?; + defs.push(SubgraphDefinition::new(name, url, sdl)); + } + Ok(defs) + } } #[derive(Clone, Debug, Deserialize, Serialize)] diff --git a/src/dot_apollo/project_types/multi_subgraph/subgraph.rs b/src/dot_apollo/project_types/multi_subgraph/subgraph.rs index e54c3352b..c2547bdb1 100644 --- a/src/dot_apollo/project_types/multi_subgraph/subgraph.rs +++ b/src/dot_apollo/project_types/multi_subgraph/subgraph.rs @@ -1,8 +1,10 @@ use buildstructor::buildstructor; +use dialoguer::Input; use reqwest::Url; use saucer::{Fs, Result, Utf8Path, Utf8PathBuf}; use serde::{Deserialize, Serialize}; +use crate::options::ProfileOpt; use crate::utils::client::StudioClientConfig; use std::{collections::HashMap, str::FromStr}; @@ -20,28 +22,47 @@ pub struct SubgraphConfig { /// This will appear in supergraph SDL and /// instructs the graph router to send all requests /// for this subgraph to this URL. - pub remote_endpoint: Option, + pub remote_endpoint: Option, /// The routing URL for the subgraph when run locally. /// This will appear in supergraph SDL /// and instructs the graph router to send requests /// for this subgraph to this URL. - pub local_endpoint: Url, + pub local_endpoint: String, /// The location of the subgraph's SDL pub schema: SchemaSource, } impl SubgraphConfig { - pub fn routing_url(&self) -> Option { - self.remote_endpoint.clone() + pub fn url(&self, dev: bool) -> Result { + if dev { + Ok(self.local_endpoint.to_string()) + } else { + if let Some(remote_endpoint) = &self.remote_endpoint { + Ok(remote_endpoint.to_string()) + } else { + let remote_endpoint: String = Input::new() + .with_prompt("What endpoint is your subgraph deployed to?") + .interact_text()?; + Ok(remote_endpoint) + } + } + } + + pub fn sdl( + &self, + client_config: &StudioClientConfig, + profile_opt: &ProfileOpt, + ) -> Result { + self.schema.resolve(client_config, profile_opt) } } #[buildstructor] impl SubgraphConfig { #[builder(entry = "schema")] - pub fn from_file(file: F, local_endpoint: Url, remote_endpoint: Option) -> Self + pub fn from_file(file: F, local_endpoint: String, remote_endpoint: Option) -> Self where F: AsRef, { @@ -54,7 +75,11 @@ impl SubgraphConfig { } #[builder(entry = "introspect")] - pub fn from_subgraph_introspect(subgraph_url: Url, local: Url, remote: Option) -> Self { + pub fn from_subgraph_introspect( + subgraph_url: String, + local: String, + remote: Option, + ) -> Self { Self { schema: SchemaSource::SubgraphIntrospection { subgraph_url }, local_endpoint: local, @@ -66,8 +91,8 @@ impl SubgraphConfig { pub fn from_studio( graphref: String, subgraph_name: Option, - local: Url, - remote: Option, + local: String, + remote: Option, ) -> Self { Self { schema: SchemaSource::Studio { @@ -79,7 +104,7 @@ impl SubgraphConfig { } } - pub fn edit_remote_endpoint(&mut self, remote_endpoint: Url) { + pub fn edit_remote_endpoint(&mut self, remote_endpoint: String) { self.remote_endpoint = Some(remote_endpoint); } } @@ -99,7 +124,7 @@ pub enum SchemaSource { file: Utf8PathBuf, }, SubgraphIntrospection { - subgraph_url: Url, + subgraph_url: String, }, Studio { graphref: String, @@ -111,7 +136,7 @@ impl SchemaSource { pub fn resolve( &self, client_config: &StudioClientConfig, - profile_name: &str, + profile_opt: &ProfileOpt, ) -> Result { match &self { SchemaSource::File { file } => Fs::read_file(file, ""), @@ -135,7 +160,7 @@ impl SchemaSource { } => { // given a graph_ref and subgraph, run subgraph fetch to // obtain SDL and add it to subgraph_definition. - let client = client_config.get_authenticated_client(profile_name)?; + let client = client_config.get_authenticated_client(&profile_opt)?; let graph_ref = GraphRef::from_str(graph_ref)?; if let Some(subgraph) = subgraph { let result = subgraph::fetch::run( diff --git a/src/options/compose.rs b/src/options/compose.rs new file mode 100644 index 000000000..43ffdfd76 --- /dev/null +++ b/src/options/compose.rs @@ -0,0 +1,17 @@ +use super::ProfileOpt; +use crate::command::install::license_accept; +use saucer::{clap, Parser}; +use serde::Serialize; +#[derive(Debug, Clone, Serialize, Parser)] +pub struct ComposeOpts { + #[clap(flatten)] + pub profile: ProfileOpt, + + /// Accept the elv2 license if you are using federation 2. Note that you only need to do this once per machine. + #[clap(long = "elv2-license", parse(from_str = license_accept), case_insensitive = true, env = "APOLLO_ELV2_LICENSE")] + pub elv2_license_accepted: Option, + + /// Skip the update check + #[clap(long = "skip-update")] + pub skip_update: bool, +} diff --git a/src/options/graph.rs b/src/options/graph.rs index 7ee71e15f..735e79b87 100644 --- a/src/options/graph.rs +++ b/src/options/graph.rs @@ -11,7 +11,7 @@ pub struct GraphRefOpt { pub graph_ref: GraphRef, } -#[derive(Debug, Serialize, Deserialize, Parser)] +#[derive(Debug, Clone, Serialize, Deserialize, Parser)] pub struct OptionalGraphRefOpt { /// @ of graph in Apollo Studio. /// @ may be left off, defaulting to @current diff --git a/src/options/mod.rs b/src/options/mod.rs index 0c06e7406..f6869e47f 100644 --- a/src/options/mod.rs +++ b/src/options/mod.rs @@ -1,10 +1,12 @@ mod check; +mod compose; mod graph; mod profile; mod schema; mod subgraph; pub(crate) use check::*; +pub(crate) use compose::*; pub(crate) use graph::*; pub(crate) use profile::*; pub(crate) use schema::*; diff --git a/src/options/profile.rs b/src/options/profile.rs index 4dd54bc60..1f4f78f8d 100644 --- a/src/options/profile.rs +++ b/src/options/profile.rs @@ -1,10 +1,17 @@ use saucer::{clap, Parser}; use serde::{Deserialize, Serialize}; +use std::fmt::Display; -#[derive(Debug, Serialize, Deserialize, Parser)] +#[derive(Debug, Clone, Serialize, Deserialize, Parser)] pub struct ProfileOpt { /// Name of configuration profile to use #[clap(long = "profile", default_value = "default")] #[serde(skip_serializing)] pub profile_name: String, } + +impl Display for ProfileOpt { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", &self.profile_name) + } +} diff --git a/src/options/schema.rs b/src/options/schema.rs index ca7a1fada..6097f0568 100644 --- a/src/options/schema.rs +++ b/src/options/schema.rs @@ -21,7 +21,7 @@ impl SchemaOpt { } } -#[derive(Debug, Parser)] +#[derive(Debug, Clone, Parser)] pub struct OptionalSchemaOpt { /// The schema file to check. You can pass `-` to use stdin instead of a file. #[clap(long, short = 's')] diff --git a/src/options/subgraph.rs b/src/options/subgraph.rs index 28211f36b..cbcb8a1b6 100644 --- a/src/options/subgraph.rs +++ b/src/options/subgraph.rs @@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize}; use saucer::{anyhow, Error}; -#[derive(Debug, Serialize, Deserialize, Parser)] +#[derive(Debug, Clone, Serialize, Deserialize, Parser)] pub struct SubgraphOpt { /// Name of the subgraph to validate #[clap(long = "name")] @@ -11,7 +11,7 @@ pub struct SubgraphOpt { pub subgraph_name: String, } -#[derive(Debug, Serialize, Deserialize, Parser)] +#[derive(Debug, Clone, Serialize, Deserialize, Parser)] pub struct OptionalSubgraphOpt { /// Name of the subgraph to validate #[clap(long = "name")] diff --git a/src/utils/client.rs b/src/utils/client.rs index 3969bc3f2..212bb8f3d 100644 --- a/src/utils/client.rs +++ b/src/utils/client.rs @@ -1,7 +1,7 @@ use core::fmt; use std::{io, str::FromStr, time::Duration}; -use crate::{PKG_NAME, PKG_VERSION}; +use crate::{options::ProfileOpt, PKG_NAME, PKG_VERSION}; use saucer::Result; use houston as config; @@ -137,8 +137,8 @@ impl StudioClientConfig { self.client.clone() } - pub fn get_authenticated_client(&self, profile_name: &str) -> Result { - let credential = config::Profile::get_credential(profile_name, &self.config)?; + pub fn get_authenticated_client(&self, profile_opt: &ProfileOpt) -> Result { + let credential = config::Profile::get_credential(&profile_opt.profile_name, &self.config)?; Ok(StudioClient::new( credential, &self.uri, diff --git a/src/utils/parsers.rs b/src/utils/parsers.rs index c67c33e99..2659a74a0 100644 --- a/src/utils/parsers.rs +++ b/src/utils/parsers.rs @@ -8,7 +8,7 @@ use std::{ str::FromStr, }; -#[derive(Debug, PartialEq)] +#[derive(Debug, Clone, PartialEq)] pub enum FileDescriptorType { Stdin, File(Utf8PathBuf),