diff --git a/Cargo.lock b/Cargo.lock index 168e63c5..6010b7b8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -76,7 +76,7 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" dependencies = [ - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -86,7 +86,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "58f54d10c6dfa51283a066ceab3ec1ab78d13fae00aa49243a45e4571fb79dfd" dependencies = [ "anstyle", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -245,6 +245,17 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" +[[package]] +name = "bstr" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" +dependencies = [ + "lazy_static", + "memchr", + "regex-automata 0.1.10", +] + [[package]] name = "bumpalo" version = "3.14.0" @@ -288,7 +299,7 @@ dependencies = [ "iana-time-zone", "num-traits", "serde", - "windows-targets", + "windows-targets 0.48.5", ] [[package]] @@ -345,7 +356,19 @@ checksum = "2674ec482fbc38012cf31e6c42ba0177b431a0cb6f15fe40efa5aab1bda516f6" dependencies = [ "is-terminal", "lazy_static", - "windows-sys", + "windows-sys 0.48.0", +] + +[[package]] +name = "console" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8" +dependencies = [ + "encode_unicode", + "lazy_static", + "libc", + "windows-sys 0.45.0", ] [[package]] @@ -473,6 +496,12 @@ version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + [[package]] name = "encoding_rs" version = "0.8.33" @@ -496,7 +525,7 @@ checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd" dependencies = [ "errno-dragonfly", "libc", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -647,6 +676,16 @@ version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" +[[package]] +name = "goldenfile" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86342e69ffaa1cd5450d6bad08a80da96c441d891a0e07c72c62c4abdd281713" +dependencies = [ + "similar-asserts", + "tempfile", +] + [[package]] name = "h2" version = "0.3.21" @@ -860,7 +899,7 @@ checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ "hermit-abi", "rustix", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -981,7 +1020,7 @@ checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", "wasi", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -1005,7 +1044,7 @@ dependencies = [ [[package]] name = "ndc-client" version = "0.1.0" -source = "git+http://github.com/hasura/ndc-spec.git?tag=v0.1.0-rc.7#1edc777373579299fac06149e48e9744865fda49" +source = "git+http://github.com/hasura/ndc-spec.git?rev=2c2def8#2c2def8a01af2f95100b4274599030c930525bee" dependencies = [ "async-trait", "indexmap 1.9.3", @@ -1031,6 +1070,7 @@ dependencies = [ "bytes", "clap", "gdc_rust_types", + "goldenfile", "http", "indexmap 1.9.3", "mime", @@ -1060,7 +1100,7 @@ dependencies = [ [[package]] name = "ndc-test" version = "0.1.0" -source = "git+http://github.com/hasura/ndc-spec.git?tag=v0.1.0-rc.7#1edc777373579299fac06149e48e9744865fda49" +source = "git+http://github.com/hasura/ndc-spec.git?rev=2c2def8#2c2def8a01af2f95100b4274599030c930525bee" dependencies = [ "async-trait", "clap", @@ -1070,6 +1110,7 @@ dependencies = [ "proptest", "reqwest", "semver", + "serde", "serde_json", "thiserror", "tokio", @@ -1314,7 +1355,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets", + "windows-targets 0.48.5", ] [[package]] @@ -1603,7 +1644,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -1636,7 +1677,7 @@ version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" dependencies = [ - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -1870,6 +1911,26 @@ dependencies = [ "libc", ] +[[package]] +name = "similar" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aeaf503862c419d66959f5d7ca015337d864e9c49485d771b732e2a20453597" +dependencies = [ + "bstr", + "unicode-segmentation", +] + +[[package]] +name = "similar-asserts" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e041bb827d1bfca18f213411d51b665309f1afb37a04a5d1464530e13779fc0f" +dependencies = [ + "console", + "similar", +] + [[package]] name = "slab" version = "0.4.9" @@ -1911,7 +1972,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4031e820eb552adee9295814c0ced9e5cf38ddf1e8b7d566d6de8e2538ea989e" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -1958,7 +2019,7 @@ dependencies = [ "fastrand", "redox_syscall", "rustix", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -2050,7 +2111,7 @@ dependencies = [ "signal-hook-registry", "socket2 0.5.4", "tokio-macros", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -2319,6 +2380,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + [[package]] name = "url" version = "2.4.1" @@ -2507,7 +2574,16 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" dependencies = [ - "windows-targets", + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", ] [[package]] @@ -2516,7 +2592,22 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets", + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", ] [[package]] @@ -2525,51 +2616,93 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -2583,5 +2716,5 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ "cfg-if", - "windows-sys", + "windows-sys 0.48.0", ] diff --git a/rust-connector-sdk/Cargo.toml b/rust-connector-sdk/Cargo.toml index 80cd661c..cabe5f67 100644 --- a/rust-connector-sdk/Cargo.toml +++ b/rust-connector-sdk/Cargo.toml @@ -16,8 +16,8 @@ async-trait = "^0.1.68" axum = "^0.6.18" axum-macros = "^0.3.7" clap = { version = "^4.3.9", features = ["derive", "env"] } -ndc-client = { git = "http://github.com/hasura/ndc-spec.git", tag = "v0.1.0-rc.7" } -ndc-test = { git = "http://github.com/hasura/ndc-spec.git", tag = "v0.1.0-rc.7" } +ndc-client = { git = "http://github.com/hasura/ndc-spec.git", rev = "2c2def8" } +ndc-test = { git = "http://github.com/hasura/ndc-spec.git", rev = "2c2def8" } http = "^0.2" opentelemetry = { version = "^0.20", features = [ "rt-tokio", @@ -58,3 +58,4 @@ mime = "0.3.17" [dev-dependencies] axum-test-helper = "0.3.0" +goldenfile = "^1.5.2" diff --git a/rust-connector-sdk/src/default_main.rs b/rust-connector-sdk/src/default_main.rs index 1adaff62..b6d513d5 100644 --- a/rust-connector-sdk/src/default_main.rs +++ b/rust-connector-sdk/src/default_main.rs @@ -29,9 +29,9 @@ use ndc_test::report; use prometheus::Registry; use schemars::{schema::RootSchema, JsonSchema}; use serde::{de::DeserializeOwned, Serialize}; -use std::error::Error; use std::net; use std::process::exit; +use std::{error::Error, path::PathBuf}; use tower_http::{cors::CorsLayer, trace::TraceLayer}; use self::v2_compat::SourceConfig; @@ -51,6 +51,8 @@ enum Command { #[command()] Test(TestCommand), #[command()] + Replay(ReplayCommand), + #[command()] CheckHealth(CheckHealthCommand), } @@ -98,10 +100,20 @@ struct ServeConfigurationCommand { #[derive(Clone, Parser)] struct TestCommand { - #[arg(long, value_name = "PORT", env = "PORT")] + #[arg(long, value_name = "SEED", env = "SEED")] seed: Option, #[arg(long, value_name = "CONFIGURATION_FILE", env = "CONFIGURATION_FILE")] configuration: String, + #[arg(long, value_name = "DIRECTORY", env = "SNAPSHOTS_DIR")] + snapshots_dir: Option, +} + +#[derive(Clone, Parser)] +struct ReplayCommand { + #[arg(long, value_name = "CONFIGURATION_FILE", env = "CONFIGURATION_FILE")] + configuration: String, + #[arg(long, value_name = "DIRECTORY", env = "SNAPSHOTS_DIR")] + snapshots_dir: PathBuf, } #[derive(Clone, Parser)] @@ -157,6 +169,7 @@ where Command::Serve(serve_command) => serve::(serve_command).await, Command::Configuration(configure_command) => configuration::(configure_command).await, Command::Test(test_command) => test::(test_command).await, + Command::Replay(replay_command) => replay::(replay_command).await, Command::CheckHealth(check_health_command) => check_health(check_health_command).await, } } @@ -592,6 +605,19 @@ where Err(err) => Err(ndc_test::Error::OtherError(err.into())), } } + + async fn mutation( + &self, + request: ndc_client::models::MutationRequest, + ) -> Result { + match C::mutation(&self.configuration, &self.state, request) + .await + .and_then(JsonResponse::into_value) + { + Ok(response) => Ok(response), + Err(err) => Err(ndc_test::Error::OtherError(err.into())), + } + } } async fn test( @@ -602,9 +628,52 @@ where C::Configuration: Sync + Send + 'static, C::State: Send + Sync + 'static, { - let test_configuration = ndc_test::TestConfiguration { seed: command.seed }; + let test_configuration = ndc_test::TestConfiguration { + seed: command.seed, + snapshots_dir: command.snapshots_dir, + }; + + let connector = make_connector_adapter::(command.configuration).await; + let results = ndc_test::test_connector(&test_configuration, &connector).await; + + if !results.failures.is_empty() { + println!(); + println!("{}", report(results)); - let configuration_json = std::fs::read_to_string(command.configuration).unwrap(); + exit(1) + } + + Ok(()) +} + +async fn replay( + command: ReplayCommand, +) -> Result<(), Box> +where + C::RawConfiguration: DeserializeOwned, + C::Configuration: Sync + Send + 'static, + C::State: Send + Sync + 'static, +{ + let connector = make_connector_adapter::(command.configuration).await; + let results = ndc_test::test_snapshots_in_directory(&connector, command.snapshots_dir).await; + + if !results.failures.is_empty() { + println!(); + println!("{}", report(results)); + + exit(1) + } + + Ok(()) +} + +async fn make_connector_adapter( + configuration_path: String, +) -> ConnectorAdapter +where + C::RawConfiguration: DeserializeOwned, +{ + let configuration_json = std::fs::read_to_string(configuration_path).unwrap(); let raw_configuration = serde_json::de::from_str::(configuration_json.as_str()).unwrap(); let configuration = C::validate_raw_configuration(raw_configuration) @@ -616,21 +685,12 @@ where .await .unwrap(); - let connector = ConnectorAdapter:: { + ConnectorAdapter:: { configuration, state, - }; - let results = ndc_test::test_connector(&test_configuration, &connector).await; - - if !results.failures.is_empty() { - println!(); - println!("{}", report(results)); - - exit(1) } - - Ok(()) } + async fn check_health( CheckHealthCommand { host, port }: CheckHealthCommand, ) -> Result<(), Box> { diff --git a/rust-connector-sdk/src/lib.rs b/rust-connector-sdk/src/lib.rs index 940485b4..4439bdcb 100644 --- a/rust-connector-sdk/src/lib.rs +++ b/rust-connector-sdk/src/lib.rs @@ -3,6 +3,7 @@ pub mod connector; pub mod default_main; pub mod json_response; pub mod routes; +pub mod secret; pub mod tracing; pub use ndc_client::models; diff --git a/rust-connector-sdk/src/secret.rs b/rust-connector-sdk/src/secret.rs new file mode 100644 index 00000000..6a582816 --- /dev/null +++ b/rust-connector-sdk/src/secret.rs @@ -0,0 +1,70 @@ +use schemars::{self, schema::Schema, JsonSchema}; +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema)] +#[serde(rename_all = "camelCase")] +#[schemars(title = "SecretValue")] +/// Either a literal string or a reference to a Hasura secret +pub enum SecretValueImpl { + Value(String), + StringValueFromSecret(String), +} + +/// Use this type to refer to secret strings within a +/// connector's configuration types. For example, a connection +/// string which might contain a password should be configured +/// using this type. +/// +/// This marker type indicates that a value should be configured +/// from secrets drawn from a secret store. +pub struct SecretValue(pub SecretValueImpl); + +impl JsonSchema for SecretValue { + fn schema_name() -> String { + SecretValueImpl::schema_name() + } + + fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { + let mut s = SecretValueImpl::json_schema(gen); + if let Schema::Object(o) = &mut s { + if let Some(m) = &mut o.metadata { + m.id = Some(Self::schema_id().into()); + } + } + s + } + + fn schema_id() -> std::borrow::Cow<'static, str> { + "https://hasura.io/jsonschemas/SecretValue".into() + } +} + +#[cfg(test)] +mod tests { + use goldenfile::Mint; + use schemars::schema_for; + use std::io::Write; + use std::path::PathBuf; + + use super::SecretValue; + + #[test] + pub fn test_json_schema() { + let test_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests"); + + let mut mint = Mint::new(&test_dir); + + let expected_path = PathBuf::from_iter(["json_schema", "secret_value.jsonschema"]); + + let mut expected = mint.new_goldenfile(expected_path).unwrap(); + + let schema = schema_for!(SecretValue); + + write!( + expected, + "{}", + serde_json::to_string_pretty(&schema).unwrap() + ) + .unwrap(); + } +} diff --git a/rust-connector-sdk/tests/json_schema/secret_value.jsonschema b/rust-connector-sdk/tests/json_schema/secret_value.jsonschema new file mode 100644 index 00000000..7c5b0c38 --- /dev/null +++ b/rust-connector-sdk/tests/json_schema/secret_value.jsonschema @@ -0,0 +1,32 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://hasura.io/jsonschemas/SecretValue", + "title": "SecretValue", + "description": "Either a literal string or a reference to a Hasura secret", + "oneOf": [ + { + "type": "object", + "required": [ + "value" + ], + "properties": { + "value": { + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "stringValueFromSecret" + ], + "properties": { + "stringValueFromSecret": { + "type": "string" + } + }, + "additionalProperties": false + } + ] +} \ No newline at end of file