diff --git a/.cargo/config.toml b/.cargo/config.toml index c372e06f92..852384da78 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -22,13 +22,7 @@ rustflags = [ # "-Wmissing_docs", "-Wrust_2018_idioms", "-Wunused_qualifications", - "--cfg", - "uuid_unstable" ] - -[build] -rustdocflags = ["--cfg", "uuid_unstable"] - [alias] gen-pg = "generate --path ../../../../connector-template -n" diff --git a/Cargo.lock b/Cargo.lock index e3c39b7184..49290228e5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,11 +4,11 @@ version = 3 [[package]] name = "actix-codec" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "617a8268e3537fe1d8c9ead925fca49ef6400927ee7bc26750e90ecee14ce4b8" +checksum = "5f7b0a21988c1bf877cf4759ef5ddaac04c1c9fe808c9142ecb78ba97d97a28a" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.0", "bytes 1.5.0", "futures-core", "futures-sink", @@ -31,22 +31,22 @@ dependencies = [ "futures-util", "log", "once_cell", - "smallvec 1.11.1", + "smallvec 1.13.1", ] [[package]] name = "actix-http" -version = "3.3.1" +version = "3.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2079246596c18b4a33e274ae10c0e50613f4d32a4198e09c7b93771013fed74" +checksum = "d223b13fd481fc0d1f83bb12659ae774d9e3601814c68a0bc539731698cca743" dependencies = [ "actix-codec", "actix-rt", "actix-service", "actix-utils", - "ahash 0.8.6", - "base64 0.21.5", - "bitflags 1.3.2", + "ahash 0.8.11", + "base64 0.21.7", + "bitflags 2.4.0", "brotli", "bytes 1.5.0", "bytestring", @@ -55,7 +55,7 @@ dependencies = [ "flate2", "futures-core", "h2", - "http 0.2.9", + "http 0.2.12", "httparse", "httpdate", "itoa", @@ -66,7 +66,7 @@ dependencies = [ "pin-project-lite", "rand 0.8.5", "sha1", - "smallvec 1.11.1", + "smallvec 1.13.1", "tokio 1.36.0", "tokio-util", "tracing", @@ -80,7 +80,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" dependencies = [ "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -118,17 +118,17 @@ dependencies = [ "parse-size", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] name = "actix-router" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66ff4d247d2b160861fa2866457e85706833527840e4133f8f49aa423a38799" +checksum = "d22475596539443685426b6bdadb926ad0ecaefdfc5fb05e5e3441f15463c511" dependencies = [ "bytestring", - "http 0.2.9", + "http 0.2.12", "regex", "serde", "tracing", @@ -156,8 +156,8 @@ dependencies = [ "actix-utils", "futures-core", "futures-util", - "mio 0.8.10", - "socket2 0.5.5", + "mio 0.8.11", + "socket2 0.5.6", "tokio 1.36.0", "tracing", ] @@ -183,7 +183,7 @@ dependencies = [ "actix-service", "actix-utils", "futures-core", - "http 0.2.9", + "http 0.2.12", "impl-more", "pin-project-lite", "rustls 0.21.10", @@ -207,9 +207,9 @@ dependencies = [ [[package]] name = "actix-web" -version = "4.3.1" +version = "4.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd3cb42f9566ab176e1ef0b8b3a896529062b4efc6be0123046095914c4c1c96" +checksum = "43a6556ddebb638c2358714d853257ed226ece6023ef9364f23f0c70737ea984" dependencies = [ "actix-codec", "actix-http", @@ -220,7 +220,7 @@ dependencies = [ "actix-service", "actix-utils", "actix-web-codegen", - "ahash 0.7.7", + "ahash 0.8.11", "bytes 1.5.0", "bytestring", "cfg-if 1.0.0", @@ -229,7 +229,6 @@ dependencies = [ "encoding_rs", "futures-core", "futures-util", - "http 0.2.9", "itoa", "language-tags", "log", @@ -240,8 +239,8 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "smallvec 1.11.1", - "socket2 0.4.9", + "smallvec 1.13.1", + "socket2 0.5.6", "time", "url", ] @@ -255,7 +254,7 @@ dependencies = [ "actix-router", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -285,19 +284,19 @@ version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a824f2aa7e75a0c98c5a504fceb80649e9c35265d44525b5f94de4771a395cd" dependencies = [ - "getrandom 0.2.11", + "getrandom 0.2.12", "once_cell", "version_check", ] [[package]] name = "ahash" -version = "0.8.6" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if 1.0.0", - "getrandom 0.2.11", + "getrandom 0.2.12", "once_cell", "version_check", "zerocopy", @@ -305,9 +304,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea5d730647d4fadd988536d06fecce94b7b4f2a7efdae548f1cf4b63205518ab" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" dependencies = [ "memchr", ] @@ -566,7 +565,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -577,7 +576,7 @@ checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -614,7 +613,7 @@ dependencies = [ "actix-tls", "actix-utils", "ahash 0.7.7", - "base64 0.21.5", + "base64 0.21.7", "bytes 1.5.0", "cfg-if 1.0.0", "cookie 0.16.2", @@ -622,7 +621,7 @@ dependencies = [ "futures-core", "futures-util", "h2", - "http 0.2.9", + "http 0.2.12", "itoa", "log", "mime", @@ -656,7 +655,7 @@ dependencies = [ "bytes 1.5.0", "fastrand 1.9.0", "hex", - "http 0.2.9", + "http 0.2.12", "hyper", "ring 0.16.20", "time", @@ -687,7 +686,7 @@ dependencies = [ "bytes 1.5.0", "fastrand 2.0.1", "hex", - "http 0.2.9", + "http 0.2.12", "hyper", "ring 0.17.8", "time", @@ -731,7 +730,7 @@ dependencies = [ "aws-smithy-http 0.55.3", "aws-smithy-types 0.55.3", "aws-types 0.55.3", - "http 0.2.9", + "http 0.2.12", "regex", "tracing", ] @@ -747,7 +746,7 @@ dependencies = [ "aws-smithy-types 0.55.3", "aws-types 0.55.3", "bytes 1.5.0", - "http 0.2.9", + "http 0.2.12", "http-body", "lazy_static", "percent-encoding", @@ -770,7 +769,7 @@ dependencies = [ "aws-types 1.1.7", "bytes 1.5.0", "fastrand 2.0.1", - "http 0.2.9", + "http 0.2.12", "http-body", "percent-encoding", "pin-project-lite", @@ -796,7 +795,7 @@ dependencies = [ "aws-smithy-types 0.55.3", "aws-types 0.55.3", "bytes 1.5.0", - "http 0.2.9", + "http 0.2.12", "regex", "tokio-stream", "tower", @@ -819,7 +818,7 @@ dependencies = [ "aws-smithy-types 1.1.7", "aws-types 1.1.7", "bytes 1.5.0", - "http 0.2.9", + "http 0.2.12", "once_cell", "regex-lite", "tracing", @@ -847,7 +846,7 @@ dependencies = [ "aws-smithy-xml 0.55.3", "aws-types 0.55.3", "bytes 1.5.0", - "http 0.2.9", + "http 0.2.12", "http-body", "once_cell", "percent-encoding", @@ -876,7 +875,7 @@ dependencies = [ "aws-smithy-types 0.55.3", "aws-types 0.55.3", "bytes 1.5.0", - "http 0.2.9", + "http 0.2.12", "regex", "tokio-stream", "tower", @@ -901,7 +900,7 @@ dependencies = [ "aws-smithy-types 0.55.3", "aws-types 0.55.3", "bytes 1.5.0", - "http 0.2.9", + "http 0.2.12", "regex", "tokio-stream", "tower", @@ -924,7 +923,7 @@ dependencies = [ "aws-smithy-types 1.1.7", "aws-types 1.1.7", "bytes 1.5.0", - "http 0.2.9", + "http 0.2.12", "once_cell", "regex-lite", "tracing", @@ -946,7 +945,7 @@ dependencies = [ "aws-smithy-types 1.1.7", "aws-types 1.1.7", "bytes 1.5.0", - "http 0.2.9", + "http 0.2.12", "once_cell", "regex-lite", "tracing", @@ -972,7 +971,7 @@ dependencies = [ "aws-smithy-xml 0.55.3", "aws-types 0.55.3", "bytes 1.5.0", - "http 0.2.9", + "http 0.2.12", "regex", "tower", "tracing", @@ -995,7 +994,7 @@ dependencies = [ "aws-smithy-types 1.1.7", "aws-smithy-xml 0.60.6", "aws-types 1.1.7", - "http 0.2.9", + "http 0.2.12", "once_cell", "regex-lite", "tracing", @@ -1012,7 +1011,7 @@ dependencies = [ "aws-smithy-eventstream", "aws-smithy-http 0.55.3", "aws-types 0.55.3", - "http 0.2.9", + "http 0.2.12", "tracing", ] @@ -1028,7 +1027,7 @@ dependencies = [ "form_urlencoded", "hex", "hmac", - "http 0.2.9", + "http 0.2.12", "once_cell", "percent-encoding", "regex", @@ -1051,8 +1050,8 @@ dependencies = [ "form_urlencoded", "hex", "hmac", - "http 0.2.9", - "http 1.0.0", + "http 0.2.12", + "http 1.1.0", "once_cell", "percent-encoding", "sha2", @@ -1095,7 +1094,7 @@ dependencies = [ "crc32c", "crc32fast", "hex", - "http 0.2.9", + "http 0.2.12", "http-body", "md-5", "pin-project-lite", @@ -1116,7 +1115,7 @@ dependencies = [ "aws-smithy-types 0.55.3", "bytes 1.5.0", "fastrand 1.9.0", - "http 0.2.9", + "http 0.2.12", "http-body", "hyper", "hyper-rustls 0.23.2", @@ -1150,7 +1149,7 @@ dependencies = [ "bytes 1.5.0", "bytes-utils", "futures-core", - "http 0.2.9", + "http 0.2.12", "http-body", "hyper", "once_cell", @@ -1173,7 +1172,7 @@ dependencies = [ "bytes 1.5.0", "bytes-utils", "futures-core", - "http 0.2.9", + "http 0.2.12", "http-body", "once_cell", "percent-encoding", @@ -1191,7 +1190,7 @@ dependencies = [ "aws-smithy-http 0.55.3", "aws-smithy-types 0.55.3", "bytes 1.5.0", - "http 0.2.9", + "http 0.2.12", "http-body", "pin-project-lite", "tower", @@ -1249,7 +1248,7 @@ dependencies = [ "bytes 1.5.0", "fastrand 2.0.1", "h2", - "http 0.2.9", + "http 0.2.12", "http-body", "hyper", "hyper-rustls 0.24.2", @@ -1270,8 +1269,8 @@ dependencies = [ "aws-smithy-async 1.1.7", "aws-smithy-types 1.1.7", "bytes 1.5.0", - "http 0.2.9", - "http 1.0.0", + "http 0.2.12", + "http 1.1.0", "pin-project-lite", "tokio 1.36.0", "tracing", @@ -1301,7 +1300,7 @@ dependencies = [ "bytes 1.5.0", "bytes-utils", "futures-core", - "http 0.2.9", + "http 0.2.12", "http-body", "itoa", "num-integer", @@ -1343,7 +1342,7 @@ dependencies = [ "aws-smithy-client", "aws-smithy-http 0.55.3", "aws-smithy-types 0.55.3", - "http 0.2.9", + "http 0.2.12", "rustc_version 0.4.0", "tracing", ] @@ -1358,7 +1357,7 @@ dependencies = [ "aws-smithy-async 1.1.7", "aws-smithy-runtime-api", "aws-smithy-types 1.1.7", - "http 0.2.9", + "http 0.2.12", "rustc_version 0.4.0", "tracing", ] @@ -1374,7 +1373,7 @@ dependencies = [ "bitflags 1.3.2", "bytes 1.5.0", "futures-util", - "http 0.2.9", + "http 0.2.12", "http-body", "hyper", "itoa", @@ -1400,7 +1399,7 @@ dependencies = [ "async-trait", "bytes 1.5.0", "futures-util", - "http 0.2.9", + "http 0.2.12", "http-body", "mime", "rustversion", @@ -1418,7 +1417,7 @@ dependencies = [ "cc", "cfg-if 1.0.0", "libc", - "miniz_oxide 0.7.1", + "miniz_oxide 0.7.2", "object", "rustc-demangle", ] @@ -1431,9 +1430,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.5" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "base64-simd" @@ -1585,7 +1584,7 @@ dependencies = [ "proc-macro-crate 2.0.0", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", "syn_derive", ] @@ -1694,9 +1693,9 @@ dependencies = [ [[package]] name = "bytestring" -version = "1.3.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "238e4886760d98c4f899360c834fa93e62cf7f721ac3c2da375cbdf4b8679aae" +checksum = "74d80203ea6b29df88012294f62733de21cfeab47f17b41af3a38bc30a03ee72" dependencies = [ "bytes 1.5.0", ] @@ -1742,7 +1741,7 @@ checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa" dependencies = [ "camino", "cargo-platform", - "semver 1.0.19", + "semver 1.0.22", "serde", "serde_json", ] @@ -1755,7 +1754,7 @@ checksum = "eee4243f1f26fc7a42710e7439c149e2b10b05472f88090acce52632f231a73a" dependencies = [ "camino", "cargo-platform", - "semver 1.0.19", + "semver 1.0.22", "serde", "serde_json", "thiserror", @@ -1769,9 +1768,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.0.83" +version = "1.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" dependencies = [ "jobserver", "libc", @@ -1907,7 +1906,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -1955,7 +1954,7 @@ dependencies = [ "fake", "futures 0.3.28", "hex", - "http 0.2.9", + "http 0.2.12", "masking", "md5", "nanoid", @@ -1979,6 +1978,7 @@ dependencies = [ "thiserror", "time", "tokio 1.36.0", + "uuid", ] [[package]] @@ -1987,7 +1987,7 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f057a694a54f12365049b0958a1685bb52d567f5593b355fbf685838e873d400" dependencies = [ - "crossbeam-utils 0.8.16", + "crossbeam-utils 0.8.19", ] [[package]] @@ -2015,7 +2015,7 @@ version = "0.1.0" dependencies = [ "anyhow", "clap", - "indexmap 2.1.0", + "indexmap 2.2.5", "serde", "serde_json", "toml 0.7.4", @@ -2089,9 +2089,9 @@ checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "cpufeatures" -version = "0.2.9" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" dependencies = [ "libc", ] @@ -2128,9 +2128,9 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.3.2" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" dependencies = [ "cfg-if 1.0.0", ] @@ -2173,12 +2173,11 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.8" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" dependencies = [ - "cfg-if 1.0.0", - "crossbeam-utils 0.8.16", + "crossbeam-utils 0.8.19", ] [[package]] @@ -2200,7 +2199,7 @@ checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" dependencies = [ "cfg-if 1.0.0", "crossbeam-epoch 0.9.15", - "crossbeam-utils 0.8.16", + "crossbeam-utils 0.8.19", ] [[package]] @@ -2226,7 +2225,7 @@ checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" dependencies = [ "autocfg", "cfg-if 1.0.0", - "crossbeam-utils 0.8.16", + "crossbeam-utils 0.8.19", "memoffset 0.9.0", "scopeguard", ] @@ -2249,7 +2248,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" dependencies = [ "cfg-if 1.0.0", - "crossbeam-utils 0.8.16", + "crossbeam-utils 0.8.19", ] [[package]] @@ -2265,12 +2264,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.16" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" -dependencies = [ - "cfg-if 1.0.0", -] +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" [[package]] name = "crypto-common" @@ -2338,7 +2334,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -2360,7 +2356,7 @@ checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" dependencies = [ "darling_core 0.20.3", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -2371,9 +2367,9 @@ checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" dependencies = [ "cfg-if 1.0.0", "hashbrown 0.14.3", - "lock_api 0.4.10", + "lock_api 0.4.11", "once_cell", - "parking_lot_core 0.9.8", + "parking_lot_core 0.9.9", ] [[package]] @@ -2442,6 +2438,16 @@ dependencies = [ "rusticata-macros", ] +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", + "serde", +] + [[package]] name = "derive_builder" version = "0.12.0" @@ -2528,7 +2534,7 @@ dependencies = [ "diesel_table_macro_syntax", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -2558,7 +2564,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc5557efc453706fed5e4fa85006fe9817c224c3f480a34c7e5959fd700921c5" dependencies = [ - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -2615,7 +2621,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -2765,7 +2771,7 @@ dependencies = [ "connector_configs", "currency_conversion", "euclid", - "getrandom 0.2.11", + "getrandom 0.2.12", "kgraph_utils", "once_cell", "ron-parser", @@ -2792,7 +2798,7 @@ dependencies = [ "aws-sdk-sesv2", "aws-sdk-sts 0.28.0", "aws-smithy-client", - "base64 0.21.5", + "base64 0.21.7", "common_utils", "dyn-clone", "error-stack", @@ -2829,7 +2835,7 @@ dependencies = [ "cookie 0.16.2", "futures-core", "futures-util", - "http 0.2.9", + "http 0.2.12", "hyper", "hyper-rustls 0.23.2", "mime", @@ -2864,12 +2870,12 @@ checksum = "8fcfdc7a0362c9f4444381a9e697c79d435fe65b52a37466fc2c1184cee9edc6" [[package]] name = "flate2" -version = "1.0.27" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6c98ee8095e9d1dcbf2fcc6d95acccb90d1c81db1e44725c6a984b1dbdfb010" +checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" dependencies = [ "crc32fast", - "miniz_oxide 0.7.1", + "miniz_oxide 0.7.2", ] [[package]] @@ -2904,9 +2910,9 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] @@ -2928,8 +2934,8 @@ dependencies = [ "parking_lot 0.12.1", "rand 0.8.5", "redis-protocol", - "semver 1.0.19", - "socket2 0.5.5", + "semver 1.0.22", + "socket2 0.5.6", "tokio 1.36.0", "tokio-stream", "tokio-util", @@ -2964,7 +2970,7 @@ checksum = "b0fa992f1656e1707946bbba340ad244f0814009ef8c0118eb7b658395f19a2e" dependencies = [ "frunk_proc_macro_helpers", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -2976,7 +2982,7 @@ dependencies = [ "frunk_core", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -2988,7 +2994,7 @@ dependencies = [ "frunk_core", "frunk_proc_macro_helpers", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -3052,9 +3058,9 @@ checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-executor" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" dependencies = [ "futures-core", "futures-task", @@ -3068,7 +3074,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a604f7a68fbf8103337523b1fadc8ade7361ee3f112f7c680ad179651616aed5" dependencies = [ "futures-core", - "lock_api 0.4.10", + "lock_api 0.4.11", "parking_lot 0.11.2", ] @@ -3101,7 +3107,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -3173,9 +3179,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -3196,9 +3202,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.28.0" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "git2" @@ -3254,8 +3260,8 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "http 0.2.9", - "indexmap 2.1.0", + "http 0.2.12", + "indexmap 2.2.5", "slab", "tokio 1.36.0", "tokio-util", @@ -3283,7 +3289,7 @@ version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" dependencies = [ - "ahash 0.8.6", + "ahash 0.8.11", "allocator-api2", ] @@ -3302,10 +3308,10 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06683b93020a07e3dbcf5f8c0f6d40080d725bea7936fc01ad345c01b97dc270" dependencies = [ - "base64 0.21.5", + "base64 0.21.7", "bytes 1.5.0", "headers-core", - "http 0.2.9", + "http 0.2.12", "httpdate", "mime", "sha1", @@ -3317,7 +3323,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" dependencies = [ - "http 0.2.9", + "http 0.2.12", ] [[package]] @@ -3331,9 +3337,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.3.3" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" +checksum = "bd5256b483761cd23699d0da46cc6fd2ee3be420bbe6d020ae4a091e70b7e9fd" [[package]] name = "hex" @@ -3361,9 +3367,9 @@ dependencies = [ [[package]] name = "http" -version = "0.2.9" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" dependencies = [ "bytes 1.5.0", "fnv", @@ -3372,9 +3378,9 @@ dependencies = [ [[package]] name = "http" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b32afd38673a8016f7c9ae69e5af41a58f81b1d31689040f2f1959594ce194ea" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" dependencies = [ "bytes 1.5.0", "fnv", @@ -3388,7 +3394,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" dependencies = [ "bytes 1.5.0", - "http 0.2.9", + "http 0.2.12", "pin-project-lite", ] @@ -3402,7 +3408,7 @@ dependencies = [ "async-channel", "base64 0.13.1", "futures-lite", - "http 0.2.9", + "http 0.2.12", "infer 0.2.3", "pin-project-lite", "rand 0.7.3", @@ -3445,7 +3451,7 @@ dependencies = [ "futures-core", "futures-util", "h2", - "http 0.2.9", + "http 0.2.12", "http-body", "httparse", "httpdate", @@ -3467,7 +3473,7 @@ dependencies = [ "bytes 1.5.0", "futures 0.3.28", "headers", - "http 0.2.9", + "http 0.2.12", "hyper", "hyper-tls", "native-tls", @@ -3482,7 +3488,7 @@ version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" dependencies = [ - "http 0.2.9", + "http 0.2.12", "hyper", "log", "rustls 0.20.9", @@ -3498,7 +3504,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", - "http 0.2.9", + "http 0.2.12", "hyper", "log", "rustls 0.21.10", @@ -3583,6 +3589,16 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "ignore" version = "0.4.20" @@ -3638,9 +3654,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.1.0" +version = "2.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4" dependencies = [ "equivalent", "hashbrown 0.14.3", @@ -3745,15 +3761,15 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "jobserver" -version = "0.1.26" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" +checksum = "ab46a6e9526ddef3ae7f787c06f0f2600639ba80ea3eade3d8e670a2230f51d6" dependencies = [ "libc", ] @@ -3765,7 +3781,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33a96c4f2128a6f44ecf7c36df2b03dddf5a07b060a4d5ebc0a81e9821f7c60e" dependencies = [ "anyhow", - "base64 0.21.5", + "base64 0.21.7", "flate2", "once_cell", "openssl", @@ -3787,9 +3803,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.64" +version = "0.3.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee" dependencies = [ "wasm-bindgen", ] @@ -3811,7 +3827,7 @@ version = "8.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6971da4d9c3aa03c3d8f3ff0f4155b534aad021292003895a469716b2a230378" dependencies = [ - "base64 0.21.5", + "base64 0.21.7", "pem", "ring 0.16.20", "serde", @@ -3857,9 +3873,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.150" +version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libgit2-sys" @@ -3915,9 +3931,9 @@ checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "local-channel" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a493488de5f18c8ffcba89eebb8532ffc562dc400490eb65b84893fae0b178" +checksum = "b6cbc85e69b8df4b8bb8b89ec634e7189099cea8927a276b7384ce5488e53ec8" dependencies = [ "futures-core", "futures-sink", @@ -3926,9 +3942,9 @@ dependencies = [ [[package]] name = "local-waker" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e34f76eb3611940e0e7d53a9aaa4e6a3151f69541a282fd0dad5571420c53ff1" +checksum = "4d873d7c67ce09b42110d801813efbc9364414e356be9935700d368351657487" [[package]] name = "lock_api" @@ -3941,9 +3957,9 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" dependencies = [ "autocfg", "scopeguard", @@ -3951,9 +3967,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.20" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "lru-cache" @@ -4058,9 +4074,9 @@ checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" [[package]] name = "memchr" -version = "2.6.4" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" [[package]] name = "memoffset" @@ -4132,9 +4148,9 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" dependencies = [ "adler", ] @@ -4160,9 +4176,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "log", @@ -4203,14 +4219,14 @@ dependencies = [ "async-trait", "crossbeam-channel", "crossbeam-epoch 0.9.15", - "crossbeam-utils 0.8.16", + "crossbeam-utils 0.8.19", "futures-util", "once_cell", "parking_lot 0.12.1", "quanta", "rustc_version 0.4.0", "skeptic", - "smallvec 1.11.1", + "smallvec 1.13.1", "tagptr", "thiserror", "triomphe", @@ -4292,6 +4308,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-integer" version = "0.1.45" @@ -4367,9 +4389,9 @@ dependencies = [ [[package]] name = "object" -version = "0.32.1" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" dependencies = [ "memchr", ] @@ -4385,9 +4407,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "oncemutex" @@ -4426,7 +4448,7 @@ dependencies = [ "aws-sigv4 1.1.7", "aws-smithy-runtime-api", "aws-types 1.1.7", - "base64 0.21.5", + "base64 0.21.7", "bytes 1.5.0", "dyn-clone", "lazy_static", @@ -4463,7 +4485,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -4503,7 +4525,7 @@ dependencies = [ "async-trait", "futures 0.3.28", "futures-util", - "http 0.2.9", + "http 0.2.12", "opentelemetry", "opentelemetry-proto", "prost", @@ -4609,7 +4631,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" dependencies = [ "instant", - "lock_api 0.4.10", + "lock_api 0.4.11", "parking_lot_core 0.8.6", ] @@ -4619,8 +4641,8 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ - "lock_api 0.4.10", - "parking_lot_core 0.9.8", + "lock_api 0.4.11", + "parking_lot_core 0.9.9", ] [[package]] @@ -4648,20 +4670,20 @@ dependencies = [ "instant", "libc", "redox_syscall 0.2.16", - "smallvec 1.11.1", + "smallvec 1.13.1", "winapi 0.3.9", ] [[package]] name = "parking_lot_core" -version = "0.9.8" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" dependencies = [ "cfg-if 1.0.0", "libc", - "redox_syscall 0.3.5", - "smallvec 1.11.1", + "redox_syscall 0.4.1", + "smallvec 1.13.1", "windows-targets 0.48.5", ] @@ -4714,9 +4736,9 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" @@ -4749,7 +4771,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -4824,22 +4846,22 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.1.3" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.3" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -4856,9 +4878,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.27" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "plotters" @@ -4898,7 +4920,7 @@ dependencies = [ "common_enums", "common_utils", "error-stack", - "http 0.2.9", + "http 0.2.12", "masking", "mime", "router_derive", @@ -4921,6 +4943,12 @@ dependencies = [ "miniz_oxide 0.3.7", ] +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -4981,9 +5009,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.76" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" dependencies = [ "unicode-ident", ] @@ -5078,7 +5106,7 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a17e662a7a8291a865152364c20c7abc5e60486ab2001e8ec10b24862de0b9ab" dependencies = [ - "crossbeam-utils 0.8.16", + "crossbeam-utils 0.8.19", "libc", "mach2", "once_cell", @@ -5199,7 +5227,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.11", + "getrandom 0.2.12", ] [[package]] @@ -5246,7 +5274,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" dependencies = [ "crossbeam-deque 0.8.3", - "crossbeam-utils 0.8.16", + "crossbeam-utils 0.8.19", ] [[package]] @@ -5332,13 +5360,22 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "redox_users" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ - "getrandom 0.2.11", + "getrandom 0.2.12", "redox_syscall 0.2.16", "thiserror", ] @@ -5351,7 +5388,7 @@ checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.5", + "regex-automata 0.4.6", "regex-syntax 0.8.2", ] @@ -5366,9 +5403,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ "aho-corasick", "memchr", @@ -5427,13 +5464,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" dependencies = [ "async-compression", - "base64 0.21.5", + "base64 0.21.7", "bytes 1.5.0", "encoding_rs", "futures-core", "futures-util", "h2", - "http 0.2.9", + "http 0.2.12", "http-body", "hyper", "hyper-rustls 0.24.2", @@ -5495,7 +5532,7 @@ checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", "cfg-if 1.0.0", - "getrandom 0.2.11", + "getrandom 0.2.12", "libc", "spin 0.9.8", "untrusted 0.9.0", @@ -5569,7 +5606,7 @@ dependencies = [ "async-bb8-diesel", "async-trait", "awc", - "base64 0.21.5", + "base64 0.21.7", "bb8", "bigdecimal", "blake3", @@ -5594,7 +5631,7 @@ dependencies = [ "external_services", "futures 0.3.28", "hex", - "http 0.2.9", + "http 0.2.12", "hyper", "hyperswitch_interfaces", "image", @@ -5660,13 +5697,13 @@ name = "router_derive" version = "0.1.0" dependencies = [ "diesel", - "indexmap 2.1.0", + "indexmap 2.2.5", "proc-macro2", "quote", "serde", "serde_json", "strum 0.24.1", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -5758,7 +5795,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.19", + "semver 1.0.22", ] [[package]] @@ -5779,7 +5816,7 @@ dependencies = [ "anyhow", "async-trait", "bytes 1.5.0", - "http 0.2.9", + "http 0.2.12", "reqwest", "rustify_derive", "serde", @@ -5859,7 +5896,7 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" dependencies = [ - "base64 0.21.5", + "base64 0.21.7", ] [[package]] @@ -5900,9 +5937,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.15" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" [[package]] name = "same-file" @@ -6019,9 +6056,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.19" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad977052201c6de01a8ef2aa3378c4bd23217a056337d1d6da40468d267a4fb0" +checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" dependencies = [ "serde", ] @@ -6034,9 +6071,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.193" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" dependencies = [ "serde_derive", ] @@ -6054,22 +6091,22 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.193" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] name = "serde_json" -version = "1.0.108" +version = "1.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" dependencies = [ - "indexmap 2.1.0", + "indexmap 2.2.5", "itoa", "ryu", "serde", @@ -6124,7 +6161,7 @@ checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -6154,11 +6191,11 @@ version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64cd236ccc1b7a29e7e2739f27c0b2dd199804abc4290e32f59f3b68d6405c23" dependencies = [ - "base64 0.21.5", + "base64 0.21.7", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.1.0", + "indexmap 2.2.5", "serde", "serde_json", "serde_with_macros", @@ -6174,7 +6211,7 @@ dependencies = [ "darling 0.20.3", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -6199,7 +6236,7 @@ checksum = "91d129178576168c589c9ec973feedf7d3126c01ac2bf08795109aa35b69fb8f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -6345,9 +6382,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.11.1" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" +checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" [[package]] name = "socket2" @@ -6361,12 +6398,12 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.5" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" dependencies = [ "libc", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -6444,7 +6481,7 @@ dependencies = [ "serde_json", "sha1", "sha2", - "smallvec 1.11.1", + "smallvec 1.13.1", "sqlformat", "sqlx-rt", "stringprep", @@ -6505,7 +6542,7 @@ dependencies = [ "dyn-clone", "error-stack", "futures 0.3.28", - "http 0.2.9", + "http 0.2.12", "masking", "mime", "moka", @@ -6587,7 +6624,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -6609,9 +6646,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.48" +version = "2.0.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" dependencies = [ "proc-macro2", "quote", @@ -6627,7 +6664,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -6735,7 +6772,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -6747,7 +6784,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", "test-case-core", ] @@ -6756,7 +6793,7 @@ name = "test_utils" version = "0.1.0" dependencies = [ "async-trait", - "base64 0.21.5", + "base64 0.21.7", "clap", "masking", "rand 0.8.5", @@ -6784,7 +6821,7 @@ dependencies = [ "cookie 0.16.2", "fantoccini", "futures 0.3.28", - "http 0.2.9", + "http 0.2.12", "log", "parking_lot 0.12.1", "serde", @@ -6812,29 +6849,29 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.49" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1177e8c6d7ede7afde3585fd2513e611227efd6481bd78d2e82ba1ce16557ed4" +checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.49" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10712f02019e9288794769fba95cd6847df9874d49d871d062172f9dd41bc4cc" +checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] name = "thread_local" -version = "1.1.7" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" dependencies = [ "cfg-if 1.0.0", "once_cell", @@ -6853,11 +6890,14 @@ dependencies = [ [[package]] name = "time" -version = "0.3.23" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59e399c068f43a5d116fedaf73b203fa4f9c519f17e2b34f63221d3792f81446" +checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" dependencies = [ + "deranged", "itoa", + "num-conv", + "powerfmt", "serde", "time-core", "time-macros", @@ -6865,16 +6905,17 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.10" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96ba15a897f3c86766b757e5ac7221554c6750054d74d5b28844fce5fb36a6c4" +checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" dependencies = [ + "num-conv", "time-core", ] @@ -6936,12 +6977,12 @@ dependencies = [ "backtrace", "bytes 1.5.0", "libc", - "mio 0.8.10", + "mio 0.8.11", "num_cpus", "parking_lot 0.12.1", "pin-project-lite", "signal-hook-registry", - "socket2 0.5.5", + "socket2 0.5.6", "tokio-macros", "windows-sys 0.48.0", ] @@ -7017,7 +7058,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -7170,9 +7211,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.9" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d68074620f57a0b21594d9735eb2e98ab38b17f80d3fcb189fca266771ca60d" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" dependencies = [ "bytes 1.5.0", "futures-core", @@ -7219,7 +7260,7 @@ version = "0.19.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" dependencies = [ - "indexmap 2.1.0", + "indexmap 2.2.5", "serde", "serde_spanned", "toml_datetime", @@ -7232,7 +7273,7 @@ version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" dependencies = [ - "indexmap 2.1.0", + "indexmap 2.2.5", "toml_datetime", "winnow", ] @@ -7251,7 +7292,7 @@ dependencies = [ "futures-core", "futures-util", "h2", - "http 0.2.9", + "http 0.2.12", "http-body", "hyper", "hyper-timeout", @@ -7315,9 +7356,9 @@ dependencies = [ [[package]] name = "tracing-actix-web" -version = "0.7.9" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fe0d5feac3f4ca21ba33496bcb1ccab58cca6412b1405ae80f0581541e0ca78" +checksum = "fa069bd1503dd526ee793bb3fce408895136c95fc86d2edb2acf1c646d7f0684" dependencies = [ "actix-web", "mutually_exclusive_features", @@ -7347,7 +7388,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -7373,12 +7414,23 @@ dependencies = [ [[package]] name = "tracing-log" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +checksum = "f751112709b4e791d8ce53e32c4ed2d353565a795ce84da2285393f41557bdf2" dependencies = [ - "lazy_static", "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", "tracing-core", ] @@ -7392,7 +7444,7 @@ dependencies = [ "opentelemetry", "tracing", "tracing-core", - "tracing-log", + "tracing-log 0.1.4", "tracing-subscriber", ] @@ -7408,9 +7460,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.17" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" dependencies = [ "matchers", "nu-ansi-term", @@ -7419,11 +7471,11 @@ dependencies = [ "serde", "serde_json", "sharded-slab", - "smallvec 1.11.1", + "smallvec 1.13.1", "thread_local", "tracing", "tracing-core", - "tracing-log", + "tracing-log 0.2.0", "tracing-serde", ] @@ -7518,9 +7570,9 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.13" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" @@ -7530,9 +7582,9 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" dependencies = [ "tinyvec", ] @@ -7575,12 +7627,12 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" dependencies = [ "form_urlencoded", - "idna", + "idna 0.5.0", "percent-encoding", "serde", ] @@ -7603,7 +7655,7 @@ version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d82b1bc5417102a73e8464c686eef947bdfb99fcdfc0a4f228e81afa9526470a" dependencies = [ - "indexmap 2.1.0", + "indexmap 2.2.5", "serde", "serde_json", "utoipa-gen", @@ -7618,18 +7670,17 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] name = "uuid" -version = "1.4.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" +checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a" dependencies = [ "atomic", - "getrandom 0.2.11", - "serde", + "getrandom 0.2.12", ] [[package]] @@ -7638,7 +7689,7 @@ version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b92f40481c04ff1f4f61f304d61793c7b56ff76ac1469f1beb199b1445b253bd" dependencies = [ - "idna", + "idna 0.4.0", "lazy_static", "regex", "serde", @@ -7662,7 +7713,7 @@ dependencies = [ "async-trait", "bytes 1.5.0", "derive_builder", - "http 0.2.9", + "http 0.2.12", "reqwest", "rustify", "rustify_derive", @@ -7758,9 +7809,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.87" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen-macro", @@ -7768,16 +7819,16 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.87" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", "wasm-bindgen-shared", ] @@ -7795,9 +7846,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.87" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -7805,22 +7856,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.87" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.87" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" +checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838" [[package]] name = "web-sys" @@ -7841,7 +7892,7 @@ dependencies = [ "base64 0.13.1", "bytes 1.5.0", "cookie 0.16.2", - "http 0.2.9", + "http 0.2.12", "log", "serde", "serde_derive", @@ -8103,7 +8154,7 @@ checksum = "bd7b0b5b253ebc0240d6aac6dd671c495c467420577bf634d3064ae7e6fa2b4c" dependencies = [ "assert-json-diff", "async-trait", - "base64 0.21.5", + "base64 0.21.7", "deadpool", "futures 0.3.28", "futures-timer", @@ -8170,22 +8221,22 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.31" +version = "0.7.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c4061bedbb353041c12f413700357bec76df2c7e2ca8e4df8bac24c6bf68e3d" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.31" +version = "0.7.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3c129550b3e6de3fd0ba67ba5c81818f9805e58b8d7fee80a3a59d2c9fc601a" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -8196,30 +8247,28 @@ checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" [[package]] name = "zstd" -version = "0.12.4" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a27595e173641171fc74a1232b7b1c7a7cb6e18222c11e9dfb9888fa424c53c" +checksum = "bffb3309596d527cfcba7dfc6ed6052f1d39dfbd7c867aa2e865e4a449c10110" dependencies = [ "zstd-safe", ] [[package]] name = "zstd-safe" -version = "6.0.6" +version = "7.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee98ffd0b48ee95e6c5168188e44a54550b1564d9d530ee21d5f0eaed1069581" +checksum = "43747c7422e2924c11144d5229878b98180ef8b06cca4ab5af37afc8a8d8ea3e" dependencies = [ - "libc", "zstd-sys", ] [[package]] name = "zstd-sys" -version = "2.0.8+zstd.1.5.5" +version = "2.0.9+zstd.1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5556e6ee25d32df2586c098bbfa278803692a20d0ab9565e049480d52707ec8c" +checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656" dependencies = [ "cc", - "libc", "pkg-config", ] diff --git a/crates/analytics/docs/clickhouse/scripts/outgoing_webhook_events.sql b/crates/analytics/docs/clickhouse/scripts/outgoing_webhook_events.sql index 3dc907629d..5d2d3fac74 100644 --- a/crates/analytics/docs/clickhouse/scripts/outgoing_webhook_events.sql +++ b/crates/analytics/docs/clickhouse/scripts/outgoing_webhook_events.sql @@ -13,7 +13,8 @@ CREATE TABLE `content` Nullable(String), `is_error` Bool, `error` Nullable(String), - `created_at_timestamp` DateTime64(3) + `created_at_timestamp` DateTime64(3), + `initial_attempt_id` Nullable(String) ) ENGINE = Kafka SETTINGS kafka_broker_list = 'kafka0:29092', kafka_topic_list = 'hyperswitch-outgoing-webhook-events', kafka_group_name = 'hyper-c1', @@ -37,6 +38,7 @@ CREATE TABLE `error` Nullable(String), `created_at_timestamp` DateTime64(3), `inserted_at` DateTime DEFAULT now() CODEC(T64, LZ4), + `initial_attempt_id` Nullable(String), INDEX eventIndex event_type TYPE bloom_filter GRANULARITY 1, INDEX webhookeventIndex outgoing_webhook_event_type TYPE bloom_filter GRANULARITY 1 ) ENGINE = MergeTree PARTITION BY toStartOfDay(created_at_timestamp) @@ -64,6 +66,7 @@ CREATE MATERIALIZED VIEW outgoing_webhook_events_mv TO outgoing_webhook_events_c `error` Nullable(String), `created_at_timestamp` DateTime64(3), `inserted_at` DateTime DEFAULT now() CODEC(T64, LZ4), + `initial_attempt_id` Nullable(String) ) AS SELECT merchant_id, @@ -80,7 +83,8 @@ SELECT is_error, error, created_at_timestamp, - now() AS inserted_at + now() AS inserted_at, + initial_attempt_id FROM outgoing_webhook_events_queue where length(_error) = 0; diff --git a/crates/common_utils/Cargo.toml b/crates/common_utils/Cargo.toml index 653795c298..6aca12cef0 100644 --- a/crates/common_utils/Cargo.toml +++ b/crates/common_utils/Cargo.toml @@ -38,6 +38,7 @@ strum = { version = "0.24.1", features = ["derive"] } thiserror = "1.0.40" time = { version = "0.3.21", features = ["serde", "serde-well-known", "std"] } tokio = { version = "1.36.0", features = ["macros", "rt-multi-thread"], optional = true } +uuid = { version = "1.7.0", features = ["v7"] } # First party crates common_enums = { version = "0.1.0", path = "../common_enums" } diff --git a/crates/common_utils/src/crypto.rs b/crates/common_utils/src/crypto.rs index c2c83a3589..2cf586dca6 100644 --- a/crates/common_utils/src/crypto.rs +++ b/crates/common_utils/src/crypto.rs @@ -493,9 +493,9 @@ pub type OptionalEncryptableName = Option>>; pub type OptionalEncryptableEmail = Option>>; /// Type alias for `Option>>` used for `phone` field pub type OptionalEncryptablePhone = Option>>; -/// Type alias for `Option>>` used for `phone` field +/// Type alias for `Option>>` pub type OptionalEncryptableValue = Option>>; -/// Type alias for `Option>` used for `phone` field +/// Type alias for `Option>` pub type OptionalSecretValue = Option>; #[cfg(test)] diff --git a/crates/common_utils/src/lib.rs b/crates/common_utils/src/lib.rs index 0ac8e886bc..3dbab96408 100644 --- a/crates/common_utils/src/lib.rs +++ b/crates/common_utils/src/lib.rs @@ -198,3 +198,9 @@ pub fn generate_id_with_default_len(prefix: &str) -> String { let len = consts::ID_LENGTH; format!("{}_{}", prefix, nanoid::nanoid!(len, &consts::ALPHABETS)) } + +/// Generate a time-ordered (time-sortable) unique identifier using the current time +#[inline] +pub fn generate_time_ordered_id(prefix: &str) -> String { + format!("{prefix}_{}", uuid::Uuid::now_v7().as_simple()) +} diff --git a/crates/common_utils/src/request.rs b/crates/common_utils/src/request.rs index f063a1940b..dfd890df79 100644 --- a/crates/common_utils/src/request.rs +++ b/crates/common_utils/src/request.rs @@ -51,6 +51,7 @@ impl std::fmt::Debug for RequestContent { Self::FormUrlEncoded(_) => "FormUrlEncodedRequestBody", Self::FormData(_) => "FormDataRequestBody", Self::Xml(_) => "XmlRequestBody", + Self::RawBytes(_) => "RawBytesRequestBody", }) } } @@ -60,6 +61,7 @@ pub enum RequestContent { FormUrlEncoded(Box), FormData(reqwest::multipart::Form), Xml(Box), + RawBytes(Vec), } impl Request { @@ -200,6 +202,7 @@ impl RequestBody { } RequestContent::Xml(i) => quick_xml::se::to_string(&i).unwrap_or_default().into(), RequestContent::FormData(_) => String::new().into(), + RequestContent::RawBytes(_) => String::new().into(), } } } diff --git a/crates/diesel_models/src/events.rs b/crates/diesel_models/src/events.rs index 817817d53d..c327cead12 100644 --- a/crates/diesel_models/src/events.rs +++ b/crates/diesel_models/src/events.rs @@ -3,7 +3,7 @@ use diesel::{AsChangeset, Identifiable, Insertable, Queryable}; use serde::{Deserialize, Serialize}; use time::PrimitiveDateTime; -use crate::{enums as storage_enums, schema::events}; +use crate::{encryption::Encryption, enums as storage_enums, schema::events}; #[derive(Clone, Debug, Insertable, router_derive::DebugAsDisplay)] #[diesel(table_name = events)] @@ -12,46 +12,43 @@ pub struct EventNew { pub event_type: storage_enums::EventType, pub event_class: storage_enums::EventClass, pub is_webhook_notified: bool, - pub intent_reference_id: Option, pub primary_object_id: String, pub primary_object_type: storage_enums::EventObjectType, -} - -#[derive(Debug)] -pub enum EventUpdate { - UpdateWebhookNotified { is_webhook_notified: Option }, + pub created_at: PrimitiveDateTime, + pub merchant_id: Option, + pub business_profile_id: Option, + pub primary_object_created_at: Option, + pub idempotent_event_id: Option, + pub initial_attempt_id: Option, + pub request: Option, + pub response: Option, } #[derive(Clone, Debug, Default, AsChangeset, router_derive::DebugAsDisplay)] #[diesel(table_name = events)] pub struct EventUpdateInternal { pub is_webhook_notified: Option, + pub response: Option, } #[derive(Clone, Debug, Deserialize, Serialize, Identifiable, Queryable)] -#[diesel(table_name = events)] +#[diesel(table_name = events, primary_key(event_id))] pub struct Event { - #[serde(skip_serializing)] - pub id: i32, pub event_id: String, pub event_type: storage_enums::EventType, pub event_class: storage_enums::EventClass, pub is_webhook_notified: bool, - pub intent_reference_id: Option, pub primary_object_id: String, pub primary_object_type: storage_enums::EventObjectType, #[serde(with = "custom_serde::iso8601")] pub created_at: PrimitiveDateTime, -} - -impl From for EventUpdateInternal { - fn from(event_update: EventUpdate) -> Self { - match event_update { - EventUpdate::UpdateWebhookNotified { - is_webhook_notified, - } => Self { - is_webhook_notified, - }, - } - } + pub merchant_id: Option, + pub business_profile_id: Option, + // This column can be used to partition the database table, so that all events related to a + // single object would reside in the same partition + pub primary_object_created_at: Option, + pub idempotent_event_id: Option, + pub initial_attempt_id: Option, + pub request: Option, + pub response: Option, } diff --git a/crates/diesel_models/src/query/events.rs b/crates/diesel_models/src/query/events.rs index 9175823e5f..7e519490dc 100644 --- a/crates/diesel_models/src/query/events.rs +++ b/crates/diesel_models/src/query/events.rs @@ -2,7 +2,7 @@ use diesel::{associations::HasTable, ExpressionMethods}; use super::generics; use crate::{ - events::{Event, EventNew, EventUpdate, EventUpdateInternal}, + events::{Event, EventNew, EventUpdateInternal}, schema::events::dsl, PgPooledConn, StorageResult, }; @@ -25,18 +25,14 @@ impl Event { pub async fn update( conn: &PgPooledConn, event_id: &str, - event: EventUpdate, + event: EventUpdateInternal, ) -> StorageResult { generics::generic_update_with_unique_predicate_get_result::< ::Table, _, _, _, - >( - conn, - dsl::event_id.eq(event_id.to_owned()), - EventUpdateInternal::from(event), - ) + >(conn, dsl::event_id.eq(event_id.to_owned()), event) .await } } diff --git a/crates/diesel_models/src/schema.rs b/crates/diesel_models/src/schema.rs index 59efc2cd32..dcae7e6d86 100644 --- a/crates/diesel_models/src/schema.rs +++ b/crates/diesel_models/src/schema.rs @@ -336,19 +336,27 @@ diesel::table! { use diesel::sql_types::*; use crate::enums::diesel_exports::*; - events (id) { - id -> Int4, + events (event_id) { #[max_length = 64] event_id -> Varchar, event_type -> EventType, event_class -> EventClass, is_webhook_notified -> Bool, #[max_length = 64] - intent_reference_id -> Nullable, - #[max_length = 64] primary_object_id -> Varchar, primary_object_type -> EventObjectType, created_at -> Timestamp, + #[max_length = 64] + merchant_id -> Nullable, + #[max_length = 64] + business_profile_id -> Nullable, + primary_object_created_at -> Nullable, + #[max_length = 64] + idempotent_event_id -> Nullable, + #[max_length = 64] + initial_attempt_id -> Nullable, + request -> Nullable, + response -> Nullable, } } diff --git a/crates/router/Cargo.toml b/crates/router/Cargo.toml index f001609ee8..82d3821a48 100644 --- a/crates/router/Cargo.toml +++ b/crates/router/Cargo.toml @@ -92,7 +92,7 @@ tokio = { version = "1.36.0", features = ["macros", "rt-multi-thread"] } unicode-segmentation = "1.10.1" url = { version = "2.4.0", features = ["serde"] } utoipa = { version = "3.3.0", features = ["preserve_order", "time"] } -uuid = { version = "1.3.3", features = ["serde", "v4"] } +uuid = { version = "1.7.0", features = ["v4"] } validator = "0.16.0" x509-parser = "0.15.0" tracing-futures = { version = "0.2.5", features = ["tokio"] } diff --git a/crates/router/src/compatibility/stripe/webhooks.rs b/crates/router/src/compatibility/stripe/webhooks.rs index 0cbd89bdd3..777c36f301 100644 --- a/crates/router/src/compatibility/stripe/webhooks.rs +++ b/crates/router/src/compatibility/stripe/webhooks.rs @@ -11,7 +11,10 @@ use super::{ payment_intents::types::StripePaymentIntentResponse, refunds::types::StripeRefundResponse, }; use crate::{ - core::{errors, webhooks::types::OutgoingWebhookType}, + core::{ + errors, + webhooks::types::{OutgoingWebhookPayloadWithSignature, OutgoingWebhookType}, + }, headers, services::request::Maskable, }; @@ -30,8 +33,8 @@ pub struct StripeOutgoingWebhook { impl OutgoingWebhookType for StripeOutgoingWebhook { fn get_outgoing_webhooks_signature( &self, - payment_response_hash_key: Option, - ) -> errors::CustomResult, errors::WebhooksFlowError> { + payment_response_hash_key: Option>, + ) -> errors::CustomResult { let timestamp = self.created; let payment_response_hash_key = payment_response_hash_key @@ -48,7 +51,7 @@ impl OutgoingWebhookType for StripeOutgoingWebhook { let v1 = hex::encode( common_utils::crypto::HmacSha256::sign_message( &common_utils::crypto::HmacSha256, - payment_response_hash_key.as_bytes(), + payment_response_hash_key.as_ref(), new_signature_payload.as_bytes(), ) .change_context(errors::WebhooksFlowError::OutgoingWebhookSigningFailed) @@ -56,7 +59,12 @@ impl OutgoingWebhookType for StripeOutgoingWebhook { ); let t = timestamp; - Ok(Some(format!("t={t},v1={v1}"))) + let signature = Some(format!("t={t},v1={v1}")); + + Ok(OutgoingWebhookPayloadWithSignature { + payload: webhook_signature_payload.into(), + signature, + }) } fn add_webhook_header(header: &mut Vec<(String, Maskable)>, signature: String) { diff --git a/crates/router/src/core/errors.rs b/crates/router/src/core/errors.rs index 609337744d..c468fea54c 100644 --- a/crates/router/src/core/errors.rs +++ b/crates/router/src/core/errors.rs @@ -276,6 +276,8 @@ pub enum WebhooksFlowError { OutgoingWebhookProcessTrackerTaskUpdateFailed, #[error("Failed to schedule retry attempt for outgoing webhook")] OutgoingWebhookRetrySchedulingFailed, + #[error("Outgoing webhook response encoding failed")] + OutgoingWebhookResponseEncodingFailed, } impl WebhooksFlowError { @@ -283,7 +285,8 @@ impl WebhooksFlowError { match self { Self::MerchantConfigNotFound | Self::MerchantWebhookDetailsNotFound - | Self::MerchantWebhookUrlNotConfigured => false, + | Self::MerchantWebhookUrlNotConfigured + | Self::OutgoingWebhookResponseEncodingFailed => false, Self::WebhookEventUpdationFailed | Self::OutgoingWebhookSigningFailed diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index b8a4c5dfc6..1642691610 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -422,7 +422,7 @@ where .into_report() .attach_printable("Frm configs label not found")?, &customer, - key_store, + key_store.clone(), )) .await?; } @@ -482,6 +482,7 @@ where crate::utils::trigger_payments_webhook( merchant_account, business_profile, + &key_store, cloned_payment_data, Some(cloned_request), cloned_customer, diff --git a/crates/router/src/core/webhooks.rs b/crates/router/src/core/webhooks.rs index 1db2fb88fa..800f7f9417 100644 --- a/crates/router/src/core/webhooks.rs +++ b/crates/router/src/core/webhooks.rs @@ -8,9 +8,11 @@ use api_models::{ payments::HeaderPayload, webhooks::{self, WebhookResponseTracker}, }; -use common_utils::{errors::ReportSwitchExt, events::ApiEventsType, request::RequestContent}; +use common_utils::{ + errors::ReportSwitchExt, events::ApiEventsType, ext_traits::Encode, request::RequestContent, +}; use error_stack::{report, IntoReport, ResultExt}; -use masking::ExposeInterface; +use masking::{ExposeInterface, Mask, PeekInterface, Secret}; use router_env::{ instrument, tracing::{self, Instrument}, @@ -39,7 +41,7 @@ use crate::{ services::{self, authentication as auth}, types::{ api::{self, mandates::MandateResponseExt}, - domain, + domain::{self, types as domain_types}, storage::{self, enums}, transformers::{ForeignInto, ForeignTryInto}, }, @@ -96,7 +98,7 @@ pub async fn payments_incoming_webhook_flow( >( state.clone(), merchant_account.clone(), - key_store, + key_store.clone(), payments::operations::PaymentStatus, api::PaymentsRetrieveRequest { resource_id: id, @@ -168,16 +170,18 @@ pub async fn payments_incoming_webhook_flow( // If event is NOT an UnsupportedEvent, trigger Outgoing Webhook if let Some(outgoing_event_type) = event_type { + let primary_object_created_at = payments_response.created; create_event_and_trigger_outgoing_webhook( state, merchant_account, business_profile, + &key_store, outgoing_event_type, enums::EventClass::Payments, - None, payment_id.clone(), enums::EventObjectType::PaymentDetails, api::OutgoingWebhookContent::PaymentDetails(payments_response), + primary_object_created_at, ) .await?; }; @@ -258,7 +262,7 @@ pub async fn refunds_incoming_webhook_flow( Box::pin(refunds::refund_retrieve_core( state.clone(), merchant_account.clone(), - key_store, + key_store.clone(), api_models::refunds::RefundsRetrieveRequest { refund_id: refund_id.to_owned(), force_sync: Some(true), @@ -278,12 +282,13 @@ pub async fn refunds_incoming_webhook_flow( state, merchant_account, business_profile, + &key_store, outgoing_event_type, enums::EventClass::Refunds, - None, refund_id, enums::EventObjectType::RefundDetails, api::OutgoingWebhookContent::RefundDetails(refund_response), + Some(updated_refund.created_at), ) .await?; } @@ -463,7 +468,7 @@ pub async fn mandates_incoming_webhook_flow( let mandates_response = Box::new( api::mandates::MandateResponse::from_db_mandate( &state, - key_store, + key_store.clone(), updated_mandate.clone(), ) .await?, @@ -474,12 +479,13 @@ pub async fn mandates_incoming_webhook_flow( state, merchant_account, business_profile, + &key_store, outgoing_event_type, enums::EventClass::Mandates, - None, updated_mandate.mandate_id.clone(), enums::EventObjectType::MandateDetails, api::OutgoingWebhookContent::MandateDetails(mandates_response), + Some(updated_mandate.created_at), ) .await?; } @@ -499,6 +505,7 @@ pub async fn disputes_incoming_webhook_flow( state: AppState, merchant_account: domain::MerchantAccount, business_profile: diesel_models::business_profile::BusinessProfile, + key_store: domain::MerchantKeyStore, webhook_details: api::IncomingWebhookDetails, source_verified: bool, connector: &(dyn api::Connector + Sync), @@ -541,12 +548,13 @@ pub async fn disputes_incoming_webhook_flow( state, merchant_account, business_profile, + &key_store, event_type, enums::EventClass::Disputes, - None, dispute_object.dispute_id.clone(), enums::EventObjectType::DisputeDetails, api::OutgoingWebhookContent::DisputeDetails(disputes_response), + Some(dispute_object.created_at), ) .await?; metrics::INCOMING_DISPUTE_WEBHOOK_MERCHANT_NOTIFIED_METRIC.add(&metrics::CONTEXT, 1, &[]); @@ -594,7 +602,7 @@ async fn bank_transfer_webhook_flow( >( state.clone(), merchant_account.to_owned(), - key_store, + key_store.clone(), payments::PaymentConfirm, request, services::api::AuthFlow::Merchant, @@ -623,16 +631,18 @@ async fn bank_transfer_webhook_flow( // If event is NOT an UnsupportedEvent, trigger Outgoing Webhook if let Some(outgoing_event_type) = event_type { + let primary_object_created_at = payments_response.created; create_event_and_trigger_outgoing_webhook( state, merchant_account, business_profile, + &key_store, outgoing_event_type, enums::EventClass::Payments, - None, payment_id.clone(), enums::EventObjectType::PaymentDetails, api::OutgoingWebhookContent::PaymentDetails(payments_response), + primary_object_created_at, ) .await?; } @@ -652,45 +662,91 @@ pub(crate) async fn create_event_and_trigger_outgoing_webhook( state: AppState, merchant_account: domain::MerchantAccount, business_profile: diesel_models::business_profile::BusinessProfile, + merchant_key_store: &domain::MerchantKeyStore, event_type: enums::EventType, event_class: enums::EventClass, - intent_reference_id: Option, primary_object_id: String, primary_object_type: enums::EventObjectType, content: api::OutgoingWebhookContent, + primary_object_created_at: Option, ) -> CustomResult<(), errors::ApiErrorResponse> { - let event_id = format!("{primary_object_id}_{event_type}"); + let delivery_attempt = types::WebhookDeliveryAttempt::InitialAttempt; + let idempotent_event_id = + utils::get_idempotent_event_id(&primary_object_id, event_type, delivery_attempt); + let webhook_url_result = get_webhook_url_from_business_profile(&business_profile); if !state.conf.webhooks.outgoing_enabled - || get_webhook_url_from_business_profile(&business_profile).is_err() + || webhook_url_result.is_err() + || webhook_url_result.as_ref().is_ok_and(String::is_empty) { logger::debug!( business_profile_id=%business_profile.profile_id, - %event_id, + %idempotent_event_id, "Outgoing webhooks are disabled in application configuration, or merchant webhook URL \ could not be obtained; skipping outgoing webhooks for event" ); return Ok(()); } + let event_id = utils::generate_event_id(); let merchant_id = business_profile.merchant_id.clone(); - let new_event = storage::EventNew { + let now = common_utils::date_time::now(); + + let outgoing_webhook = api::OutgoingWebhook { + merchant_id: merchant_id.clone(), + event_id: event_id.clone(), + event_type, + content: content.clone(), + timestamp: now, + }; + + let request_content = get_outgoing_webhook_request( + &merchant_account, + outgoing_webhook, + business_profile.payment_response_hash_key.as_deref(), + ) + .change_context(errors::ApiErrorResponse::WebhookProcessingFailure) + .attach_printable("Failed to construct outgoing webhook request content")?; + + let new_event = domain::Event { event_id: event_id.clone(), event_type, event_class, is_webhook_notified: false, - intent_reference_id, primary_object_id, primary_object_type, + created_at: now, + merchant_id: Some(business_profile.merchant_id.clone()), + business_profile_id: Some(business_profile.profile_id.clone()), + primary_object_created_at, + idempotent_event_id: Some(idempotent_event_id.clone()), + initial_attempt_id: Some(event_id.clone()), + request: Some( + domain_types::encrypt( + request_content + .encode_to_string_of_json() + .change_context(errors::ApiErrorResponse::WebhookProcessingFailure) + .attach_printable("Failed to encode outgoing webhook request content") + .map(Secret::new)?, + merchant_key_store.key.get_inner().peek(), + ) + .await + .change_context(errors::ApiErrorResponse::WebhookProcessingFailure) + .attach_printable("Failed to encrypt outgoing webhook request content")?, + ), + response: None, }; - let event_insert_result = state.store.insert_event(new_event).await; + let event_insert_result = state + .store + .insert_event(new_event, merchant_key_store) + .await; let event = match event_insert_result { Ok(event) => Ok(event), Err(error) => { if error.current_context().is_db_unique_violation() { - logger::debug!("Event `{event_id}` already exists in the database"); + logger::debug!("Event with idempotent ID `{idempotent_event_id}` already exists in the database"); return Ok(()); } else { logger::error!(event_insertion_failure=?error); @@ -701,14 +757,6 @@ pub(crate) async fn create_event_and_trigger_outgoing_webhook( } }?; - let outgoing_webhook = api::OutgoingWebhook { - merchant_id: merchant_id.clone(), - event_id: event.event_id.clone(), - event_type: event.event_type, - content: content.clone(), - timestamp: event.created_at, - }; - let process_tracker = add_outgoing_webhook_retry_task_to_process_tracker( &*state.store, &business_profile, @@ -724,19 +772,19 @@ pub(crate) async fn create_event_and_trigger_outgoing_webhook( }) .ok(); + let cloned_key_store = merchant_key_store.clone(); // Using a tokio spawn here and not arbiter because not all caller of this function // may have an actix arbiter tokio::spawn( async move { - trigger_appropriate_webhook_and_raise_event( + trigger_webhook_and_raise_event( state, - merchant_account, business_profile, - outgoing_webhook, - types::WebhookDeliveryAttempt::InitialAttempt, - content, - event.event_id, - event_type, + &cloned_key_store, + event, + request_content, + delivery_attempt, + Some(content), process_tracker, ) .await; @@ -748,83 +796,45 @@ pub(crate) async fn create_event_and_trigger_outgoing_webhook( } #[allow(clippy::too_many_arguments)] -pub(crate) async fn trigger_appropriate_webhook_and_raise_event( +#[instrument(skip_all)] +pub(crate) async fn trigger_webhook_and_raise_event( state: AppState, - merchant_account: domain::MerchantAccount, business_profile: diesel_models::business_profile::BusinessProfile, - outgoing_webhook: api::OutgoingWebhook, + merchant_key_store: &domain::MerchantKeyStore, + event: domain::Event, + request_content: types::OutgoingWebhookRequestContent, delivery_attempt: types::WebhookDeliveryAttempt, - content: api::OutgoingWebhookContent, - event_id: String, - event_type: enums::EventType, + content: Option, process_tracker: Option, ) { - match merchant_account.get_compatible_connector() { - #[cfg(feature = "stripe")] - Some(api_models::enums::Connector::Stripe) => { - trigger_webhook_and_raise_event::( - state, - business_profile, - outgoing_webhook, - delivery_attempt, - content, - event_id, - event_type, - process_tracker, - ) - .await - } - _ => { - trigger_webhook_and_raise_event::( - state, - business_profile, - outgoing_webhook, - delivery_attempt, - content, - event_id, - event_type, - process_tracker, - ) - .await - } - } -} + logger::debug!( + event_id=%event.event_id, + idempotent_event_id=?event.idempotent_event_id, + initial_attempt_id=?event.initial_attempt_id, + "Attempting to send webhook" + ); -#[allow(clippy::too_many_arguments)] -async fn trigger_webhook_and_raise_event( - state: AppState, - business_profile: diesel_models::business_profile::BusinessProfile, - outgoing_webhook: api::OutgoingWebhook, - delivery_attempt: types::WebhookDeliveryAttempt, - content: api::OutgoingWebhookContent, - event_id: String, - event_type: enums::EventType, - process_tracker: Option, -) { let merchant_id = business_profile.merchant_id.clone(); - let trigger_webhook_result = trigger_webhook_to_merchant::( + let trigger_webhook_result = trigger_webhook_to_merchant( state.clone(), business_profile, - outgoing_webhook, + merchant_key_store, + event.clone(), + request_content, delivery_attempt, process_tracker, ) .await; - raise_webhooks_analytics_event( - state, - trigger_webhook_result, - content, - &merchant_id, - &event_id, - event_type, - ); + raise_webhooks_analytics_event(state, trigger_webhook_result, content, merchant_id, event); } -async fn trigger_webhook_to_merchant( +async fn trigger_webhook_to_merchant( state: AppState, business_profile: diesel_models::business_profile::BusinessProfile, - webhook: api::OutgoingWebhook, + merchant_key_store: &domain::MerchantKeyStore, + event: domain::Event, + request_content: types::OutgoingWebhookRequestContent, delivery_attempt: types::WebhookDeliveryAttempt, process_tracker: Option, ) -> CustomResult<(), errors::WebhooksFlowError> { @@ -853,28 +863,21 @@ async fn trigger_webhook_to_merchant( (Err(error), None) => Err(error), }?; - let outgoing_webhook_event_id = webhook.event_id.clone(); - - let transformed_outgoing_webhook = W::from(webhook); - - let outgoing_webhooks_signature = transformed_outgoing_webhook - .get_outgoing_webhooks_signature(business_profile.payment_response_hash_key.clone())?; - - let mut header = vec![( - reqwest::header::CONTENT_TYPE.to_string(), - mime::APPLICATION_JSON.essence_str().into(), - )]; - - if let Some(signature) = outgoing_webhooks_signature { - W::add_webhook_header(&mut header, signature) - } + let event_id = event.event_id; + let headers = request_content + .headers + .into_iter() + .map(|(name, value)| (name, value.into_masked())) + .collect(); let request = services::RequestBuilder::new() .method(services::Method::Post) .url(&webhook_url) .attach_default_headers() - .headers(header) - .set_body(RequestContent::Json(Box::new(transformed_outgoing_webhook))) + .headers(headers) + .set_body(RequestContent::RawBytes( + request_content.payload.expose().into_bytes(), + )) .build(); let response = state @@ -903,10 +906,72 @@ async fn trigger_webhook_to_merchant( "An error occurred when sending webhook to merchant" ); }; + let update_event_in_storage = |state: AppState, + merchant_key_store: domain::MerchantKeyStore, + event_id: String, + response: reqwest::Response| async move { + let status_code = response.status(); + let is_webhook_notified = status_code.is_success(); + + let response_headers = response + .headers() + .iter() + .map(|(name, value)| { + ( + name.as_str().to_owned(), + value + .to_str() + .map(|s| Secret::from(String::from(s))) + .unwrap_or_else(|error| { + logger::warn!( + "Response header {} contains non-UTF-8 characters: {error:?}", + name.as_str() + ); + Secret::from(String::from("Non-UTF-8 header value")) + }), + ) + }) + .collect::>(); + let response_payload = response + .text() + .await + .map(Secret::from) + .unwrap_or_else(|error| { + logger::warn!("Response contains non-UTF-8 characters: {error:?}"); + Secret::from(String::from("Non-UTF-8 response body")) + }); + let response_to_store = types::OutgoingWebhookResponseContent { + payload: response_payload, + headers: response_headers, + status_code: status_code.as_u16(), + }; + + let event_update = domain::EventUpdate::UpdateResponse { + is_webhook_notified, + response: Some( + domain_types::encrypt( + response_to_store + .encode_to_string_of_json() + .change_context( + errors::WebhooksFlowError::OutgoingWebhookResponseEncodingFailed, + ) + .map(Secret::new)?, + merchant_key_store.key.get_inner().peek(), + ) + .await + .change_context(errors::WebhooksFlowError::WebhookEventUpdationFailed) + .attach_printable("Failed to encrypt outgoing webhook request content")?, + ), + }; + state + .store + .update_event(event_id, event_update, &merchant_key_store) + .await + .change_context(errors::WebhooksFlowError::WebhookEventUpdationFailed) + }; let success_response_handler = |state: AppState, merchant_id: String, - outgoing_webhook_event_id: String, process_tracker: Option, business_status: &'static str| async move { metrics::WEBHOOK_OUTGOING_RECEIVED_COUNT.add( @@ -915,15 +980,6 @@ async fn trigger_webhook_to_merchant( &[metrics::KeyValue::new(MERCHANT_ID, merchant_id)], ); - let update_event = storage::EventUpdate::UpdateWebhookNotified { - is_webhook_notified: Some(true), - }; - state - .store - .update_event(outgoing_webhook_event_id, update_event) - .await - .change_context(errors::WebhooksFlowError::WebhookEventUpdationFailed)?; - match process_tracker { Some(process_tracker) => state .store @@ -954,11 +1010,19 @@ async fn trigger_webhook_to_merchant( types::WebhookDeliveryAttempt::InitialAttempt => match response { Err(client_error) => api_client_error_handler(client_error, delivery_attempt), Ok(response) => { - if response.status().is_success() { + let status_code = response.status(); + let _updated_event = update_event_in_storage( + state.clone(), + merchant_key_store.clone(), + event_id.clone(), + response, + ) + .await?; + + if status_code.is_success() { success_response_handler( state.clone(), business_profile.merchant_id, - outgoing_webhook_event_id, process_tracker, "INITIAL_DELIVERY_ATTEMPT_SUCCESSFUL", ) @@ -967,7 +1031,7 @@ async fn trigger_webhook_to_merchant( error_response_handler( business_profile.merchant_id, delivery_attempt, - response.status().as_u16(), + status_code.as_u16(), "Ignoring error when sending webhook to merchant", ); } @@ -993,11 +1057,19 @@ async fn trigger_webhook_to_merchant( )?; } Ok(response) => { - if response.status().is_success() { + let status_code = response.status(); + let _updated_event = update_event_in_storage( + state.clone(), + merchant_key_store.clone(), + event_id.clone(), + response, + ) + .await?; + + if status_code.is_success() { success_response_handler( state.clone(), business_profile.merchant_id, - outgoing_webhook_event_id, Some(process_tracker), "COMPLETED_BY_PT", ) @@ -1006,7 +1078,7 @@ async fn trigger_webhook_to_merchant( error_response_handler( business_profile.merchant_id.clone(), delivery_attempt, - response.status().as_u16(), + status_code.as_u16(), "An error occurred when sending webhook to merchant", ); // Schedule a retry attempt for webhook delivery @@ -1031,10 +1103,9 @@ async fn trigger_webhook_to_merchant( fn raise_webhooks_analytics_event( state: AppState, trigger_webhook_result: CustomResult<(), errors::WebhooksFlowError>, - content: api::OutgoingWebhookContent, - merchant_id: &str, - event_id: &str, - event_type: enums::EventType, + content: Option, + merchant_id: String, + event: domain::Event, ) { let error = if let Err(error) = trigger_webhook_result { logger::error!(?error, "Failed to send webhook to merchant"); @@ -1051,13 +1122,16 @@ fn raise_webhooks_analytics_event( None }; - let outgoing_webhook_event_content = content.get_outgoing_webhook_event_content(); + let outgoing_webhook_event_content = content + .as_ref() + .and_then(api::OutgoingWebhookContent::get_outgoing_webhook_event_content); let webhook_event = OutgoingWebhookEvent::new( - merchant_id.to_owned(), - event_id.to_owned(), - event_type, + merchant_id, + event.event_id, + event.event_type, outgoing_webhook_event_content, error, + event.initial_attempt_id, ); match RawEvent::try_from(webhook_event.clone()) { @@ -1410,6 +1484,7 @@ pub async fn webhooks_core CustomResult { let schedule_time = outgoing_webhook_retry::get_webhook_delivery_retry_schedule_time( db, @@ -1591,6 +1666,7 @@ pub async fn add_outgoing_webhook_retry_task_to_process_tracker( event_class: event.event_class, primary_object_id: event.primary_object_id.clone(), primary_object_type: event.primary_object_type, + initial_attempt_id: event.initial_attempt_id.clone(), }; let runner = storage::ProcessTrackerRunner::OutgoingWebhookRetryWorkflow; @@ -1652,3 +1728,50 @@ fn get_webhook_url_from_business_profile( .change_context(errors::WebhooksFlowError::MerchantWebhookUrlNotConfigured) .map(ExposeInterface::expose) } + +pub(crate) fn get_outgoing_webhook_request( + merchant_account: &domain::MerchantAccount, + outgoing_webhook: api::OutgoingWebhook, + payment_response_hash_key: Option<&str>, +) -> CustomResult { + #[inline] + fn get_outgoing_webhook_request_inner( + outgoing_webhook: api::OutgoingWebhook, + payment_response_hash_key: Option<&str>, + ) -> CustomResult { + let mut headers = vec![( + reqwest::header::CONTENT_TYPE.to_string(), + mime::APPLICATION_JSON.essence_str().into(), + )]; + + let transformed_outgoing_webhook = WebhookType::from(outgoing_webhook); + + let outgoing_webhooks_signature = transformed_outgoing_webhook + .get_outgoing_webhooks_signature(payment_response_hash_key)?; + + if let Some(signature) = outgoing_webhooks_signature.signature { + WebhookType::add_webhook_header(&mut headers, signature) + } + + Ok(types::OutgoingWebhookRequestContent { + payload: outgoing_webhooks_signature.payload, + headers: headers + .into_iter() + .map(|(name, value)| (name, Secret::new(value.into_inner()))) + .collect(), + }) + } + + match merchant_account.get_compatible_connector() { + #[cfg(feature = "stripe")] + Some(api_models::enums::Connector::Stripe) => get_outgoing_webhook_request_inner::< + stripe_webhooks::StripeOutgoingWebhook, + >( + outgoing_webhook, payment_response_hash_key + ), + _ => get_outgoing_webhook_request_inner::( + outgoing_webhook, + payment_response_hash_key, + ), + } +} diff --git a/crates/router/src/core/webhooks/types.rs b/crates/router/src/core/webhooks/types.rs index a6bc7fbc23..d376b2fbdf 100644 --- a/crates/router/src/core/webhooks/types.rs +++ b/crates/router/src/core/webhooks/types.rs @@ -1,17 +1,23 @@ use api_models::webhooks; use common_utils::{crypto::SignMessage, ext_traits::Encode}; use error_stack::ResultExt; +use masking::Secret; use serde::Serialize; use crate::{core::errors, headers, services::request::Maskable, types::storage::enums}; +pub struct OutgoingWebhookPayloadWithSignature { + pub payload: Secret, + pub signature: Option, +} + pub trait OutgoingWebhookType: Serialize + From + Sync + Send + std::fmt::Debug + 'static { fn get_outgoing_webhooks_signature( &self, - payment_response_hash_key: Option, - ) -> errors::CustomResult, errors::WebhooksFlowError>; + payment_response_hash_key: Option>, + ) -> errors::CustomResult; fn add_webhook_header(header: &mut Vec<(String, Maskable)>, signature: String); } @@ -19,26 +25,32 @@ pub trait OutgoingWebhookType: impl OutgoingWebhookType for webhooks::OutgoingWebhook { fn get_outgoing_webhooks_signature( &self, - payment_response_hash_key: Option, - ) -> errors::CustomResult, errors::WebhooksFlowError> { + payment_response_hash_key: Option>, + ) -> errors::CustomResult { let webhook_signature_payload = self .encode_to_string_of_json() .change_context(errors::WebhooksFlowError::OutgoingWebhookEncodingFailed) .attach_printable("failed encoding outgoing webhook payload")?; - Ok(payment_response_hash_key + let signature = payment_response_hash_key .map(|key| { common_utils::crypto::HmacSha512::sign_message( &common_utils::crypto::HmacSha512, - key.as_bytes(), + key.as_ref(), webhook_signature_payload.as_bytes(), ) }) .transpose() .change_context(errors::WebhooksFlowError::OutgoingWebhookSigningFailed) .attach_printable("Failed to sign the message")? - .map(hex::encode)) + .map(hex::encode); + + Ok(OutgoingWebhookPayloadWithSignature { + payload: webhook_signature_payload.into(), + signature, + }) } + fn add_webhook_header(header: &mut Vec<(String, Maskable)>, signature: String) { header.push((headers::X_WEBHOOK_SIGNATURE.to_string(), signature.into())) } @@ -58,4 +70,18 @@ pub(crate) struct OutgoingWebhookTrackingData { pub(crate) event_class: enums::EventClass, pub(crate) primary_object_id: String, pub(crate) primary_object_type: enums::EventObjectType, + pub(crate) initial_attempt_id: Option, +} + +#[derive(Debug, serde::Serialize, serde::Deserialize)] +pub(crate) struct OutgoingWebhookRequestContent { + pub(crate) payload: Secret, + pub(crate) headers: Vec<(String, Secret)>, +} + +#[derive(Debug, serde::Serialize, serde::Deserialize)] +pub(crate) struct OutgoingWebhookResponseContent { + pub(crate) payload: Secret, + pub(crate) headers: Vec<(String, Secret)>, + pub(crate) status_code: u16, } diff --git a/crates/router/src/core/webhooks/utils.rs b/crates/router/src/core/webhooks/utils.rs index 86fd1d3117..7bc34a25ac 100644 --- a/crates/router/src/core/webhooks/utils.rs +++ b/crates/router/src/core/webhooks/utils.rs @@ -120,3 +120,27 @@ pub async fn construct_webhook_router_data<'a>( }; Ok(router_data) } + +#[inline] +pub(crate) fn get_idempotent_event_id( + primary_object_id: &str, + event_type: crate::types::storage::enums::EventType, + delivery_attempt: super::types::WebhookDeliveryAttempt, +) -> String { + use super::types::WebhookDeliveryAttempt; + + const EVENT_ID_SUFFIX_LENGTH: usize = 8; + + let common_prefix = format!("{primary_object_id}_{event_type}"); + match delivery_attempt { + WebhookDeliveryAttempt::InitialAttempt => common_prefix, + WebhookDeliveryAttempt::AutomaticRetry => { + common_utils::generate_id(EVENT_ID_SUFFIX_LENGTH, &common_prefix) + } + } +} + +#[inline] +pub(crate) fn generate_event_id() -> String { + common_utils::generate_time_ordered_id("evt") +} diff --git a/crates/router/src/db/events.rs b/crates/router/src/db/events.rs index 28fe4ef32d..99c11b63bd 100644 --- a/crates/router/src/db/events.rs +++ b/crates/router/src/db/events.rs @@ -1,3 +1,4 @@ +use common_utils::ext_traits::AsyncExt; use error_stack::{IntoReport, ResultExt}; use router_env::{instrument, tracing}; @@ -5,26 +6,39 @@ use super::{MockDb, Store}; use crate::{ connection, core::errors::{self, CustomResult}, - types::storage, + types::{ + domain::{ + self, + behaviour::{Conversion, ReverseConversion}, + }, + storage, + }, }; #[async_trait::async_trait] -pub trait EventInterface { +pub trait EventInterface +where + domain::Event: + Conversion, +{ async fn insert_event( &self, - event: storage::EventNew, - ) -> CustomResult; + event: domain::Event, + merchant_key_store: &domain::MerchantKeyStore, + ) -> CustomResult; async fn find_event_by_event_id( &self, event_id: &str, - ) -> CustomResult; + merchant_key_store: &domain::MerchantKeyStore, + ) -> CustomResult; async fn update_event( &self, event_id: String, - event: storage::EventUpdate, - ) -> CustomResult; + event: domain::EventUpdate, + merchant_key_store: &domain::MerchantKeyStore, + ) -> CustomResult; } #[async_trait::async_trait] @@ -32,35 +46,54 @@ impl EventInterface for Store { #[instrument(skip_all)] async fn insert_event( &self, - event: storage::EventNew, - ) -> CustomResult { + event: domain::Event, + merchant_key_store: &domain::MerchantKeyStore, + ) -> CustomResult { let conn = connection::pg_connection_write(self).await?; - event.insert(&conn).await.map_err(Into::into).into_report() + event + .construct_new() + .await + .change_context(errors::StorageError::EncryptionError)? + .insert(&conn) + .await + .map_err(Into::into) + .into_report()? + .convert(merchant_key_store.key.get_inner()) + .await + .change_context(errors::StorageError::DecryptionError) } #[instrument(skip_all)] async fn find_event_by_event_id( &self, event_id: &str, - ) -> CustomResult { + merchant_key_store: &domain::MerchantKeyStore, + ) -> CustomResult { let conn = connection::pg_connection_read(self).await?; storage::Event::find_by_event_id(&conn, event_id) .await .map_err(Into::into) - .into_report() + .into_report()? + .convert(merchant_key_store.key.get_inner()) + .await + .change_context(errors::StorageError::DecryptionError) } #[instrument(skip_all)] async fn update_event( &self, event_id: String, - event: storage::EventUpdate, - ) -> CustomResult { + event: domain::EventUpdate, + merchant_key_store: &domain::MerchantKeyStore, + ) -> CustomResult { let conn = connection::pg_connection_write(self).await?; - storage::Event::update(&conn, &event_id, event) + storage::Event::update(&conn, &event_id, event.into()) .await .map_err(Into::into) - .into_report() + .into_report()? + .convert(merchant_key_store.key.get_inner()) + .await + .change_context(errors::StorageError::DecryptionError) } } @@ -68,41 +101,41 @@ impl EventInterface for Store { impl EventInterface for MockDb { async fn insert_event( &self, - event: storage::EventNew, - ) -> CustomResult { + event: domain::Event, + merchant_key_store: &domain::MerchantKeyStore, + ) -> CustomResult { let mut locked_events = self.events.lock().await; - let now = common_utils::date_time::now(); - let stored_event = storage::Event { - id: locked_events - .len() - .try_into() - .into_report() - .change_context(errors::StorageError::MockDbError)?, - event_id: event.event_id, - event_type: event.event_type, - event_class: event.event_class, - is_webhook_notified: event.is_webhook_notified, - intent_reference_id: event.intent_reference_id, - primary_object_id: event.primary_object_id, - primary_object_type: event.primary_object_type, - created_at: now, - }; + let stored_event = Conversion::convert(event) + .await + .change_context(errors::StorageError::EncryptionError)?; locked_events.push(stored_event.clone()); - Ok(stored_event) + stored_event + .convert(merchant_key_store.key.get_inner()) + .await + .change_context(errors::StorageError::DecryptionError) } async fn find_event_by_event_id( &self, event_id: &str, - ) -> CustomResult { + merchant_key_store: &domain::MerchantKeyStore, + ) -> CustomResult { let locked_events = self.events.lock().await; locked_events .iter() .find(|event| event.event_id == event_id) .cloned() + .async_map(|event| async { + event + .convert(merchant_key_store.key.get_inner()) + .await + .change_context(errors::StorageError::DecryptionError) + }) + .await + .transpose()? .ok_or( errors::StorageError::ValueNotFound(format!( "No event available with event_id = {event_id}" @@ -114,8 +147,9 @@ impl EventInterface for MockDb { async fn update_event( &self, event_id: String, - event: storage::EventUpdate, - ) -> CustomResult { + event: domain::EventUpdate, + merchant_key_store: &domain::MerchantKeyStore, + ) -> CustomResult { let mut locked_events = self.events.lock().await; let event_to_update = locked_events .iter_mut() @@ -123,26 +157,35 @@ impl EventInterface for MockDb { .ok_or(errors::StorageError::MockDbError)?; match event { - storage::EventUpdate::UpdateWebhookNotified { + domain::EventUpdate::UpdateResponse { is_webhook_notified, + response, } => { - if let Some(is_webhook_notified) = is_webhook_notified { - event_to_update.is_webhook_notified = is_webhook_notified; - } + event_to_update.is_webhook_notified = is_webhook_notified; + event_to_update.response = response.map(Into::into); } } - Ok(event_to_update.clone()) + event_to_update + .clone() + .convert(merchant_key_store.key.get_inner()) + .await + .change_context(errors::StorageError::DecryptionError) } } #[cfg(test)] mod tests { use diesel_models::enums; + use time::macros::datetime; use crate::{ - db::{events::EventInterface, MockDb}, - types::storage, + db::{ + events::EventInterface, merchant_key_store::MerchantKeyStoreInterface, + MasterKeyInterface, MockDb, + }, + services, + types::domain, }; #[allow(clippy::unwrap_used)] @@ -152,34 +195,71 @@ mod tests { let mockdb = MockDb::new(&redis_interface::RedisSettings::default()) .await .expect("Failed to create Mock store"); + let event_id = "test_event_id"; + let merchant_id = "merchant1"; + let business_profile_id = "profile1"; - let event1 = mockdb - .insert_event(storage::EventNew { - event_id: "test_event_id".into(), - event_type: enums::EventType::PaymentSucceeded, - event_class: enums::EventClass::Payments, - is_webhook_notified: false, - intent_reference_id: Some("test".into()), - primary_object_id: "primary_object_tet".into(), - primary_object_type: enums::EventObjectType::PaymentDetails, - }) + let master_key = mockdb.get_master_key(); + mockdb + .insert_merchant_key_store( + domain::MerchantKeyStore { + merchant_id: merchant_id.into(), + key: domain::types::encrypt( + services::generate_aes256_key().unwrap().to_vec().into(), + master_key, + ) + .await + .unwrap(), + created_at: datetime!(2023-02-01 0:00), + }, + &master_key.to_vec().into(), + ) + .await + .unwrap(); + let merchant_key_store = mockdb + .get_merchant_key_store_by_merchant_id(merchant_id, &master_key.to_vec().into()) .await .unwrap(); - assert_eq!(event1.id, 0); + let event1 = mockdb + .insert_event( + domain::Event { + event_id: event_id.into(), + event_type: enums::EventType::PaymentSucceeded, + event_class: enums::EventClass::Payments, + is_webhook_notified: false, + primary_object_id: "primary_object_tet".into(), + primary_object_type: enums::EventObjectType::PaymentDetails, + created_at: common_utils::date_time::now(), + merchant_id: Some(merchant_id.to_owned()), + business_profile_id: Some(business_profile_id.to_owned()), + primary_object_created_at: Some(common_utils::date_time::now()), + idempotent_event_id: Some(event_id.into()), + initial_attempt_id: Some(event_id.into()), + request: None, + response: None, + }, + &merchant_key_store, + ) + .await + .unwrap(); + + assert_eq!(event1.event_id, event_id); let updated_event = mockdb .update_event( - "test_event_id".into(), - storage::EventUpdate::UpdateWebhookNotified { - is_webhook_notified: Some(true), + event_id.into(), + domain::EventUpdate::UpdateResponse { + is_webhook_notified: true, + response: None, }, + &merchant_key_store, ) .await .unwrap(); assert!(updated_event.is_webhook_notified); assert_eq!(updated_event.primary_object_id, "primary_object_tet"); - assert_eq!(updated_event.id, 0); + assert_eq!(updated_event.event_id, event_id); } } diff --git a/crates/router/src/db/kafka_store.rs b/crates/router/src/db/kafka_store.rs index ff03632009..c2159d5d8a 100644 --- a/crates/router/src/db/kafka_store.rs +++ b/crates/router/src/db/kafka_store.rs @@ -483,24 +483,33 @@ impl EphemeralKeyInterface for KafkaStore { impl EventInterface for KafkaStore { async fn insert_event( &self, - event: storage::EventNew, - ) -> CustomResult { - self.diesel_store.insert_event(event).await + event: domain::Event, + merchant_key_store: &domain::MerchantKeyStore, + ) -> CustomResult { + self.diesel_store + .insert_event(event, merchant_key_store) + .await } async fn find_event_by_event_id( &self, event_id: &str, - ) -> CustomResult { - self.diesel_store.find_event_by_event_id(event_id).await + merchant_key_store: &domain::MerchantKeyStore, + ) -> CustomResult { + self.diesel_store + .find_event_by_event_id(event_id, merchant_key_store) + .await } async fn update_event( &self, event_id: String, - event: storage::EventUpdate, - ) -> CustomResult { - self.diesel_store.update_event(event_id, event).await + event: domain::EventUpdate, + merchant_key_store: &domain::MerchantKeyStore, + ) -> CustomResult { + self.diesel_store + .update_event(event_id, event, merchant_key_store) + .await } } diff --git a/crates/router/src/events/outgoing_webhook_logs.rs b/crates/router/src/events/outgoing_webhook_logs.rs index bc5907b0cc..873298e493 100644 --- a/crates/router/src/events/outgoing_webhook_logs.rs +++ b/crates/router/src/events/outgoing_webhook_logs.rs @@ -16,6 +16,7 @@ pub struct OutgoingWebhookEvent { is_error: bool, error: Option, created_at_timestamp: i128, + initial_attempt_id: Option, } #[derive(Clone, Debug, PartialEq, Serialize)] @@ -83,6 +84,7 @@ impl OutgoingWebhookEvent { event_type: OutgoingWebhookEventType, content: Option, error: Option, + initial_attempt_id: Option, ) -> Self { Self { merchant_id, @@ -92,6 +94,7 @@ impl OutgoingWebhookEvent { is_error: error.is_some(), error, created_at_timestamp: OffsetDateTime::now_utc().unix_timestamp_nanos() / 1_000_000, + initial_attempt_id, } } } diff --git a/crates/router/src/services/api.rs b/crates/router/src/services/api.rs index 3b987452c9..26c638a5e8 100644 --- a/crates/router/src/services/api.rs +++ b/crates/router/src/services/api.rs @@ -374,6 +374,7 @@ where .masked_serialize() .unwrap_or(json!({ "error": "failed to mask serialize"})), RequestContent::FormData(_) => json!({"request_type": "FORM_DATA"}), + RequestContent::RawBytes(_) => json!({"request_type": "RAW_BYTES"}), }, None => serde_json::Value::Null, }; @@ -643,6 +644,7 @@ pub async fn send_request( .change_context(errors::ApiClientError::BodySerializationFailed)?; client.body(body).header("Content-Type", "application/xml") } + Some(RequestContent::RawBytes(payload)) => client.body(payload), None => client, } } @@ -658,6 +660,7 @@ pub async fn send_request( .change_context(errors::ApiClientError::BodySerializationFailed)?; client.body(body).header("Content-Type", "application/xml") } + Some(RequestContent::RawBytes(payload)) => client.body(payload), None => client, } } @@ -673,6 +676,7 @@ pub async fn send_request( .change_context(errors::ApiClientError::BodySerializationFailed)?; client.body(body).header("Content-Type", "application/xml") } + Some(RequestContent::RawBytes(payload)) => client.body(payload), None => client, } } diff --git a/crates/router/src/types/domain.rs b/crates/router/src/types/domain.rs index c93f96eaf0..3b6633defc 100644 --- a/crates/router/src/types/domain.rs +++ b/crates/router/src/types/domain.rs @@ -1,6 +1,7 @@ mod address; pub mod behaviour; mod customer; +mod event; mod merchant_account; mod merchant_connector_account; mod merchant_key_store; @@ -10,6 +11,7 @@ pub mod user; pub use address::*; pub use customer::*; +pub use event::*; pub use merchant_account::*; pub use merchant_connector_account::*; pub use merchant_key_store::*; diff --git a/crates/router/src/types/domain/event.rs b/crates/router/src/types/domain/event.rs new file mode 100644 index 0000000000..4d86a908e6 --- /dev/null +++ b/crates/router/src/types/domain/event.rs @@ -0,0 +1,133 @@ +use common_utils::crypto::OptionalEncryptableSecretString; +use diesel_models::{ + enums::{EventClass, EventObjectType, EventType}, + events::EventUpdateInternal, +}; +use error_stack::ResultExt; +use masking::{PeekInterface, Secret}; + +use crate::{ + errors::{CustomResult, ValidationError}, + types::domain::types::{self, AsyncLift}, +}; + +#[derive(Clone, Debug)] +pub struct Event { + pub event_id: String, + pub event_type: EventType, + pub event_class: EventClass, + pub is_webhook_notified: bool, + pub primary_object_id: String, + pub primary_object_type: EventObjectType, + pub created_at: time::PrimitiveDateTime, + pub merchant_id: Option, + pub business_profile_id: Option, + pub primary_object_created_at: Option, + pub idempotent_event_id: Option, + pub initial_attempt_id: Option, + pub request: OptionalEncryptableSecretString, + pub response: OptionalEncryptableSecretString, +} + +#[derive(Debug)] +pub enum EventUpdate { + UpdateResponse { + is_webhook_notified: bool, + response: OptionalEncryptableSecretString, + }, +} + +impl From for EventUpdateInternal { + fn from(event_update: EventUpdate) -> Self { + match event_update { + EventUpdate::UpdateResponse { + is_webhook_notified, + response, + } => Self { + is_webhook_notified: Some(is_webhook_notified), + response: response.map(Into::into), + }, + } + } +} + +#[async_trait::async_trait] +impl super::behaviour::Conversion for Event { + type DstType = diesel_models::events::Event; + type NewDstType = diesel_models::events::EventNew; + + async fn convert(self) -> CustomResult { + Ok(diesel_models::events::Event { + event_id: self.event_id, + event_type: self.event_type, + event_class: self.event_class, + is_webhook_notified: self.is_webhook_notified, + primary_object_id: self.primary_object_id, + primary_object_type: self.primary_object_type, + created_at: self.created_at, + merchant_id: self.merchant_id, + business_profile_id: self.business_profile_id, + primary_object_created_at: self.primary_object_created_at, + idempotent_event_id: self.idempotent_event_id, + initial_attempt_id: self.initial_attempt_id, + request: self.request.map(Into::into), + response: self.response.map(Into::into), + }) + } + + async fn convert_back( + item: Self::DstType, + key: &Secret>, + ) -> CustomResult + where + Self: Sized, + { + async { + Ok(Self { + event_id: item.event_id, + event_type: item.event_type, + event_class: item.event_class, + is_webhook_notified: item.is_webhook_notified, + primary_object_id: item.primary_object_id, + primary_object_type: item.primary_object_type, + created_at: item.created_at, + merchant_id: item.merchant_id, + business_profile_id: item.business_profile_id, + primary_object_created_at: item.primary_object_created_at, + idempotent_event_id: item.idempotent_event_id, + initial_attempt_id: item.initial_attempt_id, + request: item + .request + .async_lift(|inner| types::decrypt(inner, key.peek())) + .await?, + response: item + .response + .async_lift(|inner| types::decrypt(inner, key.peek())) + .await?, + }) + } + .await + .change_context(ValidationError::InvalidValue { + message: "Failed while decrypting event data".to_string(), + }) + } + + async fn construct_new(self) -> CustomResult { + Ok(diesel_models::events::EventNew { + event_id: self.event_id, + event_type: self.event_type, + event_class: self.event_class, + is_webhook_notified: self.is_webhook_notified, + primary_object_id: self.primary_object_id, + primary_object_type: self.primary_object_type, + created_at: self.created_at, + merchant_id: self.merchant_id, + business_profile_id: self.business_profile_id, + primary_object_created_at: self.primary_object_created_at, + idempotent_event_id: self.idempotent_event_id, + initial_attempt_id: self.initial_attempt_id, + request: self.request.map(Into::into), + response: self.response.map(Into::into), + }) + } +} diff --git a/crates/router/src/types/storage/events.rs b/crates/router/src/types/storage/events.rs index d3a85b96b0..9b459cde41 100644 --- a/crates/router/src/types/storage/events.rs +++ b/crates/router/src/types/storage/events.rs @@ -1 +1 @@ -pub use diesel_models::events::{Event, EventNew, EventUpdate}; +pub use diesel_models::events::{Event, EventNew}; diff --git a/crates/router/src/utils.rs b/crates/router/src/utils.rs index 473afd1bf6..bfd3609d50 100644 --- a/crates/router/src/utils.rs +++ b/crates/router/src/utils.rs @@ -743,9 +743,11 @@ pub fn add_apple_pay_payment_status_metrics( } } +#[allow(clippy::too_many_arguments)] pub async fn trigger_payments_webhook( merchant_account: domain::MerchantAccount, business_profile: diesel_models::business_profile::BusinessProfile, + key_store: &domain::MerchantKeyStore, payment_data: crate::core::payments::PaymentData, req: Option, customer: Option, @@ -794,7 +796,8 @@ where if let services::ApplicationResponse::JsonWithHeaders((payments_response_json, _)) = payments_response { - let m_state = state.clone(); + let cloned_state = state.clone(); + let cloned_key_store = key_store.clone(); // This spawns this futures in a background thread, the exception inside this future won't affect // the current thread and the lifecycle of spawn thread is not handled by runtime. // So when server shutdown won't wait for this thread's completion. @@ -802,18 +805,20 @@ where if let Some(event_type) = event_type { tokio::spawn( async move { + let primary_object_created_at = payments_response_json.created; Box::pin(webhooks_core::create_event_and_trigger_outgoing_webhook( - m_state, + cloned_state, merchant_account, business_profile, + &cloned_key_store, event_type, diesel_models::enums::EventClass::Payments, - None, payment_id, diesel_models::enums::EventObjectType::PaymentDetails, webhooks::OutgoingWebhookContent::PaymentDetails( payments_response_json, ), + primary_object_created_at, )) .await } diff --git a/crates/router/src/workflows/outgoing_webhook_retry.rs b/crates/router/src/workflows/outgoing_webhook_retry.rs index 6ce7e2454f..044b23890f 100644 --- a/crates/router/src/workflows/outgoing_webhook_retry.rs +++ b/crates/router/src/workflows/outgoing_webhook_retry.rs @@ -4,6 +4,7 @@ use api_models::{ }; use common_utils::ext_traits::{StringExt, ValueExt}; use error_stack::ResultExt; +use masking::PeekInterface; use router_env::tracing::{self, instrument}; use scheduler::{ consumer::{self, workflows::ProcessTrackerWorkflow}, @@ -12,7 +13,10 @@ use scheduler::{ }; use crate::{ - core::webhooks::{self as webhooks_core, types::OutgoingWebhookTrackingData}, + core::webhooks::{ + self as webhooks_core, + types::{OutgoingWebhookRequestContent, OutgoingWebhookTrackingData}, + }, db::StorageInterface, errors, logger, routes::AppState, @@ -29,6 +33,7 @@ impl ProcessTrackerWorkflow for OutgoingWebhookRetryWorkflow { state: &'a AppState, process: storage::ProcessTracker, ) -> Result<(), errors::ProcessTrackerError> { + let delivery_attempt = webhooks_core::types::WebhookDeliveryAttempt::AutomaticRetry; let tracking_data: OutgoingWebhookTrackingData = process .tracking_data .clone() @@ -41,69 +46,150 @@ impl ProcessTrackerWorkflow for OutgoingWebhookRetryWorkflow { &db.get_master_key().to_vec().into(), ) .await?; - let merchant_account = db - .find_merchant_account_by_merchant_id(&tracking_data.merchant_id, &key_store) - .await?; let business_profile = db .find_business_profile_by_profile_id(&tracking_data.business_profile_id) .await?; - let event_id = format!( - "{}_{}", - tracking_data.primary_object_id, tracking_data.event_type + let event_id = webhooks_core::utils::generate_event_id(); + let idempotent_event_id = webhooks_core::utils::get_idempotent_event_id( + &tracking_data.primary_object_id, + tracking_data.event_type, + delivery_attempt, ); - let event = db.find_event_by_event_id(&event_id).await?; - let (content, event_type) = get_outgoing_webhook_content_and_event_type( - state.clone(), - merchant_account.clone(), - key_store, - &tracking_data, - ) - .await?; + let initial_event = match &tracking_data.initial_attempt_id { + Some(initial_attempt_id) => { + db.find_event_by_event_id(initial_attempt_id, &key_store) + .await? + } + // Tracking data inserted by old version of application, fetch event using old event ID + // format + None => { + let old_event_id = format!( + "{}_{}", + tracking_data.primary_object_id, tracking_data.event_type + ); + db.find_event_by_event_id(&old_event_id, &key_store).await? + } + }; - match event_type { - // Resource status is same as the event type of the current event - Some(event_type) if event_type == tracking_data.event_type => { - let outgoing_webhook = OutgoingWebhook { - merchant_id: tracking_data.merchant_id.clone(), - event_id: event_id.clone(), - event_type, - content: content.clone(), - timestamp: event.created_at, - }; + let now = common_utils::date_time::now(); + let new_event = domain::Event { + event_id, + event_type: initial_event.event_type, + event_class: initial_event.event_class, + is_webhook_notified: false, + primary_object_id: initial_event.primary_object_id, + primary_object_type: initial_event.primary_object_type, + created_at: now, + merchant_id: Some(business_profile.merchant_id.clone()), + business_profile_id: Some(business_profile.profile_id.clone()), + primary_object_created_at: initial_event.primary_object_created_at, + idempotent_event_id: Some(idempotent_event_id), + initial_attempt_id: Some(initial_event.event_id.clone()), + request: initial_event.request, + response: None, + }; - webhooks_core::trigger_appropriate_webhook_and_raise_event( + let event = db + .insert_event(new_event, &key_store) + .await + .map_err(|error| { + logger::error!(?error, "Failed to insert event in events table"); + error + })?; + + match &event.request { + Some(request) => { + let request_content: OutgoingWebhookRequestContent = request + .get_inner() + .peek() + .parse_struct("OutgoingWebhookRequestContent")?; + + webhooks_core::trigger_webhook_and_raise_event( state.clone(), - merchant_account, business_profile, - outgoing_webhook, + &key_store, + event, + request_content, webhooks_core::types::WebhookDeliveryAttempt::AutomaticRetry, - content, - event_id, - event_type, + None, Some(process), ) .await; } - // Resource status has changed since the event was created, finish task - _ => { - logger::warn!( - %event_id, - "The current status of the resource `{}` (event type: {:?}) and the status of \ - the resource when the event was created (event type: {:?}) differ, finishing task", - tracking_data.primary_object_id, - event_type, - tracking_data.event_type - ); - db.as_scheduler() - .finish_process_with_business_status( - process.clone(), - "RESOURCE_STATUS_MISMATCH".to_string(), - ) + + // Event inserted by old version of application, fetch current information about + // resource + None => { + let merchant_account = db + .find_merchant_account_by_merchant_id(&tracking_data.merchant_id, &key_store) .await?; + + let (content, event_type) = get_outgoing_webhook_content_and_event_type( + state.clone(), + merchant_account.clone(), + key_store.clone(), + &tracking_data, + ) + .await?; + + match event_type { + // Resource status is same as the event type of the current event + Some(event_type) if event_type == tracking_data.event_type => { + let outgoing_webhook = OutgoingWebhook { + merchant_id: tracking_data.merchant_id.clone(), + event_id: event.event_id.clone(), + event_type, + content: content.clone(), + timestamp: event.created_at, + }; + + let request_content = webhooks_core::get_outgoing_webhook_request( + &merchant_account, + outgoing_webhook, + business_profile.payment_response_hash_key.as_deref(), + ) + .map_err(|error| { + logger::error!( + ?error, + "Failed to obtain outgoing webhook request content" + ); + errors::ProcessTrackerError::EApiErrorResponse + })?; + + webhooks_core::trigger_webhook_and_raise_event( + state.clone(), + business_profile, + &key_store, + event, + request_content, + webhooks_core::types::WebhookDeliveryAttempt::AutomaticRetry, + Some(content), + Some(process), + ) + .await; + } + // Resource status has changed since the event was created, finish task + _ => { + logger::warn!( + %event.event_id, + "The current status of the resource `{}` (event type: {:?}) and the status of \ + the resource when the event was created (event type: {:?}) differ, finishing task", + tracking_data.primary_object_id, + event_type, + tracking_data.event_type + ); + db.as_scheduler() + .finish_process_with_business_status( + process.clone(), + "RESOURCE_STATUS_MISMATCH".to_string(), + ) + .await?; + } + } } - } + }; Ok(()) } diff --git a/crates/router/src/workflows/payment_sync.rs b/crates/router/src/workflows/payment_sync.rs index c328a67a0f..dd364f23b7 100644 --- a/crates/router/src/workflows/payment_sync.rs +++ b/crates/router/src/workflows/payment_sync.rs @@ -69,7 +69,7 @@ impl ProcessTrackerWorkflow for PaymentsSyncWorkflow { >( state, merchant_account.clone(), - key_store, + key_store.clone(), operations::PaymentStatus, tracking_data.clone(), payment_flows::CallConnectorAction::Trigger, @@ -182,6 +182,7 @@ impl ProcessTrackerWorkflow for PaymentsSyncWorkflow { >( merchant_account, business_profile, + &key_store, payment_data, None, customer, diff --git a/crates/router_env/Cargo.toml b/crates/router_env/Cargo.toml index 14e1d1f20a..3a4dcf128b 100644 --- a/crates/router_env/Cargo.toml +++ b/crates/router_env/Cargo.toml @@ -23,7 +23,7 @@ strum = { version = "0.24.1", features = ["derive"] } time = { version = "0.3.21", default-features = false, features = ["formatting"] } tokio = { version = "1.36.0" } tracing = { version = "0.1.37" } -tracing-actix-web = { version = "0.7.8", features = ["opentelemetry_0_19", "uuid_v7"], optional = true } +tracing-actix-web = { version = "0.7.10", features = ["opentelemetry_0_19", "uuid_v7"], optional = true } tracing-appender = { version = "0.2.2" } tracing-attributes = "0.1.27" tracing-opentelemetry = { version = "0.19.0" } diff --git a/crates/scheduler/Cargo.toml b/crates/scheduler/Cargo.toml index 72eaea3be0..cad5dec628 100644 --- a/crates/scheduler/Cargo.toml +++ b/crates/scheduler/Cargo.toml @@ -23,7 +23,7 @@ strum = { version = "0.24.1", features = ["derive"] } thiserror = "1.0.40" time = { version = "0.3.21", features = ["serde", "serde-well-known", "std"] } tokio = { version = "1.36.0", features = ["macros", "rt-multi-thread"] } -uuid = { version = "1.3.3", features = ["serde", "v4"] } +uuid = { version = "1.7.0", features = ["v4"] } # First party crates common_utils = { version = "0.1.0", path = "../common_utils", features = ["signals", "async_ext"] } diff --git a/crates/scheduler/src/errors.rs b/crates/scheduler/src/errors.rs index 6a14bd2611..e4fd56ba8d 100644 --- a/crates/scheduler/src/errors.rs +++ b/crates/scheduler/src/errors.rs @@ -45,16 +45,16 @@ pub enum ProcessTrackerError { EApiErrorResponse, #[error("Received Error ClientError")] EClientError, - #[error("Received Error StorageError: {0}")] + #[error("Received Error StorageError: {0:?}")] EStorageError(error_stack::Report), - #[error("Received Error RedisError: {0}")] + #[error("Received Error RedisError: {0:?}")] ERedisError(error_stack::Report), - #[error("Received Error ParsingError: {0}")] + #[error("Received Error ParsingError: {0:?}")] EParsingError(error_stack::Report), - #[error("Validation Error Received: {0}")] + #[error("Validation Error Received: {0:?}")] EValidationError(error_stack::Report), #[cfg(feature = "email")] - #[error("Received Error EmailError: {0}")] + #[error("Received Error EmailError: {0:?}")] EEmailError(error_stack::Report), #[error("Type Conversion error")] TypeConversionError, diff --git a/migrations/2024-03-04-204051_events_store_webhook_delivery_attempt_info/down.sql b/migrations/2024-03-04-204051_events_store_webhook_delivery_attempt_info/down.sql new file mode 100644 index 0000000000..184e694cf7 --- /dev/null +++ b/migrations/2024-03-04-204051_events_store_webhook_delivery_attempt_info/down.sql @@ -0,0 +1,21 @@ +-- The following queries must be run before the older version of the application is deployed. +-- Remove `event_id` as primary key and add unique constraint +ALTER TABLE events DROP CONSTRAINT events_pkey; + +ALTER TABLE events +ADD CONSTRAINT event_id_unique UNIQUE (event_id); + +-- Adding back unused columns, and make `id` as primary key +ALTER TABLE events + ADD COLUMN id SERIAL PRIMARY KEY, + ADD COLUMN intent_reference_id VARCHAR(64) DEFAULT NULL; + +-- The following queries must be run after the older version of the application is deployed. +ALTER TABLE events + DROP COLUMN merchant_id, + DROP COLUMN business_profile_id, + DROP COLUMN primary_object_created_at, + DROP COLUMN idempotent_event_id, + DROP COLUMN initial_attempt_id, + DROP COLUMN request, + DROP COLUMN response; diff --git a/migrations/2024-03-04-204051_events_store_webhook_delivery_attempt_info/up.sql b/migrations/2024-03-04-204051_events_store_webhook_delivery_attempt_info/up.sql new file mode 100644 index 0000000000..6edd288316 --- /dev/null +++ b/migrations/2024-03-04-204051_events_store_webhook_delivery_attempt_info/up.sql @@ -0,0 +1,36 @@ +-- The following queries must be run before the newer version of the application is deployed. +ALTER TABLE events + ADD COLUMN merchant_id VARCHAR(64) DEFAULT NULL, + ADD COLUMN business_profile_id VARCHAR(64) DEFAULT NULL, + ADD COLUMN primary_object_created_at TIMESTAMP DEFAULT NULL, + ADD COLUMN idempotent_event_id VARCHAR(64) DEFAULT NULL, + ADD COLUMN initial_attempt_id VARCHAR(64) DEFAULT NULL, + ADD COLUMN request BYTEA DEFAULT NULL, + ADD COLUMN response BYTEA DEFAULT NULL; + +UPDATE events +SET idempotent_event_id = event_id +WHERE idempotent_event_id IS NULL; + +UPDATE events +SET initial_attempt_id = event_id +WHERE initial_attempt_id IS NULL; + +ALTER TABLE events +ADD CONSTRAINT idempotent_event_id_unique UNIQUE (idempotent_event_id); + +-- The following queries must be run after the newer version of the application is deployed. +-- Running these queries can even be deferred for some time (a couple of weeks or even a month) until the +-- new version being deployed is considered stable. +-- Make `event_id` primary key instead of `id` +ALTER TABLE events DROP CONSTRAINT events_pkey; + +ALTER TABLE events +ADD PRIMARY KEY (event_id); + +ALTER TABLE events DROP CONSTRAINT event_id_unique; + +-- Dropping unused columns +ALTER TABLE events + DROP COLUMN id, + DROP COLUMN intent_reference_id;