From b705026f3baf861904ed66f0dc6a69ca36a98780 Mon Sep 17 00:00:00 2001 From: Shawn Cooper Date: Fri, 6 Feb 2026 15:57:12 -0800 Subject: [PATCH 1/2] Add Sentry integration and enum input validation Initialize Sentry support and improve variable coercion for enums and input objects. Added sentry to the workspace and a server-common sentry module that reads configuration from environment and initializes the Sentry client; server startup now initializes sentry. graphql-router now captures GraphQL errors to Sentry (capture_graphql_error) and tags requests, and uses sentry::Level. OperationValidator was extended to properly coerce enum values and to handle input object variables (imports IndexMap and builds ConstValue::Object). Added an async unit test and an integration test for enum values in input-object variables, and bumped the workspace version to 0.29.9 (lockfile updated accordingly). --- Cargo.lock | 550 +++++++++++++++--- Cargo.toml | 3 +- .../src/validation/document_validator.rs | 68 +++ .../src/validation/operation_validator.rs | 97 ++- crates/graphql-router/Cargo.toml | 1 + crates/graphql-router/src/graphql_router.rs | 52 +- crates/server-common/Cargo.toml | 1 + crates/server-common/src/lib.rs | 3 + crates/server-common/src/sentry.rs | 53 ++ .../todo/tests/mutation-enum-variable.exotest | 36 ++ 10 files changed, 762 insertions(+), 102 deletions(-) create mode 100644 crates/server-common/src/sentry.rs create mode 100644 integration-tests/todo/tests/mutation-enum-variable.exotest diff --git a/Cargo.lock b/Cargo.lock index decdf25d8..0d6a7f68e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -465,9 +465,9 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.37" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d10e4f991a553474232bc0a31799f6d24b034a84c0971d80d2e2f78b2e576e40" +checksum = "82da0ea54ea533ec09d949717c6386a1c34f2d9b51c1fcc7eef8b9ce0b690a3e" dependencies = [ "compression-codecs", "compression-core", @@ -990,7 +990,7 @@ dependencies = [ [[package]] name = "builder" -version = "0.29.8" +version = "0.29.9" dependencies = [ "cc", "codemap", @@ -1365,7 +1365,7 @@ checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32" [[package]] name = "cli" -version = "0.29.8" +version = "0.29.9" dependencies = [ "anyhow", "async-recursion", @@ -1518,7 +1518,7 @@ checksum = "55b672471b4e9f9e95499ea597ff64941a309b2cdbffcc46f2cc5e2d971fd335" [[package]] name = "common" -version = "0.29.8" +version = "0.29.9" dependencies = [ "anyhow", "async-graphql-value", @@ -1713,7 +1713,7 @@ dependencies = [ [[package]] name = "core-model" -version = "0.29.8" +version = "0.29.9" dependencies = [ "async-graphql-parser", "async-graphql-value", @@ -1724,7 +1724,7 @@ dependencies = [ [[package]] name = "core-model-builder" -version = "0.29.8" +version = "0.29.9" dependencies = [ "codemap", "codemap-diagnostic", @@ -1736,7 +1736,7 @@ dependencies = [ [[package]] name = "core-plugin-interface" -version = "0.29.8" +version = "0.29.9" dependencies = [ "async-trait", "built", @@ -1750,7 +1750,7 @@ dependencies = [ [[package]] name = "core-plugin-shared" -version = "0.29.8" +version = "0.29.9" dependencies = [ "base16ct 0.3.0", "bincode 2.0.1", @@ -1767,7 +1767,7 @@ dependencies = [ [[package]] name = "core-resolver" -version = "0.29.8" +version = "0.29.9" dependencies = [ "async-graphql-parser", "async-graphql-value", @@ -1799,11 +1799,11 @@ dependencies = [ [[package]] name = "core-rest-model" -version = "0.29.8" +version = "0.29.9" [[package]] name = "core-router" -version = "0.29.8" +version = "0.29.9" dependencies = [ "core-plugin-interface", "core-plugin-shared", @@ -2365,7 +2365,7 @@ checksum = "26bf8fc351c5ed29b5c2f0cbbac1b209b74f60ecd62e675a998df72c49af5204" [[package]] name = "deno-builder" -version = "0.29.8" +version = "0.29.9" dependencies = [ "async-trait", "core-model-builder", @@ -2376,7 +2376,7 @@ dependencies = [ [[package]] name = "deno-graphql-builder" -version = "0.29.8" +version = "0.29.9" dependencies = [ "async-trait", "codemap", @@ -2395,7 +2395,7 @@ dependencies = [ [[package]] name = "deno-graphql-model" -version = "0.29.8" +version = "0.29.9" dependencies = [ "async-graphql-parser", "bincode 2.0.1", @@ -2407,7 +2407,7 @@ dependencies = [ [[package]] name = "deno-graphql-resolver" -version = "0.29.8" +version = "0.29.9" dependencies = [ "anyhow", "async-graphql-parser", @@ -2432,7 +2432,7 @@ dependencies = [ [[package]] name = "deno-resolver" -version = "0.29.8" +version = "0.29.9" dependencies = [ "async-trait", "core-plugin-interface", @@ -4269,7 +4269,7 @@ checksum = "31ae425815400e5ed474178a7a22e275a9687086a12ca63ec793ff292d8fdae8" [[package]] name = "exo-deno" -version = "0.29.8" +version = "0.29.9" dependencies = [ "async-trait", "ctor", @@ -4295,7 +4295,7 @@ dependencies = [ [[package]] name = "exo-env" -version = "0.29.8" +version = "0.29.9" dependencies = [ "dotenvy", "thiserror 2.0.18", @@ -4303,7 +4303,7 @@ dependencies = [ [[package]] name = "exo-sql" -version = "0.29.8" +version = "0.29.9" dependencies = [ "bincode 2.0.1", "bytes", @@ -4345,7 +4345,7 @@ dependencies = [ [[package]] name = "exo-wasm" -version = "0.29.8" +version = "0.29.9" dependencies = [ "anyhow", "serde_json", @@ -4488,6 +4488,18 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +[[package]] +name = "findshlibs" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40b9e59cd0f7e0806cca4be089683ecb6434e602038df21fe6bf6711b2f07f64" +dependencies = [ + "cc", + "lazy_static", + "libc", + "winapi", +] + [[package]] name = "fixedbitset" version = "0.4.2" @@ -4532,6 +4544,15 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared 0.1.1", +] + [[package]] name = "foreign-types" version = "0.5.0" @@ -4539,7 +4560,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" dependencies = [ "foreign-types-macros", - "foreign-types-shared", + "foreign-types-shared 0.3.1", ] [[package]] @@ -4553,6 +4574,12 @@ dependencies = [ "syn 2.0.114", ] +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "foreign-types-shared" version = "0.3.1" @@ -4923,7 +4950,7 @@ dependencies = [ [[package]] name = "graphql-router" -version = "0.29.8" +version = "0.29.9" dependencies = [ "async-graphql-parser", "async-stream", @@ -4936,6 +4963,7 @@ dependencies = [ "exo-env", "http 1.4.0", "introspection-resolver", + "sentry", "tracing", ] @@ -5197,6 +5225,17 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "hostname" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "617aaa3557aef3810a6369d0a99fac8a080891b68bd9f9812a1eeda0c0730cbd" +dependencies = [ + "cfg-if 1.0.4", + "libc", + "windows-link", +] + [[package]] name = "hstr" version = "2.1.0" @@ -5364,6 +5403,22 @@ dependencies = [ "tower-service", ] +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper 1.8.1", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + [[package]] name = "hyper-util" version = "0.1.20" @@ -5745,9 +5800,9 @@ dependencies = [ [[package]] name = "inquire" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae51d5da01ce7039024fbdec477767c102c454dbdb09d4e2a432ece705b1b25d" +checksum = "979f5ab9760427ada4fa5762b2d905e5b12704fb1fada07b6bfa66aeaa586f87" dependencies = [ "bitflags 2.10.0", "crossterm", @@ -5774,7 +5829,7 @@ dependencies = [ [[package]] name = "introspection-resolver" -version = "0.29.8" +version = "0.29.9" dependencies = [ "async-graphql-parser", "async-graphql-value", @@ -5789,7 +5844,7 @@ dependencies = [ [[package]] name = "introspection-util" -version = "0.29.8" +version = "0.29.9" dependencies = [ "anyhow", "ctor", @@ -6154,7 +6209,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b75877b724685dd49310bdbadbf973fc69b1d01992a6d4a861b928fc3943f87b" dependencies = [ "bytemuck", - "foreign-types", + "foreign-types 0.5.0", "lcms2-sys", ] @@ -6326,7 +6381,7 @@ checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" [[package]] name = "lsp" -version = "0.29.8" +version = "0.29.9" dependencies = [ "anyhow", "builder", @@ -6417,7 +6472,7 @@ checksum = "4facc753ae494aeb6e3c22f839b158aebd4f9270f55cd3c79906c45476c47ab4" [[package]] name = "mcp-bridge" -version = "0.29.8" +version = "0.29.9" dependencies = [ "anyhow", "clap", @@ -6431,7 +6486,7 @@ dependencies = [ [[package]] name = "mcp-router" -version = "0.29.8" +version = "0.29.9" dependencies = [ "async-graphql-parser", "async-stream", @@ -6478,9 +6533,9 @@ checksum = "ae960838283323069879657ca3de837e9f7bbb4c7bf6ea7f1b290d5e9476d2e0" [[package]] name = "memchr" -version = "2.7.6" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] name = "memfd" @@ -6518,7 +6573,7 @@ dependencies = [ "bitflags 2.10.0", "block", "core-graphics-types", - "foreign-types", + "foreign-types 0.5.0", "log", "objc", "paste", @@ -6674,6 +6729,23 @@ dependencies = [ "syn 2.0.114", ] +[[package]] +name = "native-tls" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe 0.1.6", + "openssl-sys", + "schannel", + "security-framework 2.11.1", + "security-framework-sys", + "tempfile", +] + [[package]] name = "ndk-sys" version = "0.5.0+25.2.9519653" @@ -6999,12 +7071,156 @@ dependencies = [ "objc2-encode", ] +[[package]] +name = "objc2-cloud-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ad74d880bb43877038da939b7427bba67e9dd42004a18b809ba7d87cee241c" +dependencies = [ + "bitflags 2.10.0", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-data" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b402a653efbb5e82ce4df10683b6b28027616a2715e90009947d50b8dd298fa" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +dependencies = [ + "bitflags 2.10.0", + "dispatch2", + "objc2", +] + +[[package]] +name = "objc2-core-graphics" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" +dependencies = [ + "bitflags 2.10.0", + "dispatch2", + "objc2", + "objc2-core-foundation", + "objc2-io-surface", +] + +[[package]] +name = "objc2-core-image" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d563b38d2b97209f8e861173de434bd0214cf020e3423a52624cd1d989f006" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-location" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca347214e24bc973fc025fd0d36ebb179ff30536ed1f80252706db19ee452009" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-text" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d" +dependencies = [ + "bitflags 2.10.0", + "objc2", + "objc2-core-foundation", + "objc2-core-graphics", +] + [[package]] name = "objc2-encode" version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" +[[package]] +name = "objc2-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" +dependencies = [ + "bitflags 2.10.0", + "block2", + "libc", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-io-surface" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" +dependencies = [ + "bitflags 2.10.0", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f" +dependencies = [ + "bitflags 2.10.0", + "objc2", + "objc2-core-foundation", + "objc2-foundation", +] + +[[package]] +name = "objc2-ui-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" +dependencies = [ + "bitflags 2.10.0", + "block2", + "objc2", + "objc2-cloud-kit", + "objc2-core-data", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-core-image", + "objc2-core-location", + "objc2-core-text", + "objc2-foundation", + "objc2-quartz-core", + "objc2-user-notifications", +] + +[[package]] +name = "objc2-user-notifications" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9df9128cbbfef73cda168416ccf7f837b62737d748333bfe9ab71c245d76613e" +dependencies = [ + "objc2", + "objc2-foundation", +] + [[package]] name = "object" version = "0.37.3" @@ -7070,6 +7286,32 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" +[[package]] +name = "openssl" +version = "0.10.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" +dependencies = [ + "bitflags 2.10.0", + "cfg-if 1.0.4", + "foreign-types 0.3.2", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + [[package]] name = "openssl-probe" version = "0.1.6" @@ -7082,6 +7324,18 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" +[[package]] +name = "openssl-sys" +version = "0.9.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "opentelemetry" version = "0.27.1" @@ -7190,6 +7444,22 @@ dependencies = [ "num-traits", ] +[[package]] +name = "os_info" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4022a17595a00d6a369236fdae483f0de7f0a339960a53118b818238e132224" +dependencies = [ + "android_system_properties", + "log", + "nix 0.30.1", + "objc2", + "objc2-foundation", + "objc2-ui-kit", + "serde", + "windows-sys 0.61.2", +] + [[package]] name = "os_pipe" version = "1.2.1" @@ -7557,7 +7827,7 @@ checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "playground-router" -version = "0.29.8" +version = "0.29.9" dependencies = [ "async-trait", "common", @@ -7639,7 +7909,7 @@ dependencies = [ [[package]] name = "postgres-builder" -version = "0.29.8" +version = "0.29.9" dependencies = [ "async-trait", "builder", @@ -7660,7 +7930,7 @@ dependencies = [ [[package]] name = "postgres-builder-dynamic" -version = "0.29.8" +version = "0.29.9" dependencies = [ "core-plugin-interface", "postgres-builder", @@ -7668,7 +7938,7 @@ dependencies = [ [[package]] name = "postgres-core-builder" -version = "0.29.8" +version = "0.29.9" dependencies = [ "builder", "codemap", @@ -7697,7 +7967,7 @@ dependencies = [ [[package]] name = "postgres-core-model" -version = "0.29.8" +version = "0.29.9" dependencies = [ "bincode 2.0.1", "builder", @@ -7717,7 +7987,7 @@ dependencies = [ [[package]] name = "postgres-core-resolver" -version = "0.29.8" +version = "0.29.9" dependencies = [ "async-trait", "base64 0.22.1", @@ -7741,7 +8011,7 @@ dependencies = [ [[package]] name = "postgres-graphql-builder" -version = "0.29.8" +version = "0.29.9" dependencies = [ "core-model", "core-model-builder", @@ -7755,7 +8025,7 @@ dependencies = [ [[package]] name = "postgres-graphql-model" -version = "0.29.8" +version = "0.29.9" dependencies = [ "async-graphql-parser", "async-graphql-value", @@ -7770,7 +8040,7 @@ dependencies = [ [[package]] name = "postgres-graphql-resolver" -version = "0.29.8" +version = "0.29.9" dependencies = [ "async-graphql-parser", "async-graphql-value", @@ -7820,7 +8090,7 @@ dependencies = [ [[package]] name = "postgres-resolver" -version = "0.29.8" +version = "0.29.9" dependencies = [ "async-trait", "common", @@ -7842,7 +8112,7 @@ dependencies = [ [[package]] name = "postgres-resolver-dynamic" -version = "0.29.8" +version = "0.29.9" dependencies = [ "core-plugin-interface", "postgres-resolver", @@ -7850,7 +8120,7 @@ dependencies = [ [[package]] name = "postgres-rest-builder" -version = "0.29.8" +version = "0.29.9" dependencies = [ "core-model-builder", "core-plugin-shared", @@ -7861,7 +8131,7 @@ dependencies = [ [[package]] name = "postgres-rest-model" -version = "0.29.8" +version = "0.29.9" dependencies = [ "bincode 2.0.1", "common", @@ -7877,7 +8147,7 @@ dependencies = [ [[package]] name = "postgres-rest-resolver" -version = "0.29.8" +version = "0.29.9" dependencies = [ "async-trait", "common", @@ -7890,7 +8160,7 @@ dependencies = [ [[package]] name = "postgres-rpc-builder" -version = "0.29.8" +version = "0.29.9" dependencies = [ "core-model-builder", "core-plugin-shared", @@ -7901,7 +8171,7 @@ dependencies = [ [[package]] name = "postgres-rpc-model" -version = "0.29.8" +version = "0.29.9" dependencies = [ "bincode 2.0.1", "core-model", @@ -7913,7 +8183,7 @@ dependencies = [ [[package]] name = "postgres-rpc-resolver" -version = "0.29.8" +version = "0.29.9" dependencies = [ "async-trait", "common", @@ -8459,9 +8729,11 @@ dependencies = [ "http-body-util", "hyper 1.8.1", "hyper-rustls", + "hyper-tls", "hyper-util", "js-sys", "log", + "native-tls", "percent-encoding", "pin-project-lite", "quinn", @@ -8473,6 +8745,7 @@ dependencies = [ "serde_urlencoded", "sync_wrapper", "tokio", + "tokio-native-tls", "tokio-rustls", "tokio-util", "tower 0.5.3", @@ -8493,7 +8766,7 @@ checksum = "1e061d1b48cb8d38042de4ae0a7a6401009d6143dc80d2e2d6f31f0bdd6470c7" [[package]] name = "rest-router" -version = "0.29.8" +version = "0.29.9" dependencies = [ "async-trait", "common", @@ -8563,7 +8836,7 @@ dependencies = [ [[package]] name = "rpc-router" -version = "0.29.8" +version = "0.29.9" dependencies = [ "async-stream", "async-trait", @@ -8989,6 +9262,114 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +[[package]] +name = "sentry" +version = "0.32.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00421ed8fa0c995f07cde48ba6c89e80f2b312f74ff637326f392fbfd23abe02" +dependencies = [ + "httpdate", + "native-tls", + "reqwest", + "sentry-backtrace", + "sentry-contexts", + "sentry-core", + "sentry-debug-images", + "sentry-panic", + "sentry-tracing", + "tokio", + "ureq", +] + +[[package]] +name = "sentry-backtrace" +version = "0.32.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a79194074f34b0cbe5dd33896e5928bbc6ab63a889bd9df2264af5acb186921e" +dependencies = [ + "backtrace", + "once_cell", + "regex", + "sentry-core", +] + +[[package]] +name = "sentry-contexts" +version = "0.32.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eba8870c5dba2bfd9db25c75574a11429f6b95957b0a78ac02e2970dd7a5249a" +dependencies = [ + "hostname", + "libc", + "os_info", + "rustc_version 0.4.1", + "sentry-core", + "uname", +] + +[[package]] +name = "sentry-core" +version = "0.32.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46a75011ea1c0d5c46e9e57df03ce81f5c7f0a9e199086334a1f9c0a541e0826" +dependencies = [ + "once_cell", + "rand 0.8.5", + "sentry-types", + "serde", + "serde_json", +] + +[[package]] +name = "sentry-debug-images" +version = "0.32.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ec2a486336559414ab66548da610da5e9626863c3c4ffca07d88f7dc71c8de8" +dependencies = [ + "findshlibs", + "once_cell", + "sentry-core", +] + +[[package]] +name = "sentry-panic" +version = "0.32.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eaa3ecfa3c8750c78dcfd4637cfa2598b95b52897ed184b4dc77fcf7d95060d" +dependencies = [ + "sentry-backtrace", + "sentry-core", +] + +[[package]] +name = "sentry-tracing" +version = "0.32.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f715932bf369a61b7256687c6f0554141b7ce097287e30e3f7ed6e9de82498fe" +dependencies = [ + "sentry-backtrace", + "sentry-core", + "tracing-core", + "tracing-subscriber", +] + +[[package]] +name = "sentry-types" +version = "0.32.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4519c900ce734f7a0eb7aba0869dfb225a7af8820634a7dd51449e3b093cfb7c" +dependencies = [ + "debugid", + "hex", + "rand 0.8.5", + "serde", + "serde_json", + "thiserror 1.0.69", + "time", + "url", + "uuid", +] + [[package]] name = "seq-macro" version = "0.3.6" @@ -9181,7 +9562,7 @@ dependencies = [ [[package]] name = "server-actix" -version = "0.29.8" +version = "0.29.9" dependencies = [ "actix-web", "common", @@ -9199,7 +9580,7 @@ dependencies = [ [[package]] name = "server-aws-lambda" -version = "0.29.8" +version = "0.29.9" dependencies = [ "builder", "common", @@ -9220,7 +9601,7 @@ dependencies = [ [[package]] name = "server-cf-worker" -version = "0.29.8" +version = "0.29.9" dependencies = [ "common", "console_error_panic_hook", @@ -9245,7 +9626,7 @@ dependencies = [ [[package]] name = "server-common" -version = "0.29.8" +version = "0.29.9" dependencies = [ "common", "core-plugin-interface", @@ -9253,6 +9634,7 @@ dependencies = [ "deno-resolver", "exo-env", "postgres-resolver", + "sentry", "system-router", "thiserror 2.0.18", "wasm-resolver", @@ -9667,7 +10049,7 @@ dependencies = [ [[package]] name = "subsystem-model-builder-util" -version = "0.29.8" +version = "0.29.9" dependencies = [ "async-trait", "builder", @@ -9684,7 +10066,7 @@ dependencies = [ [[package]] name = "subsystem-model-util" -version = "0.29.8" +version = "0.29.9" dependencies = [ "async-graphql-parser", "core-model", @@ -10171,7 +10553,7 @@ dependencies = [ [[package]] name = "system-router" -version = "0.29.8" +version = "0.29.9" dependencies = [ "async-trait", "common", @@ -10272,7 +10654,7 @@ dependencies = [ [[package]] name = "testing" -version = "0.29.8" +version = "0.29.9" dependencies = [ "anyhow", "async-graphql-parser", @@ -10499,6 +10881,16 @@ dependencies = [ "tokio-stream", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + [[package]] name = "tokio-postgres" version = "0.7.16" @@ -11013,6 +11405,15 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" +[[package]] +name = "uname" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b72f89f0ca32e4db1c04e2a72f5345d59796d4866a1ee0609084569f73683dc8" +dependencies = [ + "libc", +] + [[package]] name = "unic-char-property" version = "0.9.0" @@ -11157,6 +11558,19 @@ version = "0.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae" +[[package]] +name = "ureq" +version = "2.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02d1a66277ed75f640d608235660df48c8e3c19f3b4edb6a263315626cc3c01d" +dependencies = [ + "base64 0.22.1", + "log", + "native-tls", + "once_cell", + "url", +] + [[package]] name = "url" version = "2.5.8" @@ -11486,7 +11900,7 @@ checksum = "a8145dd1593bf0fb137dbfa85b8be79ec560a447298955877804640e40c2d6ea" [[package]] name = "wasm-builder" -version = "0.29.8" +version = "0.29.9" dependencies = [ "async-trait", "core-model-builder", @@ -11497,7 +11911,7 @@ dependencies = [ [[package]] name = "wasm-builder-dynamic" -version = "0.29.8" +version = "0.29.9" dependencies = [ "core-plugin-interface", "wasm-builder", @@ -11546,7 +11960,7 @@ dependencies = [ [[package]] name = "wasm-graphql-builder" -version = "0.29.8" +version = "0.29.9" dependencies = [ "async-trait", "core-model", @@ -11559,7 +11973,7 @@ dependencies = [ [[package]] name = "wasm-graphql-model" -version = "0.29.8" +version = "0.29.9" dependencies = [ "async-graphql-parser", "bincode 2.0.1", @@ -11571,7 +11985,7 @@ dependencies = [ [[package]] name = "wasm-graphql-resolver" -version = "0.29.8" +version = "0.29.9" dependencies = [ "async-graphql-parser", "async-trait", @@ -11587,7 +12001,7 @@ dependencies = [ [[package]] name = "wasm-resolver" -version = "0.29.8" +version = "0.29.9" dependencies = [ "async-trait", "core-plugin-interface", @@ -11601,7 +12015,7 @@ dependencies = [ [[package]] name = "wasm-resolver-dynamic" -version = "0.29.8" +version = "0.29.9" dependencies = [ "core-plugin-interface", "wasm-resolver", diff --git a/Cargo.toml b/Cargo.toml index 3cd05633b..2d74731f1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace.package] -version = "0.29.8" +version = "0.29.9" edition = "2024" # See https://github.com/mozilla/application-services/blob/main/Cargo.toml for the reasons why we use this structure @@ -133,6 +133,7 @@ pluralizer = "0.5.0" tracing-subscriber = "0.3.20" toml = { version = "0.9.5", features = ["parse"] } zip = "6.0.0" +sentry = "0.32.0" # reduce binary size, does not affect stack traces [profile.dev] diff --git a/crates/core-subsystem/core-resolver/src/validation/document_validator.rs b/crates/core-subsystem/core-resolver/src/validation/document_validator.rs index eb8471ec7..ad9789282 100644 --- a/crates/core-subsystem/core-resolver/src/validation/document_validator.rs +++ b/crates/core-subsystem/core-resolver/src/validation/document_validator.rs @@ -303,6 +303,36 @@ mod tests { ); } + #[cfg_attr(not(target_family = "wasm"), tokio::test)] + #[cfg_attr(target_family = "wasm", wasm_bindgen_test::wasm_bindgen_test)] + async fn enum_in_input_object_variable_is_valid() { + let schema = create_enum_test_schema().await; + + let variables = create_variables( + r#" + { + "data": { + "title": "EnumVar", + "priority": "LOW" + } + }"#, + ); + + let validator = DocumentValidator::new(&schema, None, Some(variables), 10, 10); + + let query = r#" + mutation($data: TaskCreationInput!) { + createTask(data: $data) { + id + priority + } + } + "#; + + let result = validator.validate(create_query_document(query)); + assert!(result.is_ok(), "{result:?}"); + } + #[cfg_attr(not(target_family = "wasm"), tokio::test)] #[cfg_attr(target_family = "wasm", wasm_bindgen_test::wasm_bindgen_test)] async fn invalid_subfield() { @@ -837,6 +867,44 @@ mod tests { ) } + async fn create_enum_test_schema() -> Schema { + let test_exo = r#" + @postgres + module LogModule { + @access(true) + type Task { + @pk id: Int = autoIncrement() + title: String + priority: Priority + } + + enum Priority { + LOW + MEDIUM + HIGH + } + } + "#; + + let postgres_subsystem = + create_postgres_system_from_str(test_exo, "enum-test.exo".to_string()).await; + + Schema::new( + postgres_subsystem.graphql.as_ref().unwrap().schema_types(), + postgres_subsystem + .graphql + .as_ref() + .unwrap() + .schema_queries(), + postgres_subsystem + .graphql + .as_ref() + .unwrap() + .schema_mutations(), + Arc::new(None), + ) + } + fn create_query_document(query_str: &str) -> ExecutableDocument { parse_query(query_str).unwrap() } diff --git a/crates/core-subsystem/core-resolver/src/validation/operation_validator.rs b/crates/core-subsystem/core-resolver/src/validation/operation_validator.rs index df5a42e89..2a594fc5f 100644 --- a/crates/core-subsystem/core-resolver/src/validation/operation_validator.rs +++ b/crates/core-subsystem/core-resolver/src/validation/operation_validator.rs @@ -16,7 +16,7 @@ use async_graphql_parser::{ VariableDefinition, }, }; -use async_graphql_value::{ConstValue, Name}; +use async_graphql_value::{ConstValue, Name, indexmap::IndexMap}; use serde::de::Error; use serde_json::{Map, Value}; @@ -226,33 +226,78 @@ impl<'a> OperationValidator<'a> { }), }, BaseType::Named(type_name) => { - if let Some(type_definition) = self.schema.get_type_definition(type_name.as_str()) - && let TypeKind::Enum(enum_type) = &type_definition.kind - { - return match value { - Value::String(enum_value) => { - let is_valid = enum_type - .values - .iter() - .any(|value_def| value_def.node.value.node.as_str() == enum_value); + if let Some(type_definition) = self.schema.get_type_definition(type_name.as_str()) { + match &type_definition.kind { + TypeKind::Enum(enum_type) => { + return match value { + Value::String(enum_value) => { + let is_valid = enum_type.values.iter().any(|value_def| { + value_def.node.value.node.as_str() == enum_value + }); - if is_valid { - Ok(ConstValue::Enum(Name::new(enum_value))) - } else { - Err(error(format!( - "Invalid enum value '{}' for type '{}'", - enum_value, - type_name.as_str() - ))) - } + if is_valid { + Ok(ConstValue::Enum(Name::new(enum_value))) + } else { + Err(error(format!( + "Invalid enum value '{}' for type '{}'", + enum_value, + type_name.as_str() + ))) + } + } + Value::Null => Ok(ConstValue::Null), + _ => Err(error(format!( + "Expected enum value for type '{}', got {}", + type_name.as_str(), + value + ))), + }; } - Value::Null => Ok(ConstValue::Null), - _ => Err(error(format!( - "Expected enum value for type '{}', got {}", - type_name.as_str(), - value - ))), - }; + TypeKind::InputObject(input_object_type) => { + return match value { + Value::Object(values) => { + let coerced_values = values + .into_iter() + .map(|(field_name, field_value)| { + let field_definition = input_object_type + .fields + .iter() + .find(|field| { + field.node.name.node.as_str() == field_name + }); + + let coerced_value = match field_definition { + Some(field_definition) => self.coerce_variable_value( + &field_definition.node.ty.node, + field_value, + variable_name, + )?, + None => ConstValue::from_json(field_value) + .map_err(|e| { + ValidationError::MalformedVariable( + variable_name.node.as_str().to_string(), + variable_name.pos, + e, + ) + })?, + }; + + Ok((Name::new(field_name), coerced_value)) + }) + .collect::, ValidationError>>()?; + + Ok(ConstValue::Object(coerced_values)) + } + Value::Null => Ok(ConstValue::Null), + _ => Err(error(format!( + "Expected input object for type '{}', got {}", + type_name.as_str(), + value + ))), + }; + } + _ => {} + } } ConstValue::from_json(value).map_err(|e| { diff --git a/crates/graphql-router/Cargo.toml b/crates/graphql-router/Cargo.toml index e25c0c0dc..ed43f312c 100644 --- a/crates/graphql-router/Cargo.toml +++ b/crates/graphql-router/Cargo.toml @@ -18,6 +18,7 @@ core-resolver = { path = "../core-subsystem/core-resolver" } introspection-resolver = { path = "../introspection-subsystem/introspection-resolver" } common = { path = "../common" } exo-env = { path = "../../libs/exo-env" } +sentry.workspace = true [dev-dependencies] diff --git a/crates/graphql-router/src/graphql_router.rs b/crates/graphql-router/src/graphql_router.rs index 54812e81a..f26f9b367 100644 --- a/crates/graphql-router/src/graphql_router.rs +++ b/crates/graphql-router/src/graphql_router.rs @@ -23,6 +23,7 @@ use core_resolver::introspection::definition::schema::Schema; use core_resolver::plugin::SubsystemGraphQLResolver; use core_router::SystemLoadingError; use http::StatusCode; +use sentry::Level; use ::tracing::instrument; use async_graphql_parser::Pos; @@ -83,6 +84,36 @@ impl GraphQLRouter { } } +fn capture_graphql_error( + err: &SystemResolutionError, + request_context: &RequestContext<'_>, + status_code: StatusCode, +) { + if sentry::Hub::current().client().is_none() { + return; + } + + let request_head = request_context.get_head(); + let message = err.user_error_message(); + + sentry::with_scope( + |scope| { + scope.set_tag("graphql.path", request_head.get_path()); + scope.set_tag("http.method", request_head.get_method().as_str()); + scope.set_tag("error.kind", format!("{:?}", err)); + scope.set_tag("internal_request", request_context.is_internal().to_string()); + scope.set_tag( + "auth_present", + request_context.is_authentication_info_present().to_string(), + ); + scope.set_extra("status_code", status_code.as_u16().into()); + }, + || { + sentry::capture_message(&message, Level::Error); + }, + ); +} + #[async_trait] impl<'a> Router> for GraphQLRouter { /// Resolves an incoming query, returning a response stream containing JSON and a set @@ -125,13 +156,20 @@ impl<'a> Router> for GraphQLRouter { ) .await; - if let Err(SystemResolutionError::RequestError(e)) = response { - tracing::error!("Error while resolving request: {:?}", e); - return Some(ResponsePayload { - body: ResponseBody::None, - headers: Headers::new(), - status_code: StatusCode::BAD_REQUEST, - }); + match &response { + Err(err @ SystemResolutionError::RequestError(e)) => { + tracing::error!("Error while resolving request: {:?}", e); + capture_graphql_error(err, request_context, StatusCode::BAD_REQUEST); + return Some(ResponsePayload { + body: ResponseBody::None, + headers: Headers::new(), + status_code: StatusCode::BAD_REQUEST, + }); + } + Err(err) => { + capture_graphql_error(err, request_context, StatusCode::OK); + } + Ok(_) => {} } let mut headers = if let Ok(ref response) = response { diff --git a/crates/server-common/Cargo.toml b/crates/server-common/Cargo.toml index eb81f7c27..2af5ff09d 100644 --- a/crates/server-common/Cargo.toml +++ b/crates/server-common/Cargo.toml @@ -17,6 +17,7 @@ postgres-resolver = { path = "../postgres-subsystem/postgres-resolver", features deno-resolver = { path = "../deno-subsystem/deno-resolver", optional = true } wasm-resolver = { path = "../wasm-subsystem/wasm-resolver", optional = true } exo-env = { path = "../../libs/exo-env" } +sentry.workspace = true [features] static-postgres-resolver = ["postgres-resolver"] diff --git a/crates/server-common/src/lib.rs b/crates/server-common/src/lib.rs index 8b3704c72..f7a5c794e 100644 --- a/crates/server-common/src/lib.rs +++ b/crates/server-common/src/lib.rs @@ -12,6 +12,8 @@ use std::{env, process::exit, sync::Arc}; use thiserror::Error; use common::logging_tracing::{self, OtelError}; + +mod sentry; use core_plugin_interface::interface::SubsystemLoader; use core_router::SystemLoadingError; @@ -29,6 +31,7 @@ use system_router::{SystemRouter, create_system_router_from_file}; /// - 1 - If the exo_ir file doesn't exist or can't be loaded. pub async fn init(env: Arc) -> Result { logging_tracing::init(env.as_ref()).await?; + sentry::init(env.as_ref()); println!( "Exograph server starting (version {})", diff --git a/crates/server-common/src/sentry.rs b/crates/server-common/src/sentry.rs new file mode 100644 index 000000000..ec2c086c7 --- /dev/null +++ b/crates/server-common/src/sentry.rs @@ -0,0 +1,53 @@ +use exo_env::Environment; +use std::sync::OnceLock; + +static SENTRY_GUARD: OnceLock = OnceLock::new(); + +fn get_env_value(env: &dyn Environment, key: &str) -> Option { + env.get(key).map(|value| value.trim().to_string()).filter(|v| !v.is_empty()) +} + +pub fn init(env: &dyn Environment) { + if SENTRY_GUARD.get().is_some() { + return; + } + + let dsn = get_env_value(env, "EXO_SENTRY_DSN") + .or_else(|| get_env_value(env, "SENTRY_DSN")); + + let Some(dsn) = dsn else { + return; + }; + + let environment = get_env_value(env, "EXO_SENTRY_ENVIRONMENT") + .or_else(|| get_env_value(env, "SENTRY_ENVIRONMENT")) + .or_else(|| get_env_value(env, "EXO_ENV")); + + let release = get_env_value(env, "EXO_SENTRY_RELEASE") + .or_else(|| get_env_value(env, "SENTRY_RELEASE")); + + let sample_rate = get_env_value(env, "EXO_SENTRY_SAMPLE_RATE") + .or_else(|| get_env_value(env, "SENTRY_SAMPLE_RATE")) + .and_then(|value| value.parse::().ok()) + .unwrap_or(1.0); + + let traces_sample_rate = get_env_value(env, "EXO_SENTRY_TRACES_SAMPLE_RATE") + .or_else(|| get_env_value(env, "SENTRY_TRACES_SAMPLE_RATE")) + .and_then(|value| value.parse::().ok()); + + let options = sentry::ClientOptions { + dsn: dsn.parse().ok(), + environment: environment.map(Into::into), + release: release.map(Into::into), + sample_rate, + traces_sample_rate, + ..Default::default() + }; + + let guard = sentry::init(options); + let _ = SENTRY_GUARD.set(guard); +} + +pub fn enabled() -> bool { + sentry::Hub::current().client().is_some() +} diff --git a/integration-tests/todo/tests/mutation-enum-variable.exotest b/integration-tests/todo/tests/mutation-enum-variable.exotest new file mode 100644 index 000000000..1b598d444 --- /dev/null +++ b/integration-tests/todo/tests/mutation-enum-variable.exotest @@ -0,0 +1,36 @@ + +stages: + - operation: | + mutation createTodoVar($data: TodoCreationInput!) { + createTodo(data: $data) { + id @bind(name: "varTodoId") + title + completed + priority + } + } + variable: | + { + "data": { + "title": "U1-T4-var-enum", + "completed": false, + "priority": "LOW", + "user": { "id": $.u1Id } + } + } + auth: | + { + "role": "admin", + "sub": null + } + response: | + { + "data": { + "createTodo": { + "id": $.varTodoId, + "title": "U1-T4-var-enum", + "completed": false, + "priority": "LOW" + } + } + } From 2035f8c9519b4d5afbc8768bd89a58c31c809e2b Mon Sep 17 00:00:00 2001 From: Shawn Cooper Date: Fri, 6 Feb 2026 19:47:58 -0800 Subject: [PATCH 2/2] trying to fix errors and linting --- .../src/validation/operation_validator.rs | 17 ++++++++--------- crates/graphql-router/src/graphql_router.rs | 5 ++++- crates/server-common/src/sentry.rs | 15 +++++++++------ 3 files changed, 21 insertions(+), 16 deletions(-) diff --git a/crates/core-subsystem/core-resolver/src/validation/operation_validator.rs b/crates/core-subsystem/core-resolver/src/validation/operation_validator.rs index 2a594fc5f..22ae94c17 100644 --- a/crates/core-subsystem/core-resolver/src/validation/operation_validator.rs +++ b/crates/core-subsystem/core-resolver/src/validation/operation_validator.rs @@ -259,19 +259,18 @@ impl<'a> OperationValidator<'a> { let coerced_values = values .into_iter() .map(|(field_name, field_value)| { - let field_definition = input_object_type - .fields - .iter() - .find(|field| { + let field_definition = + input_object_type.fields.iter().find(|field| { field.node.name.node.as_str() == field_name }); let coerced_value = match field_definition { - Some(field_definition) => self.coerce_variable_value( - &field_definition.node.ty.node, - field_value, - variable_name, - )?, + Some(field_definition) => self + .coerce_variable_value( + &field_definition.node.ty.node, + field_value, + variable_name, + )?, None => ConstValue::from_json(field_value) .map_err(|e| { ValidationError::MalformedVariable( diff --git a/crates/graphql-router/src/graphql_router.rs b/crates/graphql-router/src/graphql_router.rs index f26f9b367..ef9d16413 100644 --- a/crates/graphql-router/src/graphql_router.rs +++ b/crates/graphql-router/src/graphql_router.rs @@ -101,7 +101,10 @@ fn capture_graphql_error( scope.set_tag("graphql.path", request_head.get_path()); scope.set_tag("http.method", request_head.get_method().as_str()); scope.set_tag("error.kind", format!("{:?}", err)); - scope.set_tag("internal_request", request_context.is_internal().to_string()); + scope.set_tag( + "internal_request", + request_context.is_internal().to_string(), + ); scope.set_tag( "auth_present", request_context.is_authentication_info_present().to_string(), diff --git a/crates/server-common/src/sentry.rs b/crates/server-common/src/sentry.rs index ec2c086c7..1246b6d55 100644 --- a/crates/server-common/src/sentry.rs +++ b/crates/server-common/src/sentry.rs @@ -4,7 +4,9 @@ use std::sync::OnceLock; static SENTRY_GUARD: OnceLock = OnceLock::new(); fn get_env_value(env: &dyn Environment, key: &str) -> Option { - env.get(key).map(|value| value.trim().to_string()).filter(|v| !v.is_empty()) + env.get(key) + .map(|value| value.trim().to_string()) + .filter(|v| !v.is_empty()) } pub fn init(env: &dyn Environment) { @@ -12,8 +14,7 @@ pub fn init(env: &dyn Environment) { return; } - let dsn = get_env_value(env, "EXO_SENTRY_DSN") - .or_else(|| get_env_value(env, "SENTRY_DSN")); + let dsn = get_env_value(env, "EXO_SENTRY_DSN").or_else(|| get_env_value(env, "SENTRY_DSN")); let Some(dsn) = dsn else { return; @@ -23,8 +24,8 @@ pub fn init(env: &dyn Environment) { .or_else(|| get_env_value(env, "SENTRY_ENVIRONMENT")) .or_else(|| get_env_value(env, "EXO_ENV")); - let release = get_env_value(env, "EXO_SENTRY_RELEASE") - .or_else(|| get_env_value(env, "SENTRY_RELEASE")); + let release = + get_env_value(env, "EXO_SENTRY_RELEASE").or_else(|| get_env_value(env, "SENTRY_RELEASE")); let sample_rate = get_env_value(env, "EXO_SENTRY_SAMPLE_RATE") .or_else(|| get_env_value(env, "SENTRY_SAMPLE_RATE")) @@ -33,7 +34,8 @@ pub fn init(env: &dyn Environment) { let traces_sample_rate = get_env_value(env, "EXO_SENTRY_TRACES_SAMPLE_RATE") .or_else(|| get_env_value(env, "SENTRY_TRACES_SAMPLE_RATE")) - .and_then(|value| value.parse::().ok()); + .and_then(|value| value.parse::().ok()) + .unwrap_or(0.0); let options = sentry::ClientOptions { dsn: dsn.parse().ok(), @@ -48,6 +50,7 @@ pub fn init(env: &dyn Environment) { let _ = SENTRY_GUARD.set(guard); } +#[allow(dead_code)] pub fn enabled() -> bool { sentry::Hub::current().client().is_some() }