diff --git a/Cargo.lock b/Cargo.lock index 80cd7c892..75629ef1d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,9 +13,9 @@ dependencies = [ [[package]] name = "adler2" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "aes" @@ -23,19 +23,19 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ - "cfg-if", + "cfg-if 1.0.1", "cipher", "cpufeatures", ] [[package]] name = "ahash" -version = "0.8.11" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" dependencies = [ - "cfg-if", - "getrandom 0.2.15", + "cfg-if 1.0.1", + "getrandom 0.3.3", "once_cell", "serde", "version_check", @@ -51,6 +51,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "aligned-vec" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc890384c8602f339876ded803c97ad529f3842aba97f6392b3dba0dd171769b" +dependencies = [ + "equator", +] + [[package]] name = "allocator-api2" version = "0.2.21" @@ -78,14 +87,64 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" dependencies = [ - "winapi", + "winapi 0.3.9", +] + +[[package]] +name = "anstream" +version = "0.6.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.59.0", ] [[package]] name = "anyhow" -version = "1.0.96" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b964d184e89d9b6b67dd2715bc8e74cf3107fb2b529990c90cf517326150bf4" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" [[package]] name = "ariadne" @@ -157,9 +216,9 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.18" +version = "0.4.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df895a515f70646414f4b45c0b79082783b80552b373a68283012928df56f522" +checksum = "40f6024f3f856663b45fd0c9b6f2024034a702f453549449e0d84a305900dad4" dependencies = [ "flate2", "futures-core", @@ -170,14 +229,15 @@ dependencies = [ [[package]] name = "async-executor" -version = "1.13.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec" +checksum = "bb812ffb58524bdd10860d7d974e2f01cc0950c2438a74ee5ec2e2280c6c4ffa" dependencies = [ "async-task", "concurrent-queue", "fastrand", "futures-lite", + "pin-project-lite", "slab", ] @@ -198,18 +258,18 @@ dependencies = [ [[package]] name = "async-io" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a2b323ccce0a1d90b449fd71f2a06ca7faa7c54c2751f06c9bd851fc061059" +checksum = "1237c0ae75a0f3765f58910ff9cdd0a12eeb39ab2f4c7de23262f337f0aacbb3" dependencies = [ "async-lock 3.4.0", - "cfg-if", + "cfg-if 1.0.1", "concurrent-queue", "futures-io", "futures-lite", "parking", "polling", - "rustix 0.38.44", + "rustix 1.0.7", "slab", "tracing", "windows-sys 0.59.0", @@ -256,7 +316,7 @@ dependencies = [ "async-signal", "async-task", "blocking", - "cfg-if", + "cfg-if 1.0.1", "event-listener 5.4.0", "futures-lite", "rustix 1.0.7", @@ -272,7 +332,7 @@ dependencies = [ "async-io", "async-lock 3.4.0", "atomic-waker", - "cfg-if", + "cfg-if 1.0.1", "futures-core", "futures-io", "rustix 1.0.7", @@ -283,9 +343,9 @@ dependencies = [ [[package]] name = "async-std" -version = "1.13.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c634475f29802fde2b8f0b505b1bd00dfe4df7d4a000f0b36f7671197d5c3615" +checksum = "730294c1c08c2e0f85759590518f6333f0d5a0a766a27d519c1b244c3dfd8a24" dependencies = [ "async-attributes", "async-channel 1.9.0", @@ -317,13 +377,13 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] name = "async-trait" -version = "0.1.86" +version = "0.1.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "644dd749086bf3771a2fbc5f256fdb982d53f011c7d5d560304eafeecebce79d" +checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.104", ] [[package]] @@ -334,9 +394,9 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "axum" @@ -347,11 +407,11 @@ dependencies = [ "async-trait", "axum-core", "bitflags 1.3.2", - "bytes", + "bytes 1.10.1", "futures-util", - "http", - "http-body", - "hyper", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.27", "itoa", "matchit", "memchr", @@ -373,10 +433,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" dependencies = [ "async-trait", - "bytes", + "bytes 1.10.1", "futures-util", - "http", - "http-body", + "http 0.2.12", + "http-body 0.4.6", "mime", "rustversion", "tower-layer", @@ -385,12 +445,12 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.74" +version = "0.3.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" dependencies = [ "addr2line", - "cfg-if", + "cfg-if 1.0.1", "libc", "miniz_oxide", "object", @@ -418,9 +478,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" -version = "1.6.0" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +checksum = "89e25b6adfb930f02d1981565a6e5d9c547ac15a96606256d3b59040e5cd4ca3" [[package]] name = "basic-cookies" @@ -465,9 +525,21 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.8.0" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" + +[[package]] +name = "block-buffer" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" +checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +dependencies = [ + "block-padding", + "byte-tools", + "byteorder", + "generic-array 0.12.4", +] [[package]] name = "block-buffer" @@ -475,7 +547,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" dependencies = [ - "generic-array", + "generic-array 0.14.7", ] [[package]] @@ -484,7 +556,16 @@ version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ - "generic-array", + "generic-array 0.14.7", +] + +[[package]] +name = "block-padding" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" +dependencies = [ + "byte-tools", ] [[package]] @@ -502,9 +583,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.11.3" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0" +checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4" dependencies = [ "memchr", "serde", @@ -512,15 +593,27 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.17.0" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" + +[[package]] +name = "byte-tools" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" +checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" [[package]] name = "bytecount" -version = "0.6.8" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175812e0be2bccb6abe50bb8d566126198344f707e304f45c648fd8f2cc0365e" + +[[package]] +name = "bytemuck" +version = "1.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce" +checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422" [[package]] name = "byteorder" @@ -530,9 +623,19 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.10.0" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" +dependencies = [ + "byteorder", + "iovec", +] + +[[package]] +name = "bytes" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" dependencies = [ "serde", ] @@ -549,20 +652,19 @@ dependencies = [ [[package]] name = "bzip2-sys" -version = "0.1.12+1.0.8" +version = "0.1.13+1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72ebc2f1a417f01e1da30ef264ee86ae31d2dcd2d603ea283d3c244a883ca2a9" +checksum = "225bff33b2141874fe80d71e07d6eec4f85c5c216453dd96388240f96e1acc14" dependencies = [ "cc", - "libc", "pkg-config", ] [[package]] name = "cc" -version = "1.2.14" +version = "1.2.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c3d1b2e905a3a7b00a6141adb0e4c0bb941d11caf55349d863942a1cc44e3c9" +checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc" dependencies = [ "jobserver", "libc", @@ -571,15 +673,21 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.0" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" [[package]] name = "chrono" -version = "0.4.39" +version = "0.4.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" +checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" dependencies = [ "android-tzdata", "iana-time-zone", @@ -587,7 +695,7 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-targets 0.52.6", + "windows-link", ] [[package]] @@ -650,6 +758,12 @@ dependencies = [ "os_str_bytes", ] +[[package]] +name = "colorchoice" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + [[package]] name = "concurrent-queue" version = "2.5.0" @@ -661,15 +775,15 @@ dependencies = [ [[package]] name = "console" -version = "0.15.10" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea3c6ecd8059b57859df5c69830340ed3c41d30e3da0c1cbed90a96ac853041b" +checksum = "2e09ced7ebbccb63b4c65413d821f2e00ce54c5ca4514ddc6b3c892fdbcbc69d" dependencies = [ "encode_unicode", "libc", "once_cell", - "unicode-width 0.2.0", - "windows-sys 0.59.0", + "unicode-width 0.2.1", + "windows-sys 0.60.2", ] [[package]] @@ -694,6 +808,15 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "cpp_demangle" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96e58d342ad113c2b878f16d5d034c03be492ae460cdbc02b7f0f2284d310c7d" +dependencies = [ + "cfg-if 1.0.1", +] + [[package]] name = "cpufeatures" version = "0.2.17" @@ -709,7 +832,7 @@ version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ - "cfg-if", + "cfg-if 1.0.1", ] [[package]] @@ -739,9 +862,9 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crunchy" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "crypto-common" @@ -749,7 +872,7 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ - "generic-array", + "generic-array 0.14.7", "typenum", ] @@ -776,15 +899,24 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "575f75dfd25738df5b91b8e43e14d44bda14637a58fae779fd2b064f8bf3e010" +checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" + +[[package]] +name = "debugid" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" +dependencies = [ + "uuid", +] [[package]] name = "deranged" -version = "0.3.11" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" dependencies = [ "powerfmt", ] @@ -795,13 +927,22 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +dependencies = [ + "generic-array 0.12.4", +] + [[package]] name = "digest" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" dependencies = [ - "generic-array", + "generic-array 0.14.7", ] [[package]] @@ -821,7 +962,7 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" dependencies = [ - "cfg-if", + "cfg-if 1.0.1", "dirs-sys-next", ] @@ -833,20 +974,37 @@ checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" dependencies = [ "libc", "redox_users", - "winapi", + "winapi 0.3.9", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", ] +[[package]] +name = "downcast" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" + [[package]] name = "dyn-clone" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "feeef44e73baff3a26d371801df019877a9866a8c493d315ab00177843314f35" +checksum = "1c7a8fb8a9fbf66c1f703fe16184d10ca0ee9d23be5b4436400408ba54a95005" [[package]] name = "either" -version = "1.13.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "ena" @@ -869,7 +1027,7 @@ version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" dependencies = [ - "cfg-if", + "cfg-if 1.0.1", ] [[package]] @@ -881,6 +1039,49 @@ dependencies = [ "regex", ] +[[package]] +name = "env_filter" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "jiff", + "log", +] + +[[package]] +name = "equator" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4711b213838dfee0117e3be6ac926007d7f433d7bbe33595975d4190cb07e6fc" +dependencies = [ + "equator-macro", +] + +[[package]] +name = "equator-macro" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -889,12 +1090,12 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.10" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -926,9 +1127,9 @@ dependencies = [ [[package]] name = "event-listener-strategy" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c3e4e0dd3673c1139bf041f3008816d9cf2946bbfac2945c09e523b8d7b05b2" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" dependencies = [ "event-listener 5.4.0", "pin-project-lite", @@ -949,6 +1150,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" + [[package]] name = "fancy-regex" version = "0.11.0" @@ -980,12 +1187,24 @@ version = "0.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" dependencies = [ - "cfg-if", + "cfg-if 1.0.1", "libc", "libredox", "windows-sys 0.59.0", ] +[[package]] +name = "findshlibs" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40b9e59cd0f7e0806cca4be089683ecb6434e602038df21fe6bf6711b2f07f64" +dependencies = [ + "cc", + "lazy_static", + "libc", + "winapi 0.3.9", +] + [[package]] name = "fixedbitset" version = "0.4.2" @@ -994,9 +1213,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.35" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" +checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" dependencies = [ "crc32fast", "miniz_oxide", @@ -1027,6 +1246,12 @@ dependencies = [ "num", ] +[[package]] +name = "fragile" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dd6caf6059519a65843af8fe2a3ae298b14b80179855aeb4adc2c1934ee619" + [[package]] name = "fs2" version = "0.4.3" @@ -1034,9 +1259,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" dependencies = [ "libc", - "winapi", + "winapi 0.3.9", +] + +[[package]] +name = "fuchsia-zircon" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" +dependencies = [ + "bitflags 1.3.2", + "fuchsia-zircon-sys", ] +[[package]] +name = "fuchsia-zircon-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" + [[package]] name = "futures" version = "0.3.31" @@ -1106,7 +1347,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.104", ] [[package]] @@ -1154,6 +1395,15 @@ dependencies = [ "byteorder", ] +[[package]] +name = "generic-array" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" +dependencies = [ + "typenum", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -1166,27 +1416,38 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.15" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if 1.0.1", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ - "cfg-if", + "cfg-if 1.0.1", "js-sys", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi 0.11.1+wasi-snapshot-preview1", "wasm-bindgen", ] [[package]] name = "getrandom" -version = "0.3.1" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ - "cfg-if", + "cfg-if 1.0.1", "libc", - "wasi 0.13.3+wasi-0.2.2", - "windows-targets 0.52.6", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", ] [[package]] @@ -1203,9 +1464,9 @@ checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" [[package]] name = "globset" -version = "0.4.15" +version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15f1ce686646e7f1e19bf7d5533fe443a45dbfb990e00629110797578b42fb19" +checksum = "54a1028dfc5f5df5da8a56a73e6c153c9a9708ec57232470703592a3f18e49f5" dependencies = [ "aho-corasick", "bstr", @@ -1241,13 +1502,32 @@ version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" dependencies = [ - "bytes", + "bytes 1.10.1", "fnv", "futures-core", "futures-sink", "futures-util", - "http", - "indexmap 2.7.1", + "http 0.2.12", + "indexmap 2.10.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "h2" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17da50a276f1e01e0ba6c029e47b7100754904ee8a278f886546e98575380785" +dependencies = [ + "atomic-waker", + "bytes 1.10.1", + "fnv", + "futures-core", + "futures-sink", + "http 1.3.1", + "indexmap 2.10.0", "slab", "tokio", "tokio-util", @@ -1272,9 +1552,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.2" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" [[package]] name = "hashers" @@ -1299,9 +1579,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" -version = "0.4.0" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" [[package]] name = "hex" @@ -1339,7 +1619,18 @@ version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" dependencies = [ - "bytes", + "bytes 1.10.1", + "fnv", + "itoa", +] + +[[package]] +name = "http" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +dependencies = [ + "bytes 1.10.1", "fnv", "itoa", ] @@ -1350,16 +1641,39 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ - "bytes", - "http", + "bytes 1.10.1", + "http 0.2.12", + "pin-project-lite", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes 1.10.1", + "http 1.3.1", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes 1.10.1", + "futures-core", + "http 1.3.1", + "http-body 1.0.1", "pin-project-lite", ] [[package]] name = "httparse" -version = "1.10.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2d708df4e7140240a16cd6ab0ab65c972d7433ab77819ea693fde9c43811e2a" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "httpdate" @@ -1382,7 +1696,7 @@ dependencies = [ "crossbeam-utils", "form_urlencoded", "futures-util", - "hyper", + "hyper 0.14.27", "lazy_static", "levenshtein", "log", @@ -1401,13 +1715,13 @@ version = "0.14.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" dependencies = [ - "bytes", + "bytes 1.10.1", "futures-channel", "futures-core", "futures-util", - "h2", - "http", - "http-body", + "h2 0.3.26", + "http 0.2.12", + "http-body 0.4.6", "httparse", "httpdate", "itoa", @@ -1420,17 +1734,37 @@ dependencies = [ ] [[package]] -name = "hyper-rustls" -version = "0.24.2" +name = "hyper" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" dependencies = [ + "bytes 1.10.1", + "futures-channel", "futures-util", - "http", - "hyper", - "log", - "rustls", - "rustls-native-certs", + "h2 0.4.11", + "http 1.3.1", + "http-body 1.0.1", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", +] + +[[package]] +name = "hyper-rustls" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +dependencies = [ + "futures-util", + "http 0.2.12", + "hyper 0.14.27", + "log", + "rustls", + "rustls-native-certs", "tokio", "tokio-rustls", ] @@ -1441,24 +1775,41 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" dependencies = [ - "hyper", + "hyper 0.14.27", "pin-project-lite", "tokio", "tokio-io-timeout", ] +[[package]] +name = "hyper-util" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc2fdfdbff08affe55bb779f33b053aa1fe5dd5b54c257343c17edfa55711bdb" +dependencies = [ + "bytes 1.10.1", + "futures-core", + "http 1.3.1", + "http-body 1.0.1", + "hyper 1.6.0", + "pin-project-lite", + "tokio", + "tower-service", +] + [[package]] name = "iana-time-zone" -version = "0.1.61" +version = "0.1.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", + "log", "wasm-bindgen", - "windows-core", + "windows-core 0.61.2", ] [[package]] @@ -1470,14 +1821,111 @@ dependencies = [ "cc", ] +[[package]] +name = "icu_collections" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" + +[[package]] +name = "icu_properties" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "potential_utf", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" + +[[package]] +name = "icu_provider" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +dependencies = [ + "displaydoc", + "icu_locale_core", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + [[package]] name = "idna" -version = "0.5.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", ] [[package]] @@ -1492,58 +1940,85 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.7.1" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" dependencies = [ "equivalent", - "hashbrown 0.15.2", + "hashbrown 0.15.4", ] [[package]] name = "indextree" -version = "4.7.3" +version = "4.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91f3e68a01402c3404bfb739079f38858325bc7ad775b07922278a8a415b1a3f" +checksum = "cb9e21e48c85fa6643a38caca564645a3bbc9211edf506fc8ed690c7e7b4d3c7" dependencies = [ "indextree-macros", ] [[package]] name = "indextree-macros" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "477e2e7ec7379407656293ff74902caea786a1dda427ca1f84b923c4fdeb7659" +checksum = "f85dac6c239acc85fd61934c572292d93adfd2de459d9c032aa22b553506e915" dependencies = [ "either", - "itertools 0.13.0", + "itertools 0.14.0", "proc-macro2", "quote", - "strum 0.26.3", - "syn 2.0.98", - "thiserror", + "strum 0.27.1", + "syn 2.0.104", + "thiserror 2.0.12", ] [[package]] name = "indicatif" -version = "0.17.11" +version = "0.17.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "183b3088984b400f4cfac3620d5e076c84da5364016b4f49473de574b2586235" +checksum = "4adb2ee6ad319a912210a36e56e3623555817bcc877a7e6e8802d1d69c4d8056" dependencies = [ "console", - "number_prefix", "portable-atomic", - "unicode-width 0.2.0", + "unicode-width 0.2.1", + "unit-prefix", "web-time", ] +[[package]] +name = "inferno" +version = "0.11.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "232929e1d75fe899576a3d5c7416ad0d88dbfbb3c3d6aa00873a7408a50ddb88" +dependencies = [ + "ahash", + "indexmap 2.10.0", + "is-terminal", + "itoa", + "log", + "num-format", + "once_cell", + "quick-xml 0.26.0", + "rgb", + "str_stack", +] + [[package]] name = "inout" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" dependencies = [ - "generic-array", + "generic-array 0.14.7", +] + +[[package]] +name = "iovec" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" +dependencies = [ + "libc", ] [[package]] @@ -1552,11 +2027,28 @@ version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" +[[package]] +name = "is-terminal" +version = "0.4.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + [[package]] name = "iso8601" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5c177cff824ab21a6f41079a4c401241c4e8be14f316c4c6b07d5fca351c98d" +checksum = "e1082f0c48f143442a1ac6122f67e360ceee130b967af4d50996e5154a45df46" dependencies = [ "nom 8.0.0", ] @@ -1590,18 +2082,18 @@ dependencies = [ [[package]] name = "itertools" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" dependencies = [ "either", ] [[package]] name = "itoa" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jaq-core" @@ -1629,7 +2121,7 @@ dependencies = [ "ahash", "dyn-clone", "hifijson", - "indexmap 2.7.1", + "indexmap 2.10.0", "jaq-syn", "once_cell", "serde_json", @@ -1663,12 +2155,37 @@ dependencies = [ "serde", ] +[[package]] +name = "jiff" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be1f93b8b1eb69c77f24bbb0afdf66f54b632ee39af40ca21c4365a1d7347e49" +dependencies = [ + "jiff-static", + "log", + "portable-atomic", + "portable-atomic-util", + "serde", +] + +[[package]] +name = "jiff-static" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + [[package]] name = "jobserver" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" dependencies = [ + "getrandom 0.3.3", "libc", ] @@ -1688,14 +2205,28 @@ version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "367a292944c07385839818bb71c8d76611138e2dedb0677d035b8da21d29c78b" dependencies = [ - "jsonrpsee-core", - "jsonrpsee-proc-macros", - "jsonrpsee-server", - "jsonrpsee-types", + "jsonrpsee-core 0.16.3", + "jsonrpsee-proc-macros 0.16.3", + "jsonrpsee-server 0.16.3", + "jsonrpsee-types 0.16.3", "jsonrpsee-ws-client", "tracing", ] +[[package]] +name = "jsonrpsee" +version = "0.24.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b26c20e2178756451cfeb0661fb74c47dd5988cb7e3939de7e9241fd604d42" +dependencies = [ + "jsonrpsee-core 0.24.9", + "jsonrpsee-proc-macros 0.24.9", + "jsonrpsee-server 0.24.9", + "jsonrpsee-types 0.24.9", + "tokio", + "tracing", +] + [[package]] name = "jsonrpsee-client-transport" version = "0.16.3" @@ -1703,13 +2234,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8b3815d9f5d5de348e5f162b316dc9cdf4548305ebb15b4eb9328e66cf27d7a" dependencies = [ "futures-util", - "http", - "jsonrpsee-core", - "jsonrpsee-types", + "http 0.2.12", + "jsonrpsee-core 0.16.3", + "jsonrpsee-types 0.16.3", "pin-project", "rustls-native-certs", - "soketto", - "thiserror", + "soketto 0.7.1", + "thiserror 1.0.69", "tokio", "tokio-rustls", "tokio-util", @@ -1732,15 +2263,38 @@ dependencies = [ "futures-timer", "futures-util", "globset", - "hyper", - "jsonrpsee-types", + "hyper 0.14.27", + "jsonrpsee-types 0.16.3", + "parking_lot", + "rand 0.8.5", + "rustc-hash 1.1.0", + "serde", + "serde_json", + "soketto 0.7.1", + "thiserror 1.0.69", + "tokio", + "tracing", +] + +[[package]] +name = "jsonrpsee-core" +version = "0.24.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456196007ca3a14db478346f58c7238028d55ee15c1df15115596e411ff27925" +dependencies = [ + "async-trait", + "bytes 1.10.1", + "futures-util", + "http 1.3.1", + "http-body 1.0.1", + "http-body-util", + "jsonrpsee-types 0.24.9", "parking_lot", - "rand", - "rustc-hash", + "rand 0.8.5", + "rustc-hash 2.1.1", "serde", "serde_json", - "soketto", - "thiserror", + "thiserror 1.0.69", "tokio", "tracing", ] @@ -1752,12 +2306,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44e8ab85614a08792b9bff6c8feee23be78c98d0182d4c622c05256ab553892a" dependencies = [ "heck 0.4.1", - "proc-macro-crate", + "proc-macro-crate 1.3.1", "proc-macro2", "quote", "syn 1.0.109", ] +[[package]] +name = "jsonrpsee-proc-macros" +version = "0.24.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e65763c942dfc9358146571911b0cd1c361c2d63e2d2305622d40d36376ca80" +dependencies = [ + "heck 0.5.0", + "proc-macro-crate 3.3.0", + "proc-macro2", + "quote", + "syn 2.0.104", +] + [[package]] name = "jsonrpsee-server" version = "0.16.3" @@ -1766,13 +2333,40 @@ checksum = "cf4d945a6008c9b03db3354fb3c83ee02d2faa9f2e755ec1dfb69c3551b8f4ba" dependencies = [ "futures-channel", "futures-util", - "http", - "hyper", - "jsonrpsee-core", - "jsonrpsee-types", + "http 0.2.12", + "hyper 0.14.27", + "jsonrpsee-core 0.16.3", + "jsonrpsee-types 0.16.3", "serde", "serde_json", - "soketto", + "soketto 0.7.1", + "tokio", + "tokio-stream", + "tokio-util", + "tower", + "tracing", +] + +[[package]] +name = "jsonrpsee-server" +version = "0.24.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55e363146da18e50ad2b51a0a7925fc423137a0b1371af8235b1c231a0647328" +dependencies = [ + "futures-util", + "http 1.3.1", + "http-body 1.0.1", + "http-body-util", + "hyper 1.6.0", + "hyper-util", + "jsonrpsee-core 0.24.9", + "jsonrpsee-types 0.24.9", + "pin-project", + "route-recognizer", + "serde", + "serde_json", + "soketto 0.8.1", + "thiserror 1.0.69", "tokio", "tokio-stream", "tokio-util", @@ -1790,20 +2384,32 @@ dependencies = [ "beef", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", "tracing", ] +[[package]] +name = "jsonrpsee-types" +version = "0.24.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08a8e70baf945b6b5752fc8eb38c918a48f1234daf11355e07106d963f860089" +dependencies = [ + "http 1.3.1", + "serde", + "serde_json", + "thiserror 1.0.69", +] + [[package]] name = "jsonrpsee-ws-client" version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e1b3975ed5d73f456478681a417128597acd6a2487855fdb7b4a3d4d195bf5e" dependencies = [ - "http", + "http 0.2.12", "jsonrpsee-client-transport", - "jsonrpsee-core", - "jsonrpsee-types", + "jsonrpsee-core 0.16.3", + "jsonrpsee-types 0.16.3", ] [[package]] @@ -1818,7 +2424,7 @@ dependencies = [ "bytecount", "fancy-regex", "fraction", - "getrandom 0.2.15", + "getrandom 0.2.16", "iso8601", "itoa", "memchr", @@ -1834,6 +2440,16 @@ dependencies = [ "uuid", ] +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] + [[package]] name = "kv-log-macro" version = "1.0.7" @@ -1880,6 +2496,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + [[package]] name = "lenient_semver" version = "0.4.2" @@ -1917,9 +2539,9 @@ checksum = "db13adb97ab515a3691f56e4dbab09283d0b86cb45abd991d8634a9d6f501760" [[package]] name = "libc" -version = "0.2.169" +version = "0.2.174" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" [[package]] name = "libloading" @@ -1927,23 +2549,23 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" dependencies = [ - "cfg-if", - "winapi", + "cfg-if 1.0.1", + "winapi 0.3.9", ] [[package]] name = "libm" -version = "0.2.11" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" [[package]] name = "libredox" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +checksum = "1580801010e535496706ba011c15f8532df6b42297d2e471fec38ceadd8c0638" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.1", "libc", "redox_syscall", ] @@ -1960,11 +2582,17 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" +[[package]] +name = "litemap" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" + [[package]] name = "lock_api" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" dependencies = [ "autocfg", "scopeguard", @@ -1972,9 +2600,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.25" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" dependencies = [ "value-bag", ] @@ -1999,7 +2627,7 @@ dependencies = [ "proc-macro2", "quote", "regex-syntax 0.6.29", - "syn 2.0.98", + "syn 2.0.104", ] [[package]] @@ -2023,16 +2651,16 @@ dependencies = [ "futures-channel", "futures-util", "httpmock", - "hyper", + "hyper 0.14.27", "jaq-core", "jaq-interpret", "jaq-parse", "jaq-std", - "jsonrpsee", + "jsonrpsee 0.16.3", "openrpc_validator", "proc-macro2", "querystring", - "rand", + "rand 0.8.5", "regex", "ripple_sdk", "ripple_tdk", @@ -2041,6 +2669,8 @@ dependencies = [ "serde", "serde_json", "serial_test", + "ssda_service", + "ssda_types", "strum 0.24.1", "strum_macros 0.24.3", "url", @@ -2067,9 +2697,18 @@ checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" + +[[package]] +name = "memmap2" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" +dependencies = [ + "libc", +] [[package]] name = "mime" @@ -2085,22 +2724,65 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.8.4" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3b1c9bd4fe1f0f8b387f6eb9eb3b4a1aa26185e5750efb9140301703f62cd1b" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", ] [[package]] name = "mio" -version = "1.0.3" +version = "0.6.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4" dependencies = [ + "cfg-if 0.1.10", + "fuchsia-zircon", + "fuchsia-zircon-sys", + "iovec", + "kernel32-sys", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.52.0", + "log", + "miow", + "net2", + "slab", + "winapi 0.2.8", +] + +[[package]] +name = "mio" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" +dependencies = [ + "libc", + "wasi 0.11.1+wasi-snapshot-preview1", + "windows-sys 0.59.0", +] + +[[package]] +name = "mio-extras" +version = "2.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52403fe290012ce777c4626790c8951324a2b9e3316b3143779c72b029742f19" +dependencies = [ + "lazycell", + "log", + "mio 0.6.23", + "slab", +] + +[[package]] +name = "miow" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d" +dependencies = [ + "kernel32-sys", + "net2", + "winapi 0.2.8", + "ws2_32-sys", ] [[package]] @@ -2120,8 +2802,8 @@ dependencies = [ name = "mock_device" version = "1.0.0" dependencies = [ - "http", - "jsonrpsee", + "http 0.2.12", + "jsonrpsee 0.16.3", "ripple_sdk", "ripple_tdk", "serde", @@ -2130,23 +2812,49 @@ dependencies = [ "url", ] +[[package]] +name = "mockall" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39a6bfcc6c8c7eed5ee98b9c3e33adc726054389233e201c95dab2d41a3839d2" +dependencies = [ + "cfg-if 1.0.1", + "downcast", + "fragile", + "mockall_derive", + "predicates", + "predicates-tree", +] + +[[package]] +name = "mockall_derive" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ca3004c2efe9011bd4e461bd8256445052b9615405b4f7ea43fc8ca5c20898" +dependencies = [ + "cfg-if 1.0.1", + "proc-macro2", + "quote", + "syn 2.0.104", +] + [[package]] name = "multer" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01acbdc23469fd8fe07ab135923371d5f5a422fbf9c522158677c8eb15bc51c2" dependencies = [ - "bytes", + "bytes 1.10.1", "encoding_rs", "futures-util", - "http", + "http 0.2.12", "httparse", "log", "memchr", "mime", "serde", "serde_json", - "spin", + "spin 0.9.8", "version_check", ] @@ -2156,12 +2864,34 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" +[[package]] +name = "net2" +version = "0.2.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b13b648036a2339d06de780866fbdfda0dde886de7b3af2ddeba8b14f4ee34ac" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "winapi 0.3.9", +] + [[package]] name = "new_debug_unreachable" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" +[[package]] +name = "nix" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" +dependencies = [ + "bitflags 1.3.2", + "cfg-if 1.0.1", + "libc", +] + [[package]] name = "nom" version = "7.1.3" @@ -2187,7 +2917,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" dependencies = [ - "winapi", + "winapi 0.3.9", ] [[package]] @@ -2235,6 +2965,16 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +[[package]] +name = "num-format" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3" +dependencies = [ + "arrayvec", + "itoa", +] + [[package]] name = "num-integer" version = "0.1.46" @@ -2275,12 +3015,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "number_prefix" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" - [[package]] name = "object" version = "0.36.7" @@ -2292,17 +3026,23 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.20.3" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" +checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" [[package]] name = "onig" -version = "6.4.0" +version = "6.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c4b31c8722ad9171c6d77d3557db078cab2bd50afcc9d09c8b315c59df8ca4f" +checksum = "336b9c63443aceef14bea841b899035ae3abe89b7c486aaf4c5bd8aafedac3f0" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.9.1", "libc", "once_cell", "onig_sys", @@ -2310,14 +3050,20 @@ dependencies = [ [[package]] name = "onig_sys" -version = "69.8.1" +version = "69.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b829e3d7e9cc74c7e315ee8edb185bf4190da5acde74afd7fc59c35b1f086e7" +checksum = "c7f86c6eef3d6df15f23bcfb6af487cbd2fed4e5581d58d5bf1f5f8b7f6727dc" dependencies = [ "cc", "pkg-config", ] +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" + [[package]] name = "opaque-debug" version = "0.3.1" @@ -2342,11 +3088,12 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "os_info" -version = "3.10.0" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a604e53c24761286860eba4e2c8b23a0161526476b1de520139d69cdb85a6b5" +checksum = "d0e1ac5fde8d43c34139135df8ea9ee9465394b2d8d20f032d38998f64afffc3" dependencies = [ "log", + "plist", "serde", "windows-sys 0.52.0", ] @@ -2366,7 +3113,7 @@ dependencies = [ "anyhow", "async-trait", "backtrace", - "bytes", + "bytes 1.10.1", "chrono", "flate2", "futures-util", @@ -2401,13 +3148,13 @@ dependencies = [ [[package]] name = "pact_consumer" -version = "1.1.0" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdff329c97c39ffbc78640c83d33cd1d28f2ea8ecb7f0d9e48cbfcce95960b96" +checksum = "016334b4a70ecb22f448267fd817003f7c09ffda5165aa31e9fe4ede5091aa07" dependencies = [ "anyhow", "async-trait", - "bytes", + "bytes 1.10.1", "futures", "itertools 0.10.5", "lazy_static", @@ -2434,12 +3181,12 @@ dependencies = [ "ansi_term", "anyhow", "base64 0.21.7", - "bytes", + "bytes 1.10.1", "chrono", "difference", "futures", "hex", - "http", + "http 0.2.12", "itertools 0.10.5", "lazy_static", "lenient_semver", @@ -2451,7 +3198,7 @@ dependencies = [ "onig", "pact-plugin-driver", "pact_models", - "rand", + "rand 0.8.5", "reqwest", "semver", "serde", @@ -2467,14 +3214,14 @@ dependencies = [ [[package]] name = "pact_mock_server" -version = "1.2.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fb13cec89b68677e5701d4861a24335bf614b103bb9328bbf605276f758c506" +checksum = "daaea27b75fbc1bf7740562518a77933f8bb2ba5b0d4115f61f52691553fbabd" dependencies = [ "anyhow", - "bytes", + "bytes 1.10.1", "futures", - "hyper", + "hyper 0.14.27", "hyper-rustls", "itertools 0.10.5", "lazy_static", @@ -2486,7 +3233,7 @@ dependencies = [ "rustls-pemfile", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", "tokio", "tokio-rustls", "tracing", @@ -2504,7 +3251,7 @@ dependencies = [ "anyhow", "ariadne", "base64 0.21.7", - "bytes", + "bytes 1.10.1", "chrono", "chrono-tz", "fs2", @@ -2521,7 +3268,7 @@ dependencies = [ "nom 7.1.3", "onig", "parse-zoneinfo", - "rand", + "rand 0.8.5", "rand_regex", "regex", "regex-syntax 0.6.29", @@ -2542,9 +3289,9 @@ checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" [[package]] name = "parking_lot" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" dependencies = [ "lock_api", "parking_lot_core", @@ -2552,11 +3299,11 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.10" +version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" dependencies = [ - "cfg-if", + "cfg-if 1.0.1", "libc", "redox_syscall", "smallvec", @@ -2579,7 +3326,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" dependencies = [ "base64ct", - "rand_core", + "rand_core 0.6.4", "subtle", ] @@ -2614,7 +3361,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", - "indexmap 2.7.1", + "indexmap 2.10.0", ] [[package]] @@ -2643,7 +3390,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ "phf_shared", - "rand", + "rand 0.8.5", ] [[package]] @@ -2663,22 +3410,22 @@ checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" [[package]] name = "pin-project" -version = "1.1.9" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfe2e71e1471fe07709406bf725f710b02927c9c54b2b5b2ec0e8087d97c327d" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.9" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6e859e6e5bd50440ab63c47e3ebabc90f26251f7c73c3d3e837b74a1cc3fa67" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.104", ] [[package]] @@ -2706,30 +3453,61 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.31" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "plist" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" +checksum = "3d77244ce2d584cd84f6a15f86195b8c9b2a0dfbfd817c09e0464244091a58ed" +dependencies = [ + "base64 0.22.1", + "indexmap 2.10.0", + "quick-xml 0.37.5", + "serde", + "time", +] [[package]] name = "polling" -version = "3.7.4" +version = "3.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f" +checksum = "b53a684391ad002dd6a596ceb6c74fd004fdce75f4be2e3f615068abbea5fd50" dependencies = [ - "cfg-if", + "cfg-if 1.0.1", "concurrent-queue", "hermit-abi", "pin-project-lite", - "rustix 0.38.44", + "rustix 1.0.7", "tracing", "windows-sys 0.59.0", ] [[package]] name = "portable-atomic" -version = "1.10.0" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" + +[[package]] +name = "portable-atomic-util" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6" +checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "potential_utf" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +dependencies = [ + "zerovec", +] [[package]] name = "powerfmt" @@ -2737,11 +3515,33 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" +[[package]] +name = "pprof" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38a01da47675efa7673b032bf8efd8214f1917d89685e07e395ab125ea42b187" +dependencies = [ + "aligned-vec", + "backtrace", + "cfg-if 1.0.1", + "findshlibs", + "inferno", + "libc", + "log", + "nix", + "once_cell", + "smallvec", + "spin 0.10.0", + "symbolic-demangle", + "tempfile", + "thiserror 2.0.12", +] + [[package]] name = "ppv-lite86" -version = "0.2.20" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ "zerocopy", ] @@ -2752,6 +3552,32 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" +[[package]] +name = "predicates" +version = "3.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d19ee57562043d37e82899fade9a22ebab7be9cef5026b07fda9cdd4293573" +dependencies = [ + "anstyle", + "predicates-core", +] + +[[package]] +name = "predicates-core" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "727e462b119fe9c93fd0eb1429a5f7647394014cf3c04ab2c0350eeb09095ffa" + +[[package]] +name = "predicates-tree" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72dd2d6d381dfb73a193c7fca536518d7caee39fc8503f74e7dc0be0531b425c" +dependencies = [ + "predicates-core", + "termtree", +] + [[package]] name = "prettyplease" version = "0.1.25" @@ -2772,11 +3598,20 @@ dependencies = [ "toml_edit 0.19.15", ] +[[package]] +name = "proc-macro-crate" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" +dependencies = [ + "toml_edit 0.22.27", +] + [[package]] name = "proc-macro2" -version = "1.0.93" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] @@ -2787,7 +3622,7 @@ version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" dependencies = [ - "bytes", + "bytes 1.10.1", "prost-derive", ] @@ -2797,7 +3632,7 @@ version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270" dependencies = [ - "bytes", + "bytes 1.10.1", "heck 0.4.1", "itertools 0.10.5", "lazy_static", @@ -2841,15 +3676,52 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9318ead08c799aad12a55a3e78b82e0b6167271ffd1f627b758891282f739187" +[[package]] +name = "quick-xml" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f50b1c63b38611e7d4d7f68b82d3ad0cc71a2ad2e7f61fc10f1328d917c93cd" +dependencies = [ + "memchr", +] + +[[package]] +name = "quick-xml" +version = "0.37.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" +dependencies = [ + "memchr", +] + [[package]] name = "quote" -version = "1.0.38" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", +] + [[package]] name = "rand" version = "0.8.5" @@ -2857,8 +3729,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", ] [[package]] @@ -2868,7 +3750,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", ] [[package]] @@ -2877,7 +3768,16 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.16", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", ] [[package]] @@ -2886,7 +3786,7 @@ version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b2a9fe2d7d9eeaf3279d1780452a5bbd26b31b27938787ef1c3e930d1e9cfbd" dependencies = [ - "rand", + "rand 0.8.5", "regex-syntax 0.6.29", ] @@ -2912,11 +3812,11 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.8" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" +checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.1", ] [[package]] @@ -2925,9 +3825,9 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.16", "libredox", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -2979,14 +3879,14 @@ checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" dependencies = [ "async-compression", "base64 0.21.7", - "bytes", + "bytes 1.10.1", "encoding_rs", "futures-core", "futures-util", - "h2", - "http", - "http-body", - "hyper", + "h2 0.3.26", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.27", "hyper-rustls", "ipnet", "js-sys", @@ -3015,15 +3915,24 @@ dependencies = [ "winreg", ] +[[package]] +name = "rgb" +version = "0.8.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a" +dependencies = [ + "bytemuck", +] + [[package]] name = "ring" -version = "0.17.9" +version = "0.17.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e75ec5e92c4d8aede845126adc388046234541629e76029599ed35a003c7ed24" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", - "cfg-if", - "getrandom 0.2.15", + "cfg-if 1.0.1", + "getrandom 0.2.16", "libc", "untrusted", "windows-sys 0.52.0", @@ -3040,11 +3949,16 @@ dependencies = [ "futures", "futures-channel", "futures-util", - "jsonrpsee", + "jaq-core", + "jaq-interpret", + "jaq-parse", + "jaq-std", + "jsonrpsee 0.16.3", "lazy_static", "libloading", "log", "mock_app_gw", + "mockall", "regex", "ripple_sdk", "rstest", @@ -3070,6 +3984,12 @@ dependencies = [ "serde_json", ] +[[package]] +name = "route-recognizer" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afab94fb28594581f62d981211a9a4d53cc8130bbcbbb89a0440d9b8e81a7746" + [[package]] name = "rstest" version = "0.18.2" @@ -3088,22 +4008,22 @@ version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d428f8247852f894ee1be110b375111b586d4fa431f6c46e64ba5a0dcccbe605" dependencies = [ - "cfg-if", + "cfg-if 1.0.1", "glob", "proc-macro2", "quote", "regex", "relative-path", "rustc_version", - "syn 2.0.98", + "syn 2.0.104", "unicode-ident", ] [[package]] name = "rustc-demangle" -version = "0.1.24" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" [[package]] name = "rustc-hash" @@ -3111,6 +4031,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + [[package]] name = "rustc_version" version = "0.4.1" @@ -3126,7 +4052,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.1", "errno", "libc", "linux-raw-sys 0.4.15", @@ -3139,7 +4065,7 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.1", "errno", "libc", "linux-raw-sys 0.9.4", @@ -3191,15 +4117,15 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.19" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" +checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" [[package]] name = "ryu" -version = "1.0.19" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "same-file" @@ -3212,9 +4138,9 @@ dependencies = [ [[package]] name = "scc" -version = "2.3.3" +version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea091f6cac2595aa38993f04f4ee692ed43757035c36e67c180b6828356385b1" +checksum = "22b2d775fb28f245817589471dd49c5edf64237f4a19d10ce9a92ff4651a27f4" dependencies = [ "sdd", ] @@ -3265,7 +4191,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.1", "core-foundation", "core-foundation-sys", "libc", @@ -3284,35 +4210,35 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.25" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" [[package]] name = "serde" -version = "1.0.218" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.218" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.104", ] [[package]] name = "serde_json" -version = "1.0.139" +version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44f86c3acccc9c65b153fe1b85a3be07fe5515274ec9f0653b4a0875731c72a6" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ "itoa", "memchr", @@ -3341,9 +4267,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" dependencies = [ "serde", ] @@ -3366,7 +4292,7 @@ version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.7.1", + "indexmap 2.10.0", "itoa", "ryu", "serde", @@ -3395,7 +4321,19 @@ checksum = "5d69265a08751de7844521fd15003ae0a888e035773ba05695c5c759a6f89eef" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.104", +] + +[[package]] +name = "sha-1" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" +dependencies = [ + "block-buffer 0.7.3", + "digest 0.8.1", + "fake-simd", + "opaque-debug 0.2.3", ] [[package]] @@ -3405,10 +4343,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" dependencies = [ "block-buffer 0.9.0", - "cfg-if", + "cfg-if 1.0.1", "cpufeatures", "digest 0.9.0", - "opaque-debug", + "opaque-debug 0.3.1", ] [[package]] @@ -3417,7 +4355,7 @@ version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ - "cfg-if", + "cfg-if 1.0.1", "cpufeatures", "digest 0.10.7", ] @@ -3430,11 +4368,11 @@ checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" [[package]] name = "sha2" -version = "0.10.8" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ - "cfg-if", + "cfg-if 1.0.1", "cpufeatures", "digest 0.10.7", ] @@ -3447,9 +4385,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.2" +version = "1.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" dependencies = [ "libc", ] @@ -3468,18 +4406,15 @@ checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" [[package]] name = "slab" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] +checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" [[package]] name = "smallvec" -version = "1.14.0" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "socket2" @@ -3488,14 +4423,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" dependencies = [ "libc", - "winapi", + "winapi 0.3.9", ] [[package]] name = "socket2" -version = "0.5.8" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" dependencies = [ "libc", "windows-sys 0.52.0", @@ -3508,20 +4443,108 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41d1c5305e39e09653383c2c7244f2f78b3bcae37cf50c64cb4789c9f5096ec2" dependencies = [ "base64 0.13.1", - "bytes", + "bytes 1.10.1", "futures", - "http", + "http 0.2.12", "httparse", "log", - "rand", - "sha-1", + "rand 0.8.5", + "sha-1 0.9.8", +] + +[[package]] +name = "soketto" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e859df029d160cb88608f5d7df7fb4753fd20fdfb4de5644f3d8b8440841721" +dependencies = [ + "base64 0.22.1", + "bytes 1.10.1", + "futures", + "http 1.3.1", + "httparse", + "log", + "rand 0.8.5", + "sha1", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "spin" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591" +dependencies = [ + "lock_api", +] + +[[package]] +name = "ssda_client" +version = "0.1.0" +dependencies = [ + "jsonrpsee 0.24.9", + "log", + "ripple_sdk", + "serde", + "ssda_types", + "tokio", + "url", + "ws", +] + +[[package]] +name = "ssda_service" +version = "0.1.0" +dependencies = [ + "async-trait", + "env_logger", + "futures-util", + "http 0.2.12", + "log", + "mockall", + "ripple_sdk", + "serde_json", + "ssda_types", + "tokio", + "tokio-tungstenite", + "url", +] + +[[package]] +name = "ssda_types" +version = "0.1.0" +dependencies = [ + "async-trait", + "env_logger", + "futures-util", + "http 0.2.12", + "jsonrpsee 0.24.9", + "mockall", + "pprof", + "ripple_sdk", + "serde", + "serde_json", + "tokio", + "tokio-tungstenite", + "url", ] [[package]] -name = "spin" -version = "0.9.8" +name = "stable_deref_trait" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "str_stack" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9091b6114800a5f2141aee1d1b9d6ca3592ac062dc5decb3764ec5895a47b4eb" [[package]] name = "string_cache" @@ -3543,11 +4566,11 @@ checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" [[package]] name = "strum" -version = "0.26.3" +version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +checksum = "f64def088c51c9510a8579e3c5d67c65349dcf755e5479ad3d010aa6454e2c32" dependencies = [ - "strum_macros 0.26.4", + "strum_macros 0.27.1", ] [[package]] @@ -3565,15 +4588,15 @@ dependencies = [ [[package]] name = "strum_macros" -version = "0.26.4" +version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +checksum = "c77a8c5abcaf0f9ce05d62342b7d298c346515365c36b673df4ebe3ced01fde8" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", "rustversion", - "syn 2.0.98", + "syn 2.0.104", ] [[package]] @@ -3592,6 +4615,29 @@ dependencies = [ "typed-arena", ] +[[package]] +name = "symbolic-common" +version = "12.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a1150bdda9314f6cfeeea801c23f5593c6e6a6c72e64f67e48d723a12b8efdb" +dependencies = [ + "debugid", + "memmap2", + "stable_deref_trait", + "uuid", +] + +[[package]] +name = "symbolic-demangle" +version = "12.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f66537def48fbc704a92e4fdaab7833bc7cb2255faca8182592fb5fa617eb82" +dependencies = [ + "cpp_demangle", + "rustc-demangle", + "symbolic-common", +] + [[package]] name = "syn" version = "1.0.109" @@ -3605,9 +4651,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.98" +version = "2.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" dependencies = [ "proc-macro2", "quote", @@ -3620,19 +4666,30 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + [[package]] name = "sysinfo" version = "0.29.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd727fc423c2060f6c92d9534cef765c65a6ed3f428a03d7def74a8c4348e666" dependencies = [ - "cfg-if", + "cfg-if 1.0.1", "core-foundation-sys", "libc", "ntapi", "once_cell", "rayon", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -3641,7 +4698,7 @@ version = "0.30.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a5b4ddaee55fb2bea2bf0e5000747e5f5c0de765e5a5ff87f4cd106439f4bb3" dependencies = [ - "cfg-if", + "cfg-if 1.0.1", "core-foundation-sys", "libc", "ntapi", @@ -3673,9 +4730,9 @@ dependencies = [ [[package]] name = "tar" -version = "0.4.43" +version = "0.4.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c65998313f8e17d0d553d28f91a0df93e4dbbbf770279c7bc21ca0f09ea1a1f6" +checksum = "1d863878d212c87a19c1a610eb53bb01fe12951c0501cf5a0d65f724914a667a" dependencies = [ "filetime", "libc", @@ -3684,15 +4741,14 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.17.1" +version = "3.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e5a0acb1f3f55f65cc4a866c361b2fb2a0ff6366785ae6fbb5f85df07ba230" +checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" dependencies = [ - "cfg-if", "fastrand", - "getrandom 0.3.1", + "getrandom 0.3.3", "once_cell", - "rustix 0.38.44", + "rustix 1.0.7", "windows-sys 0.59.0", ] @@ -3704,9 +4760,15 @@ checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" dependencies = [ "dirs-next", "rustversion", - "winapi", + "winapi 0.3.9", ] +[[package]] +name = "termtree" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683" + [[package]] name = "test-log" version = "0.2.11" @@ -3733,7 +4795,16 @@ version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl 2.0.12", ] [[package]] @@ -3744,7 +4815,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.104", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", ] [[package]] @@ -3752,13 +4834,14 @@ name = "thunder_ripple_sdk" version = "1.1.0" dependencies = [ "base64 0.22.1", + "base64ct", "csv", "expectest", "futures", "futures-channel", "futures-util", "home", - "jsonrpsee", + "jsonrpsee 0.16.3", "maplit", "pact_consumer", "regex", @@ -3777,11 +4860,12 @@ dependencies = [ [[package]] name = "time" -version = "0.3.37" +version = "0.3.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" +checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" dependencies = [ "deranged", + "itoa", "num-conv", "powerfmt", "serde", @@ -3791,15 +4875,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.2" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" [[package]] name = "time-macros" -version = "0.2.19" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" +checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" dependencies = [ "num-conv", "time-core", @@ -3815,34 +4899,29 @@ dependencies = [ ] [[package]] -name = "tinyvec" -version = "1.8.1" +name = "tinystr" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "022db8904dfa342efe721985167e9fcd16c29b226db4397ed752a761cfce81e8" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" dependencies = [ - "tinyvec_macros", + "displaydoc", + "zerovec", ] -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - [[package]] name = "tokio" -version = "1.44.1" +version = "1.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f382da615b842244d4b8738c82ed1275e6c5dd90c459a30941cd07080b06c91a" +checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779" dependencies = [ "backtrace", - "bytes", + "bytes 1.10.1", "libc", - "mio", + "mio 1.0.4", "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.5.8", + "socket2 0.5.10", "tokio-macros", "windows-sys 0.52.0", ] @@ -3865,7 +4944,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.104", ] [[package]] @@ -3887,6 +4966,7 @@ dependencies = [ "futures-core", "pin-project-lite", "tokio", + "tokio-util", ] [[package]] @@ -3903,11 +4983,11 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.13" +version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" +checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" dependencies = [ - "bytes", + "bytes 1.10.1", "futures-core", "futures-io", "futures-sink", @@ -3917,21 +4997,21 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.20" +version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.24", + "toml_edit 0.22.27", ] [[package]] name = "toml_datetime" -version = "0.6.8" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" dependencies = [ "serde", ] @@ -3942,24 +5022,31 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.7.1", + "indexmap 2.10.0", "toml_datetime", "winnow 0.5.40", ] [[package]] name = "toml_edit" -version = "0.22.24" +version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ - "indexmap 2.7.1", + "indexmap 2.10.0", "serde", "serde_spanned", "toml_datetime", - "winnow 0.7.3", + "toml_write", + "winnow 0.7.11", ] +[[package]] +name = "toml_write" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" + [[package]] name = "tonic" version = "0.9.2" @@ -3969,13 +5056,13 @@ dependencies = [ "async-trait", "axum", "base64 0.21.7", - "bytes", + "bytes 1.10.1", "futures-core", "futures-util", - "h2", - "http", - "http-body", - "hyper", + "h2 0.3.26", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.27", "hyper-timeout", "percent-encoding", "pin-project", @@ -4012,7 +5099,7 @@ dependencies = [ "indexmap 1.9.3", "pin-project", "pin-project-lite", - "rand", + "rand 0.8.5", "slab", "tokio", "tokio-util", @@ -4047,20 +5134,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.28" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.104", ] [[package]] name = "tracing-core" -version = "0.1.33" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" dependencies = [ "once_cell", "valuable", @@ -4093,14 +5180,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9" dependencies = [ "byteorder", - "bytes", + "bytes 1.10.1", "data-encoding", - "http", + "http 0.2.12", "httparse", "log", - "rand", + "rand 0.8.5", "sha1", - "thiserror", + "thiserror 1.0.69", "url", "utf-8", ] @@ -4117,26 +5204,11 @@ version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" -[[package]] -name = "unicode-bidi" -version = "0.3.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" - [[package]] name = "unicode-ident" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe" - -[[package]] -name = "unicode-normalization" -version = "0.1.24" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" -dependencies = [ - "tinyvec", -] +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "unicode-width" @@ -4146,9 +5218,9 @@ checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "unicode-width" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" +checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c" [[package]] name = "unicode-xid" @@ -4156,6 +5228,12 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +[[package]] +name = "unit-prefix" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "323402cff2dd658f39ca17c789b502021b3f18707c91cdf22e3838e1b4023817" + [[package]] name = "unsafe-libyaml" version = "0.2.11" @@ -4170,9 +5248,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.0" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", "idna", @@ -4191,15 +5269,29 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "uuid" -version = "1.13.2" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c1f41ffb7cf259f1ecc2876861a17e7142e63ead296f671f81f6ae85903e0d6" +checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d" dependencies = [ - "getrandom 0.3.1", + "getrandom 0.3.3", + "js-sys", "serde", "sha1_smol", + "wasm-bindgen", ] [[package]] @@ -4210,9 +5302,9 @@ checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" [[package]] name = "value-bag" -version = "1.10.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ef4c4aa54d5d05a279399bfa921ec387b7aba77caf7a682ae8d86785b8fdad2" +checksum = "943ce29a8a743eb10d6082545d861b24f9d1b160b7d741e0f2cdf726bec909c5" [[package]] name = "vergen" @@ -4252,15 +5344,21 @@ dependencies = [ [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasi" -version = "0.13.3+wasi-0.2.2" +version = "0.14.2+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" dependencies = [ "wit-bindgen-rt", ] @@ -4271,7 +5369,7 @@ version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ - "cfg-if", + "cfg-if 1.0.1", "once_cell", "rustversion", "wasm-bindgen-macro", @@ -4287,7 +5385,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.104", "wasm-bindgen-shared", ] @@ -4297,7 +5395,7 @@ version = "0.4.50" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" dependencies = [ - "cfg-if", + "cfg-if 1.0.1", "js-sys", "once_cell", "wasm-bindgen", @@ -4322,7 +5420,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.104", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -4387,6 +5485,12 @@ dependencies = [ "rustix 0.38.44", ] +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" + [[package]] name = "winapi" version = "0.3.9" @@ -4397,6 +5501,12 @@ dependencies = [ "winapi-x86_64-pc-windows-gnu", ] +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" + [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" @@ -4424,7 +5534,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" dependencies = [ - "windows-core", + "windows-core 0.52.0", "windows-targets 0.52.6", ] @@ -4437,6 +5547,65 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-core" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -4464,6 +5633,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.2", +] + [[package]] name = "windows-targets" version = "0.48.5" @@ -4488,13 +5666,29 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", + "windows_i686_gnullvm 0.52.6", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows-targets" +version = "0.53.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef" +dependencies = [ + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -4507,6 +5701,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -4519,6 +5719,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -4531,12 +5737,24 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -4549,6 +5767,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" @@ -4561,6 +5785,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" @@ -4573,6 +5803,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -4585,6 +5821,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + [[package]] name = "winnow" version = "0.5.40" @@ -4596,9 +5838,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.3" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7f4ea97f6f78012141bcdb6a216b2609f0979ada50b20ca5b52dde2eac2bb1" +checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd" dependencies = [ "memchr", ] @@ -4609,28 +5851,61 @@ version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ - "cfg-if", + "cfg-if 1.0.1", "windows-sys 0.48.0", ] [[package]] name = "wit-bindgen-rt" -version = "0.33.0" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags 2.9.1", +] + +[[package]] +name = "writeable" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" + +[[package]] +name = "ws" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25fe90c75f236a0a00247d5900226aea4f2d7b05ccc34da9e7a8880ff59b5848" +dependencies = [ + "byteorder", + "bytes 0.4.12", + "httparse", + "log", + "mio 0.6.23", + "mio-extras", + "rand 0.7.3", + "sha-1 0.8.2", + "slab", + "url", +] + +[[package]] +name = "ws2_32-sys" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" dependencies = [ - "bitflags 2.8.0", + "winapi 0.2.8", + "winapi-build", ] [[package]] name = "xattr" -version = "1.4.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e105d177a3871454f754b33bb0ee637ecaaac997446375fd3e5d43a2ed00c909" +checksum = "af3a19837351dc82ba89f8a125e22a3c475f05aba604acc023d62b2739ae2909" dependencies = [ "libc", - "linux-raw-sys 0.4.15", - "rustix 0.38.44", + "rustix 1.0.7", ] [[package]] @@ -4639,25 +5914,102 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" +[[package]] +name = "yoke" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", + "synstructure", +] + [[package]] name = "zerocopy" -version = "0.7.35" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" dependencies = [ - "byteorder", "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.35" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", + "synstructure", +] + +[[package]] +name = "zerotrie" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.104", ] [[package]] @@ -4701,9 +6053,9 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.13+zstd.1.5.6" +version = "2.0.15+zstd.1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" +checksum = "eb81183ddd97d0c74cedf1d50d85c8d08c1b8b68ee863bdee9e706eedba1a237" dependencies = [ "cc", "pkg-config", diff --git a/Cargo.toml b/Cargo.toml index 5dd5b9016..c3635f69c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,12 +22,16 @@ members = [ "device/thunder_ripple_sdk", "core/main", "device/mock_device", - "openrpc_validator"] + "openrpc_validator", + "ssda/ssda_types", + "ssda/ssda_client", + "ssda/ssda_service"] [workspace.package] version = "1.1.0" [workspace.dependencies] +base64ct= {version = "=1.7.3"} base64 = { version = "0.22.1", default-features = false, features=["alloc"] } chrono = { version = "0.4", default-features = false, features = ["clock"] } futures = { version = "0.3.21", default-features = false } @@ -39,7 +43,7 @@ serde_json = { version = "1.0", default-features = false} serde = { version = "1.0", features = ["derive"], default-features = false } tokio = { version = "1.44.1", default-features = false } tokio-tungstenite = { version = "0.20.1", default-features = false } -url = { version = "=2.5.0", default-features = false} +url = { version = "=2.5.4", default-features = false} urlencoding = { version = "2.1.0", default-features = false} uuid = { version = "1.13.1", default-features = false } ripple_sdk = { path = "./core/sdk" } diff --git a/core/main/Cargo.toml b/core/main/Cargo.toml index 0035baf14..34bfd516b 100644 --- a/core/main/Cargo.toml +++ b/core/main/Cargo.toml @@ -43,9 +43,12 @@ contract_tests = [ "websocket_contract_tests", ] tdk=[] +ssda=[] [dependencies] base64.workspace = true -ripple_sdk = { workspace = true, features = ["full"] } +ripple_sdk = { path = "../sdk" } + + jsonrpsee = { workspace = true, features = ["macros"] } futures-channel.workspace = true futures.workspace = true @@ -71,6 +74,10 @@ strum_macros = "0.24" openrpc_validator = { path = "../../openrpc_validator" } proc-macro2.workspace = true +ssda_service = { path="../../ssda/ssda_service" } +ssda_types= { path = "../../ssda/ssda_types"} + + [build-dependencies] vergen = "1" @@ -81,4 +88,4 @@ rstest = "0.18.0" # serial_test is used to provide determinism around monotonic counter generation # using AtomicU64 serial_test = "3" -httpmock = "0.7.0" \ No newline at end of file +httpmock = "0.7.0" diff --git a/core/main/src/bootstrap/start_communication_broker.rs b/core/main/src/bootstrap/start_communication_broker.rs index fd8e03c06..024485334 100644 --- a/core/main/src/bootstrap/start_communication_broker.rs +++ b/core/main/src/bootstrap/start_communication_broker.rs @@ -46,7 +46,7 @@ impl Bootstep for StartCommunicationBroker { } // Setup the endpoints from the manifests let mut endpoint_state = ps.clone().endpoint_state; - endpoint_state.build_thunder_endpoint(Some(state.platform_state.clone())); + endpoint_state.build_thunder_endpoint().await; Ok(()) } } @@ -67,7 +67,9 @@ impl Bootstep for StartOtherBrokers { } // Setup the endpoints from the manifests let mut endpoint_state = ps.clone().endpoint_state; - endpoint_state.build_other_endpoints(ps.clone(), ps.session_state.get_account_session()); + endpoint_state + .build_other_endpoints(ps.clone(), ps.session_state.get_account_session()) + .await; Ok(()) } } diff --git a/core/main/src/bootstrap/start_ws_step.rs b/core/main/src/bootstrap/start_ws_step.rs index 512c53a89..745ef752b 100644 --- a/core/main/src/bootstrap/start_ws_step.rs +++ b/core/main/src/bootstrap/start_ws_step.rs @@ -37,19 +37,38 @@ impl Bootstep for StartWsStep { let ws_enabled = manifest.get_web_socket_enabled(); let internal_ws_enabled = manifest.get_internal_ws_enabled(); let iai_c = iai.clone(); + if ws_enabled { + let api_gateway_state_ws = state.platform_state.services_gateway_api.clone(); let ws_addr = manifest.get_ws_gateway_host(); let state_for_ws = state.platform_state.clone(); + tokio::spawn(async move { - FireboltWs::start(ws_addr.as_str(), state_for_ws, true, iai.clone()).await; + FireboltWs::start( + ws_addr.as_str(), + state_for_ws, + true, + iai.clone(), + api_gateway_state_ws, + ) + .await; }); } if internal_ws_enabled { + let api_gateway_state_internal_ws = state.platform_state.services_gateway_api.clone(); + let ws_addr = manifest.get_internal_gateway_host(); let state_for_ws = state.platform_state; tokio::spawn(async move { - FireboltWs::start(ws_addr.as_str(), state_for_ws, false, iai_c).await; + FireboltWs::start( + ws_addr.as_str(), + state_for_ws, + false, + iai_c, + api_gateway_state_internal_ws, + ) + .await; }); } diff --git a/core/main/src/broker/broker_utils.rs b/core/main/src/broker/broker_utils.rs index 8cc794daa..5fd863cc1 100644 --- a/core/main/src/broker/broker_utils.rs +++ b/core/main/src/broker/broker_utils.rs @@ -85,13 +85,9 @@ impl BrokerUtils { if let Some(app_id) = app_id { rpc_request.ctx.app_id = app_id.to_owned(); } - state.endpoint_state.handle_brokerage( - rpc_request, - None, - callback, - Vec::new(), - None, - Vec::new(), - ) + state + .endpoint_state + .handle_brokerage(rpc_request, None, callback, Vec::new(), None, Vec::new()) + .await } } diff --git a/core/main/src/broker/endpoint_broker.rs b/core/main/src/broker/endpoint_broker.rs index 1a1b7067e..9020619b2 100644 --- a/core/main/src/broker/endpoint_broker.rs +++ b/core/main/src/broker/endpoint_broker.rs @@ -15,6 +15,8 @@ // SPDX-License-Identifier: Apache-2.0 // +use ripple_sdk::api::rules_engine::EventHandler; +use ripple_sdk::tokio::sync::RwLock as TokioRwLock; use ripple_sdk::{ api::{ firebolt::fb_capabilities::{ @@ -25,28 +27,28 @@ use ripple_sdk::{ RpcRequest, RPC_V2, }, observability::log_signal::LogSignal, + rules_engine::{ + jq_compile, Rule, RuleEndpoint, RuleEndpointProtocol, RuleEngine, RuleEngineProvider, + RuleRetrievalError, RuleRetrieved, RuleTransformType, RuleType, + }, session::AccountSession, }, extn::extn_client_message::{ExtnEvent, ExtnMessage}, framework::RippleResponse, log::{debug, error, info, trace}, - service::service_message::{ - Id as ServiceMessageId, JsonRpcMessage as ServiceJsonRpcMessage, - JsonRpcSuccess as ServiceJsonRpcSuccess, ServiceMessage, - }, tokio::{ self, sync::mpsc::{self, Receiver, Sender}, }, - tokio_tungstenite::tungstenite::Message, utils::error::RippleError, }; use serde_json::{json, Value}; +use std::sync::RwLock; use std::{ collections::HashMap, sync::{ atomic::{AtomicU64, Ordering}, - Arc, RwLock, + Arc, }, }; @@ -67,11 +69,6 @@ use super::{ extn_broker::ExtnBroker, http_broker::HttpBroker, provider_broker_state::{ProvideBrokerState, ProviderResult}, - rules::rules_engine::{ - jq_compile, EventHandler, Rule, RuleEndpoint, RuleEndpointProtocol, RuleEngine, - RuleRetrievalError, RuleRetrieved, RuleType, - }, - service_broker::ServiceBroker, thunder_broker::ThunderBroker, websocket_broker::WebsocketBroker, workflow_broker::WorkflowBroker, @@ -369,18 +366,23 @@ impl BrokerSender { } } -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct EndpointBrokerState { endpoint_map: Arc>>, callback: BrokerCallback, request_map: Arc>>, extension_request_map: Arc>>, - rule_engine: Arc>, + rule_engine: Arc>>, cleaner_list: Arc>>, reconnect_tx: Sender, provider_broker_state: ProvideBrokerState, metrics_state: OpMetricState, } +impl std::fmt::Debug for EndpointBrokerState { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "EndpointBrokerState {{}}") + } +} #[derive(Debug)] pub enum HandleBrokerageError { @@ -393,11 +395,17 @@ impl From for HandleBrokerageError { fn from(value: RuleRetrievalError) -> Self { match value { RuleRetrievalError::RuleNotFound(e) => HandleBrokerageError::RuleNotFound(e), - RuleRetrievalError::RuleNotFoundAsWildcard => { - HandleBrokerageError::RuleNotFound("Rule Not found as wildcard".to_string()) + RuleRetrievalError::RuleNotFoundAsWildcard(method) => { + HandleBrokerageError::RuleNotFound(format!( + "Rule Not found as wildcard for method {}", + method + )) } - RuleRetrievalError::TooManyWildcardMatches => { - HandleBrokerageError::RuleNotFound("Too many wildcard matches".to_string()) + RuleRetrievalError::TooManyWildcardMatches(method) => { + HandleBrokerageError::RuleNotFound(format!( + "Too many wildcard matches for method {}", + method + )) } } } @@ -466,7 +474,7 @@ impl Default for EndpointBrokerState { callback: BrokerCallback::default(), request_map: Arc::new(RwLock::new(HashMap::new())), extension_request_map: Arc::new(RwLock::new(HashMap::new())), - rule_engine: Arc::new(RwLock::new(RuleEngine::default())), + rule_engine: Arc::new(TokioRwLock::new(Box::new(RuleEngine::default()))), cleaner_list: Arc::new(RwLock::new(Vec::new())), reconnect_tx: mpsc::channel(2).0, provider_broker_state: ProvideBrokerState::default(), @@ -479,7 +487,7 @@ impl EndpointBrokerState { pub fn new( metrics_state: OpMetricState, tx: Sender, - rule_engine: RuleEngine, + rule_engine: Arc>>, _ripple_client: RippleClient, ) -> Self { let (reconnect_tx, _rec_tr) = mpsc::channel(2); @@ -488,7 +496,7 @@ impl EndpointBrokerState { callback: BrokerCallback { sender: tx }, request_map: Arc::new(RwLock::new(HashMap::new())), extension_request_map: Arc::new(RwLock::new(HashMap::new())), - rule_engine: Arc::new(RwLock::new(rule_engine)), + rule_engine, cleaner_list: Arc::new(RwLock::new(Vec::new())), reconnect_tx, provider_broker_state: ProvideBrokerState::default(), @@ -499,16 +507,20 @@ impl EndpointBrokerState { state.reconnect_thread(_rec_tr, _ripple_client); state } - pub fn with_rules_engine(mut self, rule_engine: Arc>) -> Self { + pub fn with_rules_engine( + mut self, + rule_engine: Arc>>, + ) -> Self { self.rule_engine = rule_engine; self } - pub fn add_rule(self, rule: Rule) -> Self { - self.rule_engine.write().unwrap().add_rule(rule); + pub async fn add_rule(self, rule: Rule) -> Self { + let rule_engine = self.rule_engine.clone(); + rule_engine.write().await.add_rule(rule); self } - pub fn has_rule(&self, rule: &str) -> bool { - self.rule_engine.read().unwrap().has_rule(rule) + pub async fn has_rule(&self, rule: &str) -> bool { + self.rule_engine.read().await.has_rule(rule) } #[cfg(not(test))] fn reconnect_thread(&self, mut rx: Receiver, client: RippleClient) { @@ -583,12 +595,16 @@ impl EndpointBrokerState { #[allow(dead_code)] fn apply_request_rule(rpc_request: &BrokerRequest) -> Result { if let Ok(mut params) = serde_json::from_str::>(&rpc_request.rpc.params_json) { - let last = params.pop().unwrap_or(Value::Null); + let last = if params.len() > 1 { + params.pop().unwrap() + } else { + Value::Null + }; if let Some(filter) = rpc_request .rule .transform - .get_transform_data(super::rules::rules_engine::RuleTransformType::Request) + .get_transform_data(RuleTransformType::Request) { let transformed_request_res = jq_compile( last, @@ -625,6 +641,49 @@ impl EndpointBrokerState { .emit_error(); Err(RippleError::ParseError) } + /// Adds BrokerContext to a given request used by the Broker Implementations + /// just before sending the data through the protocol + /// + fn _update_request( + &self, + rpc_request: &RpcRequest, + rule: Rule, + extn_message: Option, + workflow_callback: Option, + telemetry_response_listeners: Vec>, + ) -> (u64, BrokerRequest) { + let id = Self::get_next_id(); + let mut rpc_request_c = rpc_request.clone(); + { + let mut request_map = self.request_map.write().unwrap(); + let _ = request_map.insert( + id, + BrokerRequest { + rpc: rpc_request.clone(), + rule: rule.clone(), + subscription_processed: None, + workflow_callback: workflow_callback.clone(), + telemetry_response_listeners: telemetry_response_listeners.clone(), + }, + ); + } + + if extn_message.is_some() { + let mut extn_map = self.extension_request_map.write().unwrap(); + let _ = extn_map.insert(id, extn_message.unwrap()); + } + + rpc_request_c.ctx.call_id = id; + ( + id, + BrokerRequest::new( + &rpc_request_c, + rule, + workflow_callback, + telemetry_response_listeners, + ), + ) + } pub fn update_request( &self, @@ -664,29 +723,25 @@ impl EndpointBrokerState { telemetry_response_listeners, ) } - pub fn build_thunder_endpoint(&mut self, ps: Option) { - let endpoint = { - self.rule_engine - .write() - .unwrap() - .rules - .endpoints - .get("thunder") - .cloned() - }; - if let Some(endpoint) = endpoint { + pub async fn build_thunder_endpoint(&mut self) { + let rules = self.rule_engine.read().await.get_rules().clone(); + if let Some(endpoint) = rules.endpoints.get("thunder").cloned() { let request = BrokerConnectRequest::new( "thunder".to_owned(), endpoint.clone(), self.reconnect_tx.clone(), ); - self.build_endpoint(ps, request); + self.build_endpoint(None, request); } } - pub fn build_other_endpoints(&mut self, ps: PlatformState, session: Option) { - let endpoints = self.rule_engine.read().unwrap().rules.endpoints.clone(); - for (key, endpoint) in endpoints { + pub async fn build_other_endpoints( + &mut self, + ps: PlatformState, + session: Option, + ) { + let rules = self.rule_engine.read().await.get_rules().endpoints.clone(); + for (key, endpoint) in rules { // skip thunder endpoint as it is already built using build_thunder_endpoint if let RuleEndpointProtocol::Thunder = endpoint.protocol { continue; @@ -715,6 +770,7 @@ impl EndpointBrokerState { fn build_endpoint(&mut self, ps: Option, request: BrokerConnectRequest) { let endpoint = request.endpoint.clone(); let key = request.key.clone(); + info!("Building endpoint {:?} using key {}", endpoint, key); let (broker, cleaner) = match endpoint.protocol { RuleEndpointProtocol::Http => ( HttpBroker::get_broker(None, request, self.callback.clone(), self).get_sender(), @@ -727,7 +783,7 @@ impl EndpointBrokerState { } RuleEndpointProtocol::Thunder => { let thunder_broker = - ThunderBroker::get_broker(ps, request, self.callback.clone(), self); + ThunderBroker::get_broker(None, request, self.callback.clone(), self); ( thunder_broker.get_sender(), Some(thunder_broker.get_cleaner()), @@ -741,10 +797,16 @@ impl EndpointBrokerState { ExtnBroker::get_broker(ps, request, self.callback.clone(), self).get_sender(), None, ), - RuleEndpointProtocol::Service => ( - ServiceBroker::get_broker(ps, request, self.callback.clone(), self).get_sender(), - None, - ), + RuleEndpointProtocol::Service => { + self.get_service_broker(ps, request) + + // let service_broker = + // SsdaServiceBroker::get_broker(ps, request, self.callback.clone(), self); + // ( + // service_broker.get_sender(), + // Some(service_broker.get_cleaner()), + // ) + } }; self.add_endpoint(key, broker); @@ -753,6 +815,34 @@ impl EndpointBrokerState { cleaner_list.push(cleaner); } } + #[cfg(not(feature = "ssda"))] + fn get_service_broker( + &mut self, + ps: Option, + request: BrokerConnectRequest, + ) -> (BrokerSender, Option) { + use crate::broker::service_broker::ServiceBroker; + let service_broker = ServiceBroker::get_broker(ps, request, self.callback.clone(), self); + ( + service_broker.get_sender(), + Some(service_broker.get_cleaner()), + ) + } + + #[cfg(feature = "ssda")] + fn get_service_broker( + &mut self, + ps: Option, + request: BrokerConnectRequest, + ) -> (BrokerSender, Option) { + use super::ssda_service_broker::SsdaServiceBroker; + let service_broker = + SsdaServiceBroker::get_broker(ps, request, self.callback.clone(), self); + ( + service_broker.get_sender(), + Some(service_broker.get_cleaner()), + ) + } fn handle_static_request(&self, rpc_request: RpcRequest) -> JsonRpcApiResponse { let mut data = JsonRpcApiResponse::default(); @@ -823,19 +913,19 @@ impl EndpointBrokerState { fn get_sender(&self, hash: &str) -> Option { self.endpoint_map.read().unwrap().get(hash).cloned() } - fn get_broker_rule( + async fn get_broker_rule( &self, rpc_request: &RpcRequest, ) -> Result { - self.rule_engine.read().unwrap().get_rule(rpc_request) + self.rule_engine.read().await.get_rule(rpc_request) } - /// Main handler method which checks for brokerage and then sends the request for + /// Main handler method whcih checks for brokerage and then sends the request for /// asynchronous processing - pub fn handle_brokerage( + pub async fn handle_brokerage( &self, rpc_request: RpcRequest, extn_message: Option, - custom_callback: Option, + workflow_callback: Option, permissions: Vec, session: Option, telemetry_response_listeners: Vec>, @@ -845,30 +935,25 @@ impl EndpointBrokerState { "starting brokerage".to_string(), rpc_request.ctx.clone(), ) - .with_diagnostic_context_item("workflow", &custom_callback.is_some().to_string()) + .with_diagnostic_context_item("workflow", &workflow_callback.is_some().to_string()) .emit_debug(); - let resp = self.handle_brokerage_workflow( - rpc_request.clone(), - extn_message, - custom_callback, - permissions, - session, - telemetry_response_listeners, - ); - - if resp.is_err() { - let err = resp.unwrap_err(); - LogSignal::new( - "handle_brokerage".to_string(), - "Rule error".to_string(), - rpc_request.ctx.clone(), + match self + .handle_brokerage_workflow( + rpc_request, + extn_message, + workflow_callback, + permissions, + session, + telemetry_response_listeners, ) - .with_diagnostic_context_item("error", &format!("{:?}", err)) - .emit_error(); - false - } else { - true + .await + { + Ok(_yay) => true, + Err(e) => { + error!("Error in brokerage {:?}", e); + false + } } } @@ -923,12 +1008,12 @@ impl EndpointBrokerState { let rpc_request = broker_request.rpc.clone(); match rule.rule_type() { - super::rules::rules_engine::RuleType::Static => { + RuleType::Static => { let response = RenderedRequest::JsonRpc(self.handle_static_request(rpc_request.clone())); Ok(response) } - super::rules::rules_engine::RuleType::Provider => { + RuleType::Provider => { let response = self.handle_provided_request( &rpc_request, rpc_request.ctx.call_id, @@ -937,7 +1022,7 @@ impl EndpointBrokerState { ); Ok(response) } - super::rules::rules_engine::RuleType::Endpoint => { + RuleType::Endpoint => { if rpc_request.is_unlisten() { Ok(RenderedRequest::Unlisten(broker_request.clone())) } else { @@ -947,7 +1032,7 @@ impl EndpointBrokerState { } } - pub fn handle_brokerage_workflow( + pub async fn handle_brokerage_workflow( &self, rpc_request: RpcRequest, extn_message: Option, @@ -957,7 +1042,7 @@ impl EndpointBrokerState { telemetry_response_listeners: Vec>, ) -> Result { /*if rule not found, "unhandled https://github.com/rdkcentral/Ripple/blob/ae3fcd78b055cf70022959bf827de9ed569762aa/core/main/src/broker/endpoint_broker.rs#L719" */ - let rule: Rule = match self.get_broker_rule(&rpc_request)? { + let rule: Rule = match self.get_broker_rule(&rpc_request).await? { RuleRetrieved::ExactMatch(rule) | RuleRetrieved::WildcardMatch(rule) => rule, }; /* @@ -972,7 +1057,6 @@ impl EndpointBrokerState { ) .with_diagnostic_context_item("rule", &format!("{}", rule)) .with_diagnostic_context_item("endpoint", &format!("{}", endpoint)) - // this is printing non debuggable data .with_diagnostic_context_item("workflow", &workflow_callback.is_some().to_string()) .emit_debug(); /* @@ -996,10 +1080,7 @@ impl EndpointBrokerState { RenderedRequest::JsonRpc(data) => { tokio::spawn(async move { if let Err(err) = broker_callback.sender.try_send(BrokerOutput::new(data)) { - error!( - "Error sending RenderedRequest json rpc response to broker {:?}", - err - ); + error!("Error sending json rpc response to broker {:?}", err); } }); Ok(response) @@ -1054,7 +1135,7 @@ impl EndpointBrokerState { .sender .try_send(BrokerOutput::new(json_rpc_api_response)) { - error!("Error sending RenderedRequest provider json rpc response to broker {:?}", err); + error!("Error sending json rpc response to broker {:?}", err); } }); Ok(response) @@ -1081,6 +1162,13 @@ impl EndpointBrokerState { } } + pub async fn get_rule( + &self, + rpc_request: &RpcRequest, + ) -> Result { + self.rule_engine.read().await.get_rule(rpc_request) + } + // Method to cleanup all subscription on App termination pub async fn cleanup_for_app(&self, app_id: &str) { let cleaners = { self.cleaner_list.read().unwrap().clone() }; @@ -1113,40 +1201,42 @@ pub trait EndpointBroker { /// Adds BrokerContext to a given request used by the Broker Implementations /// just before sending the data through the protocol - fn update_request(broker_request: &BrokerRequest) -> Result { - let v = Self::apply_request_rule(broker_request)?; + fn update_request(rpc_request: &BrokerRequest) -> Result { + let v = Self::apply_request_rule(rpc_request)?; trace!("transformed request {:?}", v); - let id = broker_request.rpc.ctx.call_id; - let method = broker_request.rule.alias.clone(); - let rpc_request_str = if let Value::Null = v { - json!({ + let id = rpc_request.rpc.ctx.call_id; + let method = rpc_request.rule.alias.clone(); + if let Value::Null = v { + Ok(json!({ "jsonrpc": "2.0", "id": id, "method": method }) - .to_string() + .to_string()) } else { - json!({ + Ok(json!({ "jsonrpc": "2.0", "id": id, "method": method, "params": v }) - .to_string() - }; - - Ok(rpc_request_str) + .to_string()) + } } /// Generic method which takes the given parameters from RPC request and adds rules using rule engine fn apply_request_rule(rpc_request: &BrokerRequest) -> Result { if let Ok(mut params) = serde_json::from_str::>(&rpc_request.rpc.params_json) { - let last = params.pop().unwrap_or(Value::Null); + let last = if params.len() > 1 { + params.pop().unwrap() + } else { + Value::Null + }; if let Some(filter) = rpc_request .rule .transform - .get_transform_data(super::rules::rules_engine::RuleTransformType::Request) + .get_transform_data(RuleTransformType::Request) { let transformed_request_res = jq_compile( last, @@ -1231,6 +1321,7 @@ impl BrokerOutputForwarder { let output_c = output.clone(); let mut response = output.data.clone(); let mut is_event = false; + // First validate the id check if it could be an event let id = if let Some(e) = output_c.get_event() { is_event = true; Some(e) @@ -1246,50 +1337,139 @@ impl BrokerOutputForwarder { broker_request.clone(), ) .emit_debug(); - + /* + save off rpc method name for rule context telemetry + */ let rule_context_name = broker_request.rpc.method.clone(); - let workflow_callback = broker_request.workflow_callback.clone(); + + let workflow_callback = broker_request.clone().workflow_callback; let telemetry_response_listeners = - broker_request.telemetry_response_listeners.clone(); + broker_request.clone().telemetry_response_listeners; let sub_processed = broker_request.is_subscription_processed(); let rpc_request = broker_request.rpc.clone(); + let session_id = rpc_request.ctx.get_id(); let is_subscription = rpc_request.is_subscription(); + let mut apply_response_needed = false; + + // Step 1: Create the data + if let Some(result) = response.result.clone() { + LogSignal::new( + "start_forwarder".to_string(), + "processing event".to_string(), + broker_request.clone(), + ) + .emit_debug(); - let apply_response_needed = if let Some(result) = response.result.clone() { if is_event { - LogSignal::new( - "handle_event_output".to_string(), - "processing event".to_string(), - broker_request.clone(), - ) - .emit_debug(); - if Self::handle_event_output( - &broker_request, - &rpc_request, - &mut response, - &platform_state, - &event_utility_clone, - result.clone(), - ) - .await + if let Some(method) = broker_request.rule.event_handler.clone() { + let platform_state_c = platform_state.clone(); + let rpc_request_c = rpc_request.clone(); + let response_c = response.clone(); + let broker_request_c = broker_request.clone(); + + tokio::spawn(Self::handle_event( + platform_state_c, + method, + broker_request_c, + rpc_request_c, + response_c, + )); + + continue; + } + + if let Some(filter) = broker_request + .rule + .transform + .get_transform_data(RuleTransformType::Event( + rpc_request.ctx.context.contains(&RPC_V2.into()), + )) { + apply_rule_for_event( + &broker_request, + &result, + &rpc_request, + &filter, + &mut response, + ); + } + + if !apply_filter(&broker_request, &result, &rpc_request) { continue; } - } else if is_subscription - && Self::handle_subscription_response( - &broker_request, - &rpc_request, - &mut response, - sub_processed, - &platform_state, - id, - ) - { - continue; - } - !is_event && !is_subscription + // check if the request transform has event_decorator_method + if let Some(decorator_method) = + broker_request.rule.transform.event_decorator_method.clone() + { + if let Some(func) = + event_utility_clone.get_function(&decorator_method) + { + // spawn a tokio thread to run the function and continue the main thread. + LogSignal::new( + "start_forwarder".to_string(), + "event decorator method found".to_string(), + rpc_request.ctx.clone(), + ) + .emit_debug(); + let session_id = rpc_request.ctx.get_id(); + let request_id = rpc_request.ctx.call_id; + let protocol = rpc_request.ctx.protocol.clone(); + let platform_state_c = platform_state.clone(); + let ctx = rpc_request.ctx.clone(); + tokio::spawn(async move { + if let Ok(value) = func( + platform_state_c.clone(), + ctx.clone(), + Some(result.clone()), + ) + .await + { + response.result = Some(value.expect("REASON")); + } + response.id = Some(request_id); + + let message = ApiMessage::new( + protocol, + serde_json::to_string(&response).unwrap(), + rpc_request.ctx.request_id.clone(), + ); + + if let Some(session) = platform_state_c + .session_state + .get_session_for_connection_id(&session_id) + { + let _ = session.send_json_rpc(message).await; + } + }); + continue; + } else { + LogSignal::new( + "start_forwarder".to_string(), + "event decorator method not found".to_string(), + rpc_request.ctx.clone(), + ) + .emit_debug(); + error!( + "Failed to invoke decorator method {:?}", + decorator_method + ); + } + } + } else if is_subscription { + if sub_processed { + continue; + } + response.result = Some(json!({ + "listening" : rpc_request.is_listening(), + "event" : rpc_request.ctx.method + })); + platform_state.endpoint_state.update_unsubscribe_request(id); + } else { + apply_response_needed = true; + } } else { + trace!("start_forwarder: no result {:?}", response); LogSignal::new( "start_forwarder".to_string(), "no result".to_string(), @@ -1297,30 +1477,126 @@ impl BrokerOutputForwarder { ) .with_diagnostic_context_item("response", response.to_string().as_str()) .emit_debug(); - true - }; + apply_response_needed = true; + } if apply_response_needed { - Self::apply_response_transform( - &broker_request, - &output_c, - &mut response, - &rule_context_name, - ); + // Apply response rule using params if there is any; otherwise, apply response rule using main broker request's response rule + let mut apply_response_using_main_req_needed = true; + if let Some(params) = output.data.params { + if let Some(param) = params.as_object() { + for (key, value) in param { + if key == "response" { + if let Some(filter) = value.as_str() { + apply_response_using_main_req_needed = false; + apply_response( + filter.to_string(), + &rpc_request.ctx.method, + &mut response, + ); + } + } + } + } + } + if apply_response_using_main_req_needed { + if let Some(filter) = broker_request + .rule + .transform + .get_transform_data(RuleTransformType::Response) + { + apply_response(filter, &rule_context_name, &mut response); + } else if response.result.is_none() && response.error.is_none() { + response.result = Some(Value::Null); + } + } } - response.id = Some(rpc_request.ctx.call_id); + let request_id = rpc_request.ctx.call_id; + response.id = Some(request_id); - Self::forward_response( - response, - &rpc_request, - &mut platform_state, - is_event, - id, - workflow_callback, - telemetry_response_listeners, - ) - .await; + if let Some(workflow_callback) = workflow_callback { + debug!("sending to workflow callback {:?}", response); + LogSignal::new( + "start_forwarder".to_string(), + "sending to workflow callback".to_string(), + rpc_request.ctx.clone(), + ) + .emit_debug(); + let _ = workflow_callback + .sender + .try_send(BrokerOutput::new(response.clone())); + } else { + let tm_str = get_rpc_header(&rpc_request); + + if is_event { + response.update_event_message(&rpc_request); + } + + // Step 2: Create the message + let mut message = ApiMessage::new( + rpc_request.ctx.protocol.clone(), + serde_json::to_string(&response).unwrap(), + rpc_request.ctx.request_id.clone(), + ); + let mut status_code: i64 = 1; + if let Some(e) = &response.error { + if let Some(Value::Number(n)) = e.get("code") { + if let Some(v) = n.as_i64() { + status_code = v; + } + } + } + + platform_state.metrics.update_api_stats_ref( + &rpc_request.ctx.request_id, + add_telemetry_status_code( + &tm_str, + status_code.to_string().as_str(), + ), + ); + + if let Some(api_stats) = platform_state + .metrics + .get_api_stats(&rpc_request.ctx.request_id) + { + message.stats = Some(api_stats); + + if rpc_request.ctx.app_id.eq_ignore_ascii_case("internal") { + platform_state + .metrics + .remove_api_stats(&rpc_request.ctx.request_id); + } + } + + // Step 3: Handle Non Extension + if matches!(rpc_request.ctx.protocol, ApiProtocol::Extn) { + if let Ok(extn_message) = + platform_state.endpoint_state.get_extn_message(id, is_event) + { + if is_event { + forward_extn_event( + &extn_message, + response.clone(), + &platform_state, + ) + .await; + } else { + let client = platform_state.get_client().get_extn_client(); + return_extn_response(message, extn_message, client); + } + } + } else if let Some(session) = platform_state + .session_state + .get_session_for_connection_id(&session_id) + { + let _ = session.send_json_rpc(message).await; + } + } + + for listener in telemetry_response_listeners { + let _ = listener.try_send(BrokerOutput::new(response.clone())); + } } else { error!( "start_forwarder:{} request not found for {:?}", @@ -1338,326 +1614,6 @@ impl BrokerOutputForwarder { }); } - async fn forward_response( - response: JsonRpcApiResponse, - rpc_request: &RpcRequest, - platform_state: &mut PlatformState, - is_event: bool, - id: u64, - workflow_callback: Option, - telemetry_response_listeners: Vec>, - ) { - LogSignal::new( - "forward_response".to_string(), - "entered".to_string(), - rpc_request.ctx.clone(), - ) - .emit_debug(); - let session_id = rpc_request.ctx.get_id(); - if let Some(workflow_callback) = workflow_callback { - debug!("sending to workflow callback {:?}", response); - LogSignal::new( - "forward_response".to_string(), - "sending to workflow callback".to_string(), - rpc_request.ctx.clone(), - ) - .emit_debug(); - let _ = workflow_callback - .sender - .try_send(BrokerOutput::new(response.clone())); - } else { - let tm_str = get_rpc_header(rpc_request); - let mut response = response.clone(); - if is_event { - response.update_event_message(rpc_request); - } - let mut message = ApiMessage::new( - rpc_request.ctx.protocol.clone(), - serde_json::to_string(&response).unwrap(), - rpc_request.ctx.request_id.clone(), - ); - let mut status_code: i64 = 1; - if let Some(e) = &response.error { - if let Some(Value::Number(n)) = e.get("code") { - if let Some(v) = n.as_i64() { - status_code = v; - } - } - } - platform_state.metrics.update_api_stats_ref( - &rpc_request.ctx.request_id, - add_telemetry_status_code(&tm_str, status_code.to_string().as_str()), - ); - if let Some(api_stats) = platform_state - .metrics - .get_api_stats(&rpc_request.ctx.request_id) - { - message.stats = Some(api_stats); - if rpc_request.ctx.app_id.eq_ignore_ascii_case("internal") { - platform_state - .metrics - .remove_api_stats(&rpc_request.ctx.request_id); - } - } - if matches!(rpc_request.ctx.protocol, ApiProtocol::Extn) { - if let Ok(extn_message) = - platform_state.endpoint_state.get_extn_message(id, is_event) - { - let client = platform_state.get_client().get_extn_client(); - if is_event { - forward_extn_event(&extn_message, response.clone(), platform_state).await; - } else { - return_extn_response(message, extn_message, client) - } - } - } else if matches!(rpc_request.ctx.protocol, ApiProtocol::Service) { - Self::handle_service_message(rpc_request, &message, platform_state).await; - } else if let Some(session) = platform_state - .session_state - .get_session_for_connection_id(&session_id) - { - let _ = session.send_json_rpc(message).await; - } - } - for listener in telemetry_response_listeners { - let _ = listener.try_send(BrokerOutput::new(response.clone())); - } - } - - async fn handle_event_output( - broker_request: &BrokerRequest, - rpc_request: &RpcRequest, - response: &mut JsonRpcApiResponse, - platform_state: &PlatformState, - event_utility: &Arc, - result: Value, - ) -> bool { - if let Some(event_handler) = broker_request.rule.event_handler.clone() { - let platform_state_c = platform_state.clone(); - let rpc_request_c = rpc_request.clone(); - let response_c = response.clone(); - let broker_request_c = broker_request.clone(); - - LogSignal::new( - "handle_event_output".to_string(), - "spawning event handler".to_string(), - broker_request.clone(), - ) - .emit_debug(); - - tokio::spawn(Self::handle_event( - platform_state_c, - event_handler, - broker_request_c, - rpc_request_c, - response_c, - )); - return true; - } - - if let Some(filter) = broker_request.rule.transform.get_transform_data( - super::rules::rules_engine::RuleTransformType::Event( - rpc_request.ctx.context.contains(&RPC_V2.into()), - ), - ) { - apply_rule_for_event(broker_request, &result, rpc_request, &filter, response); - } - - if !apply_filter(broker_request, &result, rpc_request) { - LogSignal::new( - "handle_event_output".to_string(), - "event filtered out".to_string(), - broker_request.clone(), - ) - .emit_debug(); - return true; - } - - if let Some(decorator_method) = broker_request.rule.transform.event_decorator_method.clone() - { - if let Some(func) = event_utility.get_function(&decorator_method) { - LogSignal::new( - "handle_event_output".to_string(), - "event decorator method found".to_string(), - rpc_request.ctx.clone(), - ) - .emit_debug(); - let session_id = rpc_request.ctx.get_id(); - let request_id = rpc_request.ctx.call_id; - let protocol = rpc_request.ctx.protocol.clone(); - let platform_state_c = platform_state.clone(); - let ctx = rpc_request.ctx.clone(); - let mut response_c = response.clone(); - tokio::spawn(async move { - if let Ok(value) = - func(platform_state_c.clone(), ctx.clone(), Some(result.clone())).await - { - response_c.result = Some(value.expect("REASON")); - } - response_c.id = Some(request_id); - let message = ApiMessage::new( - protocol, - serde_json::to_string(&response_c).unwrap(), - ctx.request_id.clone(), - ); - if let Some(session) = platform_state_c - .session_state - .get_session_for_connection_id(&session_id) - { - let _ = session.send_json_rpc(message).await; - } - }); - return true; - } else { - LogSignal::new( - "handle_event_output".to_string(), - "event decorator method not found".to_string(), - rpc_request.ctx.clone(), - ) - .emit_debug(); - error!("Failed to invoke decorator method {:?}", decorator_method); - } - } - false - } - - fn handle_subscription_response( - broker_request: &BrokerRequest, - rpc_request: &RpcRequest, - response: &mut JsonRpcApiResponse, - sub_processed: bool, - platform_state: &PlatformState, - id: u64, - ) -> bool { - LogSignal::new( - "handle_subscription_response".to_string(), - "entered".to_string(), - broker_request.clone(), - ) - .emit_debug(); - - if sub_processed { - LogSignal::new( - "handle_subscription_response".to_string(), - "subscription already processed".to_string(), - broker_request.clone(), - ) - .emit_debug(); - return true; - } - response.result = Some(json!({ - "listening" : rpc_request.is_listening(), - "event" : rpc_request.ctx.method - })); - platform_state.endpoint_state.update_unsubscribe_request(id); - - LogSignal::new( - "handle_subscription_response".to_string(), - "subscription response set".to_string(), - broker_request.clone(), - ) - .emit_debug(); - false - } - - fn apply_response_transform( - broker_request: &BrokerRequest, - output: &BrokerOutput, - response: &mut JsonRpcApiResponse, - rule_context_name: &str, - ) { - LogSignal::new( - "apply_response_transform".to_string(), - "entered".to_string(), - broker_request.clone(), - ) - .emit_debug(); - - let mut apply_response_using_main_req_needed = true; - if let Some(params) = output.data.params.clone() { - if let Some(param) = params.as_object() { - for (key, value) in param { - if key == "response" { - if let Some(filter) = value.as_str() { - apply_response_using_main_req_needed = false; - apply_response( - filter.to_string(), - &broker_request.rpc.ctx.method, - response, - ); - } - } - } - } - } - if apply_response_using_main_req_needed { - if let Some(filter) = broker_request - .rule - .transform - .get_transform_data(super::rules::rules_engine::RuleTransformType::Response) - { - apply_response(filter, rule_context_name, response); - } else if response.result.is_none() && response.error.is_none() { - response.result = Some(Value::Null); - } - } - } - - async fn handle_service_message( - rpc_request: &RpcRequest, - message: &ApiMessage, - platform_state: &PlatformState, - ) { - let context = rpc_request.ctx.clone().context; - if context.len() < 2 { - error!("Context does not contain a valid service id"); - return; - } - let service_id = context[1].to_string(); - let service_sender = platform_state - .service_controller_state - .get_sender(&service_id) - .await; - if let Some(sender) = service_sender { - let json_rpc_response = - serde_json::from_str::(message.jsonrpc_msg.clone().as_str()) - .unwrap(); - - let result = json_rpc_response.get("result").cloned().unwrap_or_default(); - let jsonrpc = serde_json::to_string( - &json_rpc_response - .get("jsonrpc") - .cloned() - .unwrap_or_default(), - ) - .unwrap(); - let id = ServiceMessageId::String(message.request_id.clone()); - - let service_message = ServiceMessage { - message: ServiceJsonRpcMessage::Success(ServiceJsonRpcSuccess { - result, - jsonrpc, - id, - }), - context: Some(serde_json::to_value(rpc_request.ctx.clone()).unwrap_or_default()), - }; - let msg_str = serde_json::to_string(&service_message).unwrap(); - let mes = Message::Text(msg_str.clone()); - debug!( - "Sending response to service {} message: {:?}", - service_id, mes - ); - if let Err(err) = sender.try_send(mes) { - error!( - "Failed to send request to service {}: {:?}", - service_id, err - ); - } else { - debug!("Successfully sent request to service: {}", service_id); - } - } - } - async fn handle_event( platform_state: PlatformState, event_handler: EventHandler, @@ -1690,11 +1646,14 @@ impl BrokerOutputForwarder { if let Ok(event_handler_response_string) = serde_json::to_string(&event_handler_response) { - if let Some(mut event_filter) = broker_request.rule.transform.get_transform_data( - super::rules::rules_engine::RuleTransformType::Event( - rpc_request.ctx.context.contains(&RPC_V2.into()), - ), - ) { + if let Some(mut event_filter) = + broker_request + .rule + .transform + .get_transform_data(RuleTransformType::Event( + rpc_request.ctx.context.contains(&RPC_V2.into()), + )) + { event_filter = event_filter .replace("$event_handler_response", &event_handler_response_string); @@ -1730,7 +1689,6 @@ impl BrokerOutputForwarder { let _ = session.send_json_rpc(message).await; } } - pub fn handle_non_jsonrpc_response( data: &[u8], callback: BrokerCallback, @@ -1745,19 +1703,17 @@ impl BrokerOutputForwarder { } else { None }; - - let result = if !data.is_empty() { - match serde_json::from_slice::(data) { - Ok(v) => Some(v), - Err(e) => { - error!("handle_non_jsonrpc_response: Error parsing data: e={:?}", e); - return Err(RippleError::ParseError); - } - } - } else { - None - }; - + let parse_result = serde_json::from_slice::(data); + debug!( + "parse result {:?} processing: {:?}, which is: {:?}", + parse_result, + data, + String::from_utf8_lossy(data) + ); + if parse_result.is_err() { + return Err(RippleError::ParseError); + } + let result = Some(parse_result.unwrap()); debug!("result {:?}", result); // build JsonRpcApiResponse let data = JsonRpcApiResponse { @@ -1851,7 +1807,7 @@ pub fn apply_response( Err(e) => { response.error = Some(json!(e.to_string())); error!( - "jq compile error: e={:?}, filter={}, response={:?}", + "jq compile error {:?} for rule {} and data {:?}", e, result_response_filter, response ); } @@ -1860,7 +1816,7 @@ pub fn apply_response( Err(e) => { response.error = Some(json!(e.to_string())); error!( - "json rpc response error: e={:?}, filter={}, response={:?}", + "json rpc response error {:?} for rule {} and data {}", e, result_response_filter, response ); } @@ -1920,8 +1876,12 @@ fn apply_filter(broker_request: &BrokerRequest, result: &Value, rpc_request: &Rp #[cfg(test)] mod endpoint_broker_tests { use super::*; - use crate::broker::rules::rules_engine::RuleTransform; - use ripple_sdk::{tokio::sync::mpsc::channel, Mockable}; + + use ripple_sdk::{ + api::rules_engine::{Rule, RuleTransform}, + tokio::sync::mpsc::channel, + Mockable, + }; #[tokio::test] async fn test_send_error() { @@ -1979,10 +1939,12 @@ mod endpoint_broker_tests { } mod endpoint_broker_state { - use ripple_sdk::{tokio, tokio::sync::mpsc::channel}; + use ripple_sdk::{ + api::rules_engine::{RuleEndpoint, RuleEndpointProtocol, RuleEngine}, + tokio::{self, sync::mpsc::channel}, + }; use crate::{ - broker::rules::rules_engine::{RuleEngine, RuleSet}, service::extn::ripple_client::RippleClient, state::{bootstrap_state::ChannelsState, ops_metrics_state::OpMetricState}, }; @@ -1990,10 +1952,9 @@ mod endpoint_broker_tests { use super::EndpointBrokerState; use crate::broker::endpoint_broker::BrokerConnectRequest; use crate::broker::endpoint_broker::ATOMIC_ID; - use crate::broker::rules::rules_engine::RuleEndpoint; - use crate::broker::rules::rules_engine::RuleEndpointProtocol; + use ripple_sdk::api::session::AccountSession; - use std::{collections::HashMap, sync::atomic::Ordering}; + use std::sync::atomic::Ordering; fn reset_counter(value: u64) { ATOMIC_ID.store(value, Ordering::SeqCst); @@ -2121,10 +2082,9 @@ mod endpoint_broker_tests { let mut state = EndpointBrokerState::new( OpMetricState::default(), tx, - RuleEngine { - rules: RuleSet::default(), - functions: HashMap::default(), - }, + std::sync::Arc::new(ripple_sdk::tokio::sync::RwLock::new(Box::new( + RuleEngine::default(), + ))), client, ); @@ -2152,10 +2112,9 @@ mod endpoint_broker_tests { let mut state = EndpointBrokerState::new( OpMetricState::default(), tx, - RuleEngine { - rules: RuleSet::default(), - functions: HashMap::default(), - }, + std::sync::Arc::new(ripple_sdk::tokio::sync::RwLock::new(Box::new( + RuleEngine::default(), + ))), client, ); @@ -2183,10 +2142,9 @@ mod endpoint_broker_tests { let mut state = EndpointBrokerState::new( OpMetricState::default(), tx, - RuleEngine { - rules: RuleSet::default(), - functions: HashMap::default(), - }, + std::sync::Arc::new(ripple_sdk::tokio::sync::RwLock::new(Box::new( + RuleEngine::default(), + ))), client, ); @@ -2214,10 +2172,9 @@ mod endpoint_broker_tests { let mut state = EndpointBrokerState::new( OpMetricState::default(), tx, - RuleEngine { - rules: RuleSet::default(), - functions: HashMap::default(), - }, + std::sync::Arc::new(ripple_sdk::tokio::sync::RwLock::new(Box::new( + RuleEngine::default(), + ))), client, ); @@ -2245,10 +2202,9 @@ mod endpoint_broker_tests { let mut state = EndpointBrokerState::new( OpMetricState::default(), tx, - RuleEngine { - rules: RuleSet::default(), - functions: HashMap::default(), - }, + std::sync::Arc::new(ripple_sdk::tokio::sync::RwLock::new(Box::new( + RuleEngine::default(), + ))), client, ); @@ -2276,10 +2232,9 @@ mod endpoint_broker_tests { let mut state = EndpointBrokerState::new( OpMetricState::default(), tx, - RuleEngine { - rules: RuleSet::default(), - functions: HashMap::default(), - }, + std::sync::Arc::new(ripple_sdk::tokio::sync::RwLock::new(Box::new( + RuleEngine::default(), + ))), client, ); @@ -2307,10 +2262,9 @@ mod endpoint_broker_tests { let mut state = EndpointBrokerState::new( OpMetricState::default(), tx, - RuleEngine { - rules: RuleSet::default(), - functions: HashMap::default(), - }, + std::sync::Arc::new(ripple_sdk::tokio::sync::RwLock::new(Box::new( + RuleEngine::default(), + ))), client, ); @@ -2340,10 +2294,9 @@ mod endpoint_broker_tests { let mut state = EndpointBrokerState::new( OpMetricState::default(), tx, - RuleEngine { - rules: RuleSet::default(), - functions: HashMap::default(), - }, + std::sync::Arc::new(ripple_sdk::tokio::sync::RwLock::new(Box::new( + RuleEngine::default(), + ))), client, ); @@ -2578,23 +2531,21 @@ mod endpoint_broker_tests { } #[cfg(test)] mod static_rules { - use std::collections::HashMap; - use crate::broker::endpoint_broker::apply_response; use crate::broker::endpoint_broker::BrokerConnectRequest; use crate::broker::endpoint_broker::BrokerOutput; use crate::broker::endpoint_broker::EndpointBrokerState; - use crate::broker::rules::rules_engine::RuleEndpoint; - use crate::broker::rules::rules_engine::RuleEndpointProtocol; - use crate::broker::rules::rules_engine::RuleEngine; - use crate::broker::rules::rules_engine::RuleSet; - use crate::broker::rules::rules_engine::{Rule, RuleTransform}; use crate::service::extn::ripple_client::RippleClient; use crate::state::bootstrap_state::ChannelsState; use crate::state::ops_metrics_state::OpMetricState; use ripple_sdk::api::gateway::rpc_gateway_api::JsonRpcApiResponse; use ripple_sdk::api::gateway::rpc_gateway_api::RpcRequest; + use ripple_sdk::api::rules_engine::Rule; + use ripple_sdk::api::rules_engine::RuleEndpoint; + use ripple_sdk::api::rules_engine::RuleEndpointProtocol; + use ripple_sdk::api::rules_engine::RuleEngine; + use ripple_sdk::api::rules_engine::RuleTransform; use ripple_sdk::tokio; use ripple_sdk::tokio::sync::mpsc::channel; use ripple_sdk::Mockable; @@ -2611,10 +2562,9 @@ mod endpoint_broker_tests { let mut state = EndpointBrokerState::new( OpMetricState::default(), tx, - RuleEngine { - rules: RuleSet::default(), - functions: HashMap::default(), - }, + std::sync::Arc::new(ripple_sdk::tokio::sync::RwLock::new(Box::new( + RuleEngine::default(), + ))), client, ); let endpoint = RuleEndpoint { @@ -2657,10 +2607,9 @@ mod endpoint_broker_tests { let mut state = EndpointBrokerState::new( OpMetricState::default(), tx, - RuleEngine { - rules: RuleSet::default(), - functions: HashMap::default(), - }, + std::sync::Arc::new(ripple_sdk::tokio::sync::RwLock::new(Box::new( + RuleEngine::default(), + ))), client, ); let endpoint = RuleEndpoint { @@ -2705,10 +2654,9 @@ mod endpoint_broker_tests { let mut state = EndpointBrokerState::new( OpMetricState::default(), tx, - RuleEngine { - rules: RuleSet::default(), - functions: HashMap::default(), - }, + std::sync::Arc::new(ripple_sdk::tokio::sync::RwLock::new(Box::new( + RuleEngine::default(), + ))), client, ); let endpoint = RuleEndpoint { @@ -2752,10 +2700,9 @@ mod endpoint_broker_tests { let mut state = EndpointBrokerState::new( OpMetricState::default(), tx, - RuleEngine { - rules: RuleSet::default(), - functions: HashMap::default(), - }, + std::sync::Arc::new(ripple_sdk::tokio::sync::RwLock::new(Box::new( + RuleEngine::default(), + ))), client, ); let endpoint = RuleEndpoint { @@ -2798,10 +2745,9 @@ mod endpoint_broker_tests { let mut state = EndpointBrokerState::new( OpMetricState::default(), tx, - RuleEngine { - rules: RuleSet::default(), - functions: HashMap::default(), - }, + std::sync::Arc::new(ripple_sdk::tokio::sync::RwLock::new(Box::new( + RuleEngine::default(), + ))), client, ); let endpoint = RuleEndpoint { @@ -2838,28 +2784,25 @@ mod endpoint_broker_tests { #[cfg(test)] mod provided_request { - use std::collections::HashMap; - use crate::{ - broker::{ - endpoint_broker::{EndpointBrokerState, RenderedRequest}, - rules::rules_engine::{Rule, RuleEngine, RuleSet}, - }, + broker::endpoint_broker::{EndpointBrokerState, RenderedRequest}, service::extn::ripple_client::RippleClient, state::{bootstrap_state::ChannelsState, ops_metrics_state::OpMetricState}, }; use ripple_sdk::{ - api::gateway::rpc_gateway_api::RpcRequest, tokio::sync::mpsc::channel, Mockable, + api::{ + gateway::rpc_gateway_api::RpcRequest, + rules_engine::{Rule, RuleEngine}, + }, + tokio::sync::mpsc::channel, + Mockable, }; #[test] fn test_basic() { let (tx, _) = channel(2); let client = RippleClient::new(ChannelsState::new()); - let mut engine = RuleEngine { - rules: RuleSet::default(), - functions: HashMap::default(), - }; + let mut engine = RuleEngine::default(); let r = Rule { alias: "provided".to_owned(), transform: Default::default(), @@ -2870,7 +2813,12 @@ mod endpoint_broker_tests { }; engine.add_rule(r); - let under_test = EndpointBrokerState::new(OpMetricState::default(), tx, engine, client); + let under_test = EndpointBrokerState::new( + OpMetricState::default(), + tx, + std::sync::Arc::new(ripple_sdk::tokio::sync::RwLock::new(Box::new(engine))), + client, + ); let f = under_test.handle_provided_request( &RpcRequest::mock(), @@ -2895,10 +2843,9 @@ mod endpoint_broker_tests { let mut endpoint_broker = EndpointBrokerState::new( OpMetricState::default(), tx, - RuleEngine { - rules: RuleSet::default(), - functions: HashMap::default(), - }, + std::sync::Arc::new(ripple_sdk::tokio::sync::RwLock::new(Box::new( + RuleEngine::default(), + ))), client, ); for endpoint in endpoints { @@ -2908,20 +2855,21 @@ mod endpoint_broker_tests { endpoint_broker } - use std::collections::HashMap; + use std::sync::Arc; + use crate::broker::endpoint_broker::TokioRwLock; use crate::{ - broker::{ - endpoint_broker::{ - BrokerRequest, BrokerSender, EndpointBrokerState, HandleBrokerageError, - }, - rules::rules_engine::{Rule, RuleEngine, RuleSet}, + broker::endpoint_broker::{ + BrokerRequest, BrokerSender, EndpointBrokerState, HandleBrokerageError, }, service::extn::ripple_client::RippleClient, state::{bootstrap_state::ChannelsState, ops_metrics_state::OpMetricState}, }; use ripple_sdk::{ - api::gateway::rpc_gateway_api::RpcRequest, + api::{ + gateway::rpc_gateway_api::RpcRequest, + rules_engine::{Rule, RuleEngine, RuleEngineProvider}, + }, extn::extn_client_message::ExtnMessage, tokio::{ self, @@ -2938,7 +2886,8 @@ mod endpoint_broker_tests { .with_alias("static".to_string()) .with_endpoint("thunder".to_string()) .to_owned(), - ); + ) + .await; let broker_request = BrokerRequest::default(); assert!( under_test @@ -2959,11 +2908,13 @@ mod endpoint_broker_tests { #[tokio::test] async fn test_dispatch_brokerage_provided_rule() { let (bs, _) = channel(2); - let mut under_test = endpoint_broker_state_under_test(vec![]).add_rule( - Rule::default() - .with_alias("provided".to_string()) - .to_owned(), - ); + let mut under_test = endpoint_broker_state_under_test(vec![]) + .add_rule( + Rule::default() + .with_alias("provided".to_string()) + .to_owned(), + ) + .await; let under_test = under_test.add_endpoint("thunder".to_string(), BrokerSender { sender: bs }); let broker_request = BrokerRequest::default(); @@ -3000,10 +2951,10 @@ mod endpoint_broker_tests { async fn test_dispatch_brokerage_endpoint_rule() { let (tx, _) = channel(2); let client = RippleClient::new(ChannelsState::new()); - let mut engine = RuleEngine { - rules: RuleSet::default(), - functions: HashMap::default(), - }; + let engine: Arc>> = + std::sync::Arc::new(ripple_sdk::tokio::sync::RwLock::new(Box::new( + RuleEngine::default(), + ))); let rule = Rule { alias: "endpoint".to_owned(), transform: Default::default(), @@ -3012,9 +2963,13 @@ mod endpoint_broker_tests { event_handler: None, sources: None, }; - engine.add_rule(rule); + { + let mut mutant = engine.write().await; + mutant.add_rule(rule); + drop(mutant); + } let mut under_test = - EndpointBrokerState::new(OpMetricState::default(), tx, engine, client); + EndpointBrokerState::new(OpMetricState::default(), tx, engine.clone(), client); let (tx, _) = mpsc::channel::(10); under_test.add_endpoint("thunder".to_string(), BrokerSender { sender: tx }); @@ -3022,8 +2977,9 @@ mod endpoint_broker_tests { let mut request = RpcRequest::mock(); request.method = "endpoint".to_string(); - let result = - under_test.handle_brokerage_workflow(request, None, None, vec![], None, vec![]); + let result = under_test + .handle_brokerage_workflow(request, None, None, vec![], None, vec![]) + .await; assert!(result.is_ok(), "Expected Ok but got: {:?}", result); } @@ -3031,17 +2987,21 @@ mod endpoint_broker_tests { async fn test_dispatch_brokerage_rule_not_found() { let (tx, _) = channel(2); let client = RippleClient::new(ChannelsState::new()); - let engine = RuleEngine { - rules: RuleSet::default(), - functions: HashMap::default(), - }; - let under_test = EndpointBrokerState::new(OpMetricState::default(), tx, engine, client); + let under_test = EndpointBrokerState::new( + OpMetricState::default(), + tx, + std::sync::Arc::new(ripple_sdk::tokio::sync::RwLock::new(Box::new( + RuleEngine::default(), + ))), + client, + ); let mut request = RpcRequest::mock(); request.method = "nonexistent".to_string(); - let result = - under_test.handle_brokerage_workflow(request, None, None, vec![], None, vec![]); + let result = under_test + .handle_brokerage_workflow(request, None, None, vec![], None, vec![]) + .await; assert!( matches!(result, Err(HandleBrokerageError::RuleNotFound(_))), "Expected RuleNotFound error but got: {:?}", @@ -3053,10 +3013,10 @@ mod endpoint_broker_tests { async fn test_dispatch_brokerage_broker_not_found() { let (tx, _) = channel(2); let client = RippleClient::new(ChannelsState::new()); - let mut engine = RuleEngine { - rules: RuleSet::default(), - functions: HashMap::default(), - }; + let engine: Arc>> = + std::sync::Arc::new(ripple_sdk::tokio::sync::RwLock::new(Box::new( + RuleEngine::default(), + ))); let rule = Rule { alias: "endpoint".to_owned(), transform: Default::default(), @@ -3065,14 +3025,20 @@ mod endpoint_broker_tests { event_handler: None, sources: None, }; - engine.add_rule(rule); - let under_test = EndpointBrokerState::new(OpMetricState::default(), tx, engine, client); + { + let mut mutant = engine.write().await; + mutant.add_rule(rule); + drop(mutant); + } + let under_test = + EndpointBrokerState::new(OpMetricState::default(), tx, engine.clone(), client); let mut request = RpcRequest::mock(); request.method = "endpoint".to_string(); - let result = - under_test.handle_brokerage_workflow(request, None, None, vec![], None, vec![]); + let result = under_test + .handle_brokerage_workflow(request, None, None, vec![], None, vec![]) + .await; assert!( matches!(result, Err(HandleBrokerageError::BrokerNotFound(_))), "Expected BrokerNotFound error but got: {:?}", @@ -3081,14 +3047,13 @@ mod endpoint_broker_tests { } #[cfg(test)] mod update_request { - use ripple_sdk::{api::gateway::rpc_gateway_api::RpcRequest, tokio}; + use ripple_sdk::{ + api::{gateway::rpc_gateway_api::RpcRequest, rules_engine::RuleTransform}, + tokio, + }; use crate::{ - broker::{ - endpoint_broker::BrokerCallback, - rules::rules_engine::{Rule, RuleSet, RuleTransform}, - }, - state::ops_metrics_state::OpMetricState, + broker::endpoint_broker::BrokerCallback, state::ops_metrics_state::OpMetricState, }; use super::*; @@ -3101,10 +3066,9 @@ mod endpoint_broker_tests { let state = EndpointBrokerState::new( OpMetricState::default(), tx, - RuleEngine { - rules: RuleSet::default(), - functions: HashMap::default(), - }, + std::sync::Arc::new(ripple_sdk::tokio::sync::RwLock::new(Box::new( + RuleEngine::default(), + ))), client, ); @@ -3130,10 +3094,9 @@ mod endpoint_broker_tests { let state = EndpointBrokerState::new( OpMetricState::default(), tx, - RuleEngine { - rules: RuleSet::default(), - functions: HashMap::default(), - }, + std::sync::Arc::new(ripple_sdk::tokio::sync::RwLock::new(Box::new( + RuleEngine::default(), + ))), client, ); @@ -3161,10 +3124,9 @@ mod endpoint_broker_tests { let state = EndpointBrokerState::new( OpMetricState::default(), tx, - RuleEngine { - rules: RuleSet::default(), - functions: HashMap::default(), - }, + std::sync::Arc::new(ripple_sdk::tokio::sync::RwLock::new(Box::new( + RuleEngine::default(), + ))), client, ); @@ -3198,10 +3160,9 @@ mod endpoint_broker_tests { let state = EndpointBrokerState::new( OpMetricState::default(), tx, - RuleEngine { - rules: RuleSet::default(), - functions: HashMap::default(), - }, + std::sync::Arc::new(ripple_sdk::tokio::sync::RwLock::new(Box::new( + RuleEngine::default(), + ))), client, ); @@ -3256,29 +3217,4 @@ mod endpoint_broker_tests { assert!(cleaner.cleanup_session("test_app").await.is_err()); } } - #[cfg(test)] - mod workflow { - // fn test_workflow() { - // let (tx, _) = channel(2); - // let client = RippleClient::new(ChannelsState::new()); - // let mut state = EndpointBrokerState::new( - // MetricsState::default(), - // tx, - // RuleEngine { - // rules: RuleSet::default(), - // }, - // client, - // ); - // let endpoint = RuleEndpoint { - // protocol: RuleEndpointProtocol::Http, - // ..Default::default() - // }; - // let request = BrokerConnectRequest::new( - // "http_endpoint".to_string(), - // endpoint.clone(), - // state.reconnect_tx.clone(), - // ); - // state.build_endpoint(None, request); - // } - } } diff --git a/core/main/src/broker/extn_broker.rs b/core/main/src/broker/extn_broker.rs index e29980927..9137d8fab 100644 --- a/core/main/src/broker/extn_broker.rs +++ b/core/main/src/broker/extn_broker.rs @@ -171,15 +171,19 @@ impl EndpointBroker for ExtnBroker { #[cfg(test)] mod tests { + use std::sync::Arc; + use super::*; use crate::broker::endpoint_broker::BrokerOutput; - use crate::broker::rules::rules_engine::Rule; + use crate::service::extn::ripple_client::RippleClient; use crate::state::bootstrap_state::ChannelsState; use ripple_sdk::api::gateway::rpc_gateway_api::RpcRequest; use ripple_sdk::api::manifest::device_manifest::DeviceManifest; use ripple_sdk::api::manifest::extn_manifest::ExtnManifest; + use ripple_sdk::api::rules_engine::{Rule, RuleEngine, RuleEngineProvider}; use ripple_sdk::Mockable; + use ssda_types::gateway::ApiGatewayServer; #[tokio::test] pub async fn test_log_error_and_send_broker_failure_response() { @@ -286,6 +290,13 @@ mod tests { workflow_callback: Some(callback.clone()), telemetry_response_listeners: vec![], }; + let rules_engine: Arc>> = + Arc::new(tokio::sync::RwLock::new(Box::new(RuleEngine::default()))); + + let api_gateway_state: Arc>> = + Arc::new(tokio::sync::Mutex::new(Box::new( + ssda_service::ApiGateway::new(rules_engine.clone()), + ))); let platform_state = PlatformState::new( ExtnManifest::default(), @@ -293,6 +304,8 @@ mod tests { RippleClient::new(ChannelsState::default()), Vec::new(), None, + api_gateway_state.clone(), + rules_engine.clone(), ); let sender = ExtnBroker::start( Some(platform_state), diff --git a/core/main/src/broker/http_broker.rs b/core/main/src/broker/http_broker.rs index 79d302886..e0ead7a75 100644 --- a/core/main/src/broker/http_broker.rs +++ b/core/main/src/broker/http_broker.rs @@ -19,7 +19,11 @@ use std::vec; use hyper::{client::HttpConnector, Body, Client, Method, Request, Response, Uri}; use ripple_sdk::{ - api::{gateway::rpc_gateway_api::JsonRpcApiError, observability::log_signal::LogSignal}, + api::{ + gateway::rpc_gateway_api::JsonRpcApiError, + observability::log_signal::LogSignal, + rules_engine::{jq_compile, RuleTransformType}, + }, log::{debug, error}, tokio::{self, sync::mpsc}, utils::error::RippleError, @@ -31,10 +35,7 @@ use super::endpoint_broker::{ BrokerSender, EndpointBroker, EndpointBrokerState, BROKER_CHANNEL_BUFFER_SIZE, }; -use crate::{ - broker::rules::rules_engine::{jq_compile, RuleTransformType}, - state::platform_state::PlatformState, -}; +use crate::state::platform_state::PlatformState; use ripple_sdk::tokio_tungstenite::tungstenite::http::uri::InvalidUri; @@ -225,15 +226,15 @@ mod tests { use serde_json::{json, Value}; use std::time::Duration; - use crate::broker::{ - endpoint_broker::BrokerOutput, - rules::rules_engine::{Rule, RuleEndpoint, RuleEndpointProtocol}, - }; + use crate::broker::endpoint_broker::BrokerOutput; use super::*; use ripple_sdk::{ - api::gateway::rpc_gateway_api::{JsonRpcApiResponse, RpcRequest}, + api::{ + gateway::rpc_gateway_api::{JsonRpcApiResponse, RpcRequest}, + rules_engine::{Rule, RuleEndpoint, RuleEndpointProtocol}, + }, tokio::{runtime::Runtime, task::JoinHandle, time::timeout}, Mockable, }; diff --git a/core/main/src/broker/mod.rs b/core/main/src/broker/mod.rs index 4c95a69b7..2ae5b6934 100644 --- a/core/main/src/broker/mod.rs +++ b/core/main/src/broker/mod.rs @@ -20,9 +20,10 @@ pub mod event_management_utility; pub mod extn_broker; pub mod http_broker; pub mod provider_broker_state; -pub mod rules; +#[cfg(not(feature = "ssda"))] pub mod service_broker; -#[cfg(test)] +#[cfg(feature = "ssda")] +pub mod ssda_service_broker; pub mod test; pub mod thunder; pub mod thunder_broker; diff --git a/core/main/src/broker/service_broker.rs b/core/main/src/broker/service_broker.rs index 50a217336..56fa53b53 100644 --- a/core/main/src/broker/service_broker.rs +++ b/core/main/src/broker/service_broker.rs @@ -211,12 +211,8 @@ impl EndpointBroker for ServiceBroker { mod tests { use super::*; use crate::broker::endpoint_broker::BrokerOutput; - use crate::broker::rules::rules_engine::Rule; - use crate::service::extn::ripple_client::RippleClient; - use crate::state::bootstrap_state::ChannelsState; use ripple_sdk::api::gateway::rpc_gateway_api::RpcRequest; - use ripple_sdk::api::manifest::device_manifest::DeviceManifest; - use ripple_sdk::api::manifest::extn_manifest::ExtnManifest; + use ripple_sdk::api::rules_engine::Rule; use ripple_sdk::Mockable; #[tokio::test] @@ -326,14 +322,8 @@ mod tests { workflow_callback: Some(callback.clone()), telemetry_response_listeners: vec![], }; + let platform_state = PlatformState::default(); - let platform_state = PlatformState::new( - ExtnManifest::default(), - DeviceManifest::default(), - RippleClient::new(ChannelsState::default()), - Vec::new(), - None, - ); let sender = ServiceBroker::start( Some(platform_state), callback.clone(), diff --git a/core/main/src/broker/ssda_service_broker.rs b/core/main/src/broker/ssda_service_broker.rs new file mode 100644 index 000000000..d101a8ef6 --- /dev/null +++ b/core/main/src/broker/ssda_service_broker.rs @@ -0,0 +1,123 @@ +use ripple_sdk::{ + api::gateway::rpc_gateway_api::{JsonRpcApiError, JsonRpcApiResponse}, + log::{error, info}, + tokio::sync::mpsc, +}; +use ssda_types::gateway::{ServiceRoutingRequest, ServiceRoutingResponse}; +use ssda_types::ServiceRequestId; + +use super::endpoint_broker::{ + BrokerCleaner, BrokerOutputForwarder, BrokerSender, EndpointBroker, BROKER_CHANNEL_BUFFER_SIZE, +}; +use ripple_sdk::tokio; + +pub struct SsdaServiceBroker { + broker_sender: BrokerSender, +} + +impl SsdaServiceBroker {} +impl EndpointBroker for SsdaServiceBroker { + fn get_broker( + ps: Option, + _connect_request: super::endpoint_broker::BrokerConnectRequest, + broker_callback: super::endpoint_broker::BrokerCallback, + _endpoint_broker: &mut super::endpoint_broker::EndpointBrokerState, + ) -> Self { + //todo!(); + // let endpoint = request.endpoint.clone(); + let (tx, mut tr) = mpsc::channel(BROKER_CHANNEL_BUFFER_SIZE); + let broker_sender = BrokerSender { sender: tx }; + let callback = broker_callback.clone(); + if let Some(platform_state) = ps.clone() { + tokio::spawn(async move { + while let Some(request) = tr.recv().await { + info!("ServiceBroker received request: {:?}", request); + let services_tx = platform_state + .services_gateway_api + .lock() + .await + .get_sender(); + use tokio::sync::oneshot; + + let (oneshot_tx, oneshot_rx) = oneshot::channel::(); + + let service_request = ServiceRoutingRequest { + request_id: ServiceRequestId { + request_id: request.rpc.ctx.call_id, + }, + payload: request.rpc.clone(), + respond_to: oneshot_tx, + }; + info!( + "ServiceBroker sending service request: {:?}", + service_request + ); + + services_tx.try_send(service_request).unwrap(); + + match oneshot_rx.await { + Ok(response) => { + info!("ServiceBroker received response: {:?}", response); + match response { + ServiceRoutingResponse::Error(e) => { + error!("ServiceBroker received error response: {:?}", e); + let err = JsonRpcApiError::default() + .with_id(e.request_id.request_id) + .with_message(e.error) + .to_response(); + BrokerOutputForwarder::send_json_rpc_response_to_broker( + err, + callback.clone(), + ); + // send_broker_response(&callback, &request, &err.as_bytes()) + // .await; + } + ServiceRoutingResponse::Success(response) => { + info!( + "ServiceBroker received success response: {:?}", + response.response + ); + let json_rpc_response = + JsonRpcApiResponse::from_value(response.response).unwrap(); + info!( + "ServiceBroker creating JSON-RPC response: {:?}", + json_rpc_response + ); + // Convert the response to + let win: Vec = json_rpc_response.to_string().into_bytes(); + info!("ServiceBroker sending response: {:?}", win); + BrokerOutputForwarder::send_json_rpc_response_to_broker( + json_rpc_response, + callback.clone(), + ); + } + } + } + Err(e) => { + error!("ServiceBroker failed to receive response {}", e); + } + } + } + }); + } else { + panic!("Platform state is required"); + }; + + Self { + // platform_state: ps.clone(), + // connect_request, + // broker_callback, + // endpoint_broker_state: endpoint_broker.clone(), + broker_sender, + //cleaner: BrokerCleaner { cleaner: None }, + } + } + + fn get_sender(&self) -> super::endpoint_broker::BrokerSender { + self.broker_sender.clone() + } + + fn get_cleaner(&self) -> super::endpoint_broker::BrokerCleaner { + BrokerCleaner::default() + } +} diff --git a/core/main/src/broker/test/mock_thunder_lite_server.rs b/core/main/src/broker/test/mock_thunder_lite_server.rs index 430a3f127..84802ea49 100644 --- a/core/main/src/broker/test/mock_thunder_lite_server.rs +++ b/core/main/src/broker/test/mock_thunder_lite_server.rs @@ -374,7 +374,7 @@ macro_rules! process_broker_output { .clone() .rule .transform - .get_transform_data(RuleTransformType::Response) + .get_transform_data(ripple_sdk::api::rules_engine::RuleTransformType::Response) { apply_response(filter, &rule_context_name, &mut response); } else if response.result.is_none() && response.error.is_none() { @@ -396,12 +396,9 @@ macro_rules! process_broker_output_event_resposne { // Apply the jq transform to the response - if let Some(filter) = $broker_request - .clone() - .rule - .transform - .get_transform_data(RuleTransformType::Event(false)) - { + if let Some(filter) = $broker_request.clone().rule.transform.get_transform_data( + ripple_sdk::api::rules_engine::RuleTransformType::Event(false), + ) { let broker_request_clone = $broker_request.clone(); let result = response.clone().result.unwrap(); let rpc = $broker_request.rpc.clone(); diff --git a/core/main/src/broker/thunder/user_data_migrator.rs b/core/main/src/broker/thunder/user_data_migrator.rs index b2cc56927..c7393ba60 100644 --- a/core/main/src/broker/thunder/user_data_migrator.rs +++ b/core/main/src/broker/thunder/user_data_migrator.rs @@ -24,6 +24,7 @@ use std::{ }; use ripple_sdk::{ + api::rules_engine::{jq_compile, Rule, RuleTransformType}, api::{device::device_peristence::StorageData, gateway::rpc_gateway_api::JsonRpcApiResponse}, log::{debug, error, info}, tokio::{ @@ -40,7 +41,6 @@ use serde_json::{json, Value}; use crate::broker::{ endpoint_broker::{self, BrokerCallback, BrokerOutput, BrokerRequest, EndpointBrokerState}, - rules::rules_engine::{Rule, RuleTransformType}, thunder_broker::ThunderBroker, }; @@ -472,11 +472,7 @@ impl UserDataMigrator { .transform .get_transform_data(RuleTransformType::Request) { - return crate::broker::rules::rules_engine::jq_compile( - data, - &filter, - format!("{}_request", method), - ); + return jq_compile(data, &filter, format!("{}_request", method)); } serde_json::to_value(&data).map_err(|e| { error!( @@ -863,7 +859,7 @@ impl UserDataMigrator { response.data.result = Some(legacy_value.clone()); if let Some(conversion_rule) = &config_entry.legacy_to_plugin_value_conversion { - let data = crate::broker::rules::rules_engine::jq_compile( + let data = jq_compile( json!({ "value": legacy_value }), &conversion_rule.conversion_rule, "legacy_to_plugin_value_conversion".to_string(), diff --git a/core/main/src/broker/thunder_broker.rs b/core/main/src/broker/thunder_broker.rs index 081f8f3ab..71ceba1e9 100644 --- a/core/main/src/broker/thunder_broker.rs +++ b/core/main/src/broker/thunder_broker.rs @@ -692,16 +692,13 @@ impl EndpointBroker for ThunderBroker { #[cfg(test)] mod tests { use super::*; - use crate::broker::thunder_broker::tests::rules_engine::RuleTransformType; + use crate::{ broker::{ endpoint_broker::{ apply_response, apply_rule_for_event, BrokerCallback, BrokerConnectRequest, BrokerOutput, BrokerRequest, EndpointBroker, }, - rules::rules_engine::{ - self, EventHandler, Rule, RuleEndpoint, RuleEndpointProtocol, RuleTransform, - }, test::mock_thunder_lite_server::MockThunderLiteServer, }, create_and_send_broker_request, create_and_send_broker_request_with_jq_transform, @@ -710,7 +707,10 @@ mod tests { utils::test_utils::{MockWebsocket, WSMockData}, }; use ripple_sdk::{ - api::gateway::rpc_gateway_api::{ApiProtocol, CallContext, RpcRequest}, + api::{ + gateway::rpc_gateway_api::{ApiProtocol, CallContext, RpcRequest}, + rules_engine::{EventHandler, Rule, RuleEndpoint, RuleEndpointProtocol, RuleTransform}, + }, uuid::Uuid, }; use serde_json::json; @@ -812,7 +812,7 @@ mod tests { let endpoint = RuleEndpoint { url: format!("ws://127.0.0.1:{}", port), - protocol: crate::broker::rules::rules_engine::RuleEndpointProtocol::Websocket, + protocol: RuleEndpointProtocol::Websocket, jsonrpc: false, }; let (tx, _) = mpsc::channel(1); diff --git a/core/main/src/broker/websocket_broker.rs b/core/main/src/broker/websocket_broker.rs index 15fadf3e5..8442dd60b 100644 --- a/core/main/src/broker/websocket_broker.rs +++ b/core/main/src/broker/websocket_broker.rs @@ -248,13 +248,16 @@ mod tests { use std::time::Duration; use crate::{ - broker::{ - endpoint_broker::{BrokerOutput, BrokerRequest}, - rules::rules_engine::{Rule, RuleEndpoint, RuleTransform}, - }, + broker::endpoint_broker::{BrokerOutput, BrokerRequest}, utils::test_utils::{MockWebsocket, WSMockData}, }; - use ripple_sdk::{api::gateway::rpc_gateway_api::RpcRequest, utils::logger::init_logger}; + use ripple_sdk::{ + api::{ + gateway::rpc_gateway_api::RpcRequest, + rules_engine::{Rule, RuleEndpoint, RuleEndpointProtocol, RuleTransform}, + }, + utils::logger::init_logger, + }; use serde_json::json; use super::*; @@ -276,7 +279,7 @@ mod tests { let endpoint = RuleEndpoint { url: format!("ws://127.0.0.1:{}", port), - protocol: crate::broker::rules::rules_engine::RuleEndpointProtocol::Websocket, + protocol: RuleEndpointProtocol::Websocket, jsonrpc: false, }; let (tx, _) = mpsc::channel(1); @@ -413,7 +416,7 @@ mod tests { let endpoint = RuleEndpoint { url: format!("ws://127.0.0.1:{}", port), - protocol: crate::broker::rules::rules_engine::RuleEndpointProtocol::Websocket, + protocol: RuleEndpointProtocol::Websocket, jsonrpc: false, }; @@ -513,7 +516,7 @@ mod tests { let port: u32 = 34743; let endpoint = RuleEndpoint { url: format!("ws://127.0.0.1:{}", port), - protocol: crate::broker::rules::rules_engine::RuleEndpointProtocol::Websocket, + protocol: RuleEndpointProtocol::Websocket, jsonrpc: false, }; let _ = WSNotificationBroker::start(request, callback, endpoint.get_url().clone()); diff --git a/core/main/src/broker/workflow_broker.rs b/core/main/src/broker/workflow_broker.rs index 0be37a1e0..bdbd8bba3 100644 --- a/core/main/src/broker/workflow_broker.rs +++ b/core/main/src/broker/workflow_broker.rs @@ -2,12 +2,13 @@ use super::endpoint_broker::{ BrokerCallback, BrokerCleaner, BrokerConnectRequest, BrokerRequest, BrokerSender, EndpointBroker, HandleBrokerageError, BROKER_CHANNEL_BUFFER_SIZE, }; -use super::rules::rules_engine::JsonDataSource; + use crate::broker::endpoint_broker::{BrokerOutput, EndpointBrokerState}; -use crate::broker::rules::rules_engine::{compose_json_values, make_name_json_safe}; + use crate::state::platform_state::PlatformState; use futures::future::{join_all, BoxFuture}; use futures::FutureExt; +use ripple_sdk::api::rules_engine::{compose_json_values, make_name_json_safe, JsonDataSource}; use serde_json::json; use ripple_sdk::api::gateway::rpc_gateway_api::{JsonRpcApiError, JsonRpcApiResponse, RpcRequest}; @@ -55,16 +56,18 @@ async fn subbroker_call( source: JsonDataSource, ) -> Result { let (brokered_tx, mut brokered_rx) = mpsc::channel::(BROKER_CHANNEL_BUFFER_SIZE); - endpoint_broker.handle_brokerage( - rpc_request, - None, - Some(BrokerCallback { - sender: brokered_tx, - }), - Vec::new(), - None, - vec![], - ); + let _ = endpoint_broker + .handle_brokerage( + rpc_request, + None, + Some(BrokerCallback { + sender: brokered_tx, + }), + Vec::new(), + None, + vec![], + ) + .await; match brokered_rx.recv().await { Some(msg) => { @@ -307,15 +310,16 @@ write exhaustive tests for the WorkflowBroker #[cfg(test)] pub mod tests { - use std::sync::{Arc, RwLock}; - - use ripple_sdk::{api::gateway::rpc_gateway_api::RpcRequest, tokio, Mockable}; + use ripple_sdk::{ + api::{ + gateway::rpc_gateway_api::RpcRequest, + rules_engine::{JsonDataSource, Rule, RuleEngine, RuleEngineProvider}, + }, + tokio, Mockable, + }; use serde_json::json; - use crate::broker::{ - endpoint_broker::{BrokerCallback, BrokerRequest, EndpointBrokerState}, - rules::rules_engine::{JsonDataSource, Rule, RuleEngine}, - }; + use crate::broker::endpoint_broker::{BrokerCallback, BrokerRequest, EndpointBrokerState}; pub fn broker_request(callback: BrokerCallback) -> BrokerRequest { let mut rule = Rule { alias: "module.method".to_string(), @@ -368,7 +372,15 @@ pub mod tests { engine.unwrap() } pub fn endppoint_broker_state() -> EndpointBrokerState { - EndpointBrokerState::default().with_rules_engine(Arc::new(RwLock::new(rule_engine()))) + use ripple_sdk::tokio::sync::RwLock as TokioRwLock; + use std::sync::Arc; + + let rule_engine = rule_engine(); + let boxed: Box = Box::new(rule_engine); + let rw_locked = TokioRwLock::new(boxed); + let arc = Arc::new(rw_locked); + + EndpointBrokerState::default().with_rules_engine(arc) } #[tokio::test] diff --git a/core/main/src/firebolt/firebolt_gateway.rs b/core/main/src/firebolt/firebolt_gateway.rs index 659ed231d..d60731326 100644 --- a/core/main/src/firebolt/firebolt_gateway.rs +++ b/core/main/src/firebolt/firebolt_gateway.rs @@ -364,14 +364,17 @@ impl FireboltGateway { let requestor_callback_tx = Self::handle_broker_callback(platform_state.clone(), request_c.clone()); - let handled = platform_state.endpoint_state.handle_brokerage( - request_c.clone(), - extn_msg.clone(), - None, - p, - session.clone(), - vec![requestor_callback_tx], - ); + let handled = platform_state + .endpoint_state + .handle_brokerage( + request_c.clone(), + extn_msg.clone(), + None, + p, + session.clone(), + vec![requestor_callback_tx], + ) + .await; //.is_ok(); if !handled { diff --git a/core/main/src/firebolt/firebolt_ws.rs b/core/main/src/firebolt/firebolt_ws.rs index 03e7b6d77..954198c7a 100644 --- a/core/main/src/firebolt/firebolt_ws.rs +++ b/core/main/src/firebolt/firebolt_ws.rs @@ -23,7 +23,6 @@ use std::{ use super::firebolt_gateway::FireboltGatewayCommand; use crate::{ service::apps::delegated_launcher_handler::{AppManagerState, AppManagerState2_0}, - service::ripple_service::service_controller_state::ServiceControllerState, state::{ cap::permitted_state::PermissionHandler, platform_state::PlatformState, session_state::Session, @@ -34,10 +33,16 @@ use futures::StreamExt; use jsonrpsee::types::{error::INVALID_REQUEST_CODE, ErrorObject, ErrorResponse, Id}; use ripple_sdk::{ api::manifest::extn_manifest::ExtnSymbol, + extn::{ + extn_client_message::{ExtnMessage, ExtnPayload, ExtnResponse}, + extn_id::ExtnId, + }, + framework::ripple_contract::RippleContract, tokio_tungstenite::{ tungstenite::{self, Message}, WebSocketStream, }, + utils::error::RippleError, }; use ripple_sdk::{ api::{ @@ -46,15 +51,19 @@ use ripple_sdk::{ }, observability::log_signal::LogSignal, }, - log::{error, info, trace}, + log::{error, info}, tokio::{ net::{TcpListener, TcpStream}, - sync::{mpsc, oneshot}, + sync::{mpsc, oneshot, Mutex}, }, utils::channel_utils::oneshot_send_and_log, uuid::Uuid, }; use ripple_sdk::{log::debug, tokio}; +use ssda_service::ApiGateway; +use ssda_types::gateway::{APIGatewayServiceConnectionDisposition, ApiGatewayServer}; +use ssda_types::ServiceId; + #[allow(dead_code)] pub struct FireboltWs {} @@ -66,7 +75,10 @@ pub struct ClientIdentity { pub rpc_v2: bool, pub service_info: Option, } - +#[derive(Debug, Clone)] +struct ServiceConnection { + service_id: ServiceId, +} struct ConnectionCallbackConfig { pub next: oneshot::Sender, pub app_state: AppManagerState, @@ -74,9 +86,9 @@ struct ConnectionCallbackConfig { pub app_lifecycle_2_enabled: bool, pub secure: bool, pub internal_app_id: Option, - extns: Vec, + pub extns: Vec, + pub service_connection: Arc>>, } - impl ConnectionCallbackConfig { fn get_extn(&self, id: &str) -> Option { for extn in &self.extns { @@ -141,32 +153,29 @@ impl tungstenite::handshake::server::Callback for ConnectionCallback { if !cfg.secure { if let Ok(Some(extn_id)) = get_query(request, "service_handshake", false) { info!("Service handshake for extn_id={}", extn_id); - let cid = if let Some(c) = cfg.get_extn(&extn_id) { + if let Some(c) = cfg.get_extn(&extn_id) { // valid extn_id - ClientIdentity { + info!("New Service connection {:?}", extn_id); + let cid = ClientIdentity { session_id: Uuid::new_v4().to_string(), - app_id: extn_id.clone(), + app_id: extn_id, rpc_v2: true, service_info: Some(c), - } - } else { - // extn_id without any symbol in the manifest - info!("Extn not found for extn_id={} in {:?}", extn_id, cfg.extns); - // Accept the connection, the service will be registered later. - let extn_symbol = ExtnSymbol { - id: extn_id.clone(), - ..Default::default() }; - ClientIdentity { - session_id: Uuid::new_v4().to_string(), - app_id: extn_id.clone(), - rpc_v2: true, - service_info: Some(extn_symbol), - } - }; - info!("New Service connection {:?}", extn_id); - oneshot_send_and_log(cfg.next, cid, "ResolveClientIdentity"); - return Ok(response); + oneshot_send_and_log(cfg.next, cid, "ResolveClientIdentity"); + return Ok(response); + } + info!("Extn not found for extn_id={} in {:?} ", extn_id, cfg.extns); + // invalid extn_id + let err = tungstenite::http::response::Builder::new() + .status(403) + .body(Some(format!( + "Invalid service handshake for extn_id={}", + extn_id + ))) + .unwrap(); + error!("Invalid service handshake for extn_id={}", extn_id); + return Err(err); } } @@ -177,7 +186,12 @@ impl tungstenite::handshake::server::Callback for ConnectionCallback { None => cfg.internal_app_id, }, }; - + if let Ok(APIGatewayServiceConnectionDisposition::Accept(service_id)) = + ApiGateway::is_apigateway_connection(request.uri()) + { + let service_connection = ServiceConnection { service_id }; + *cfg.service_connection.lock().unwrap() = Some(service_connection); + }; let session_id = match app_id_opt { Some(_) => Uuid::new_v4().to_string(), // can unwrap here because if session is not given, then error will be returned @@ -259,6 +273,7 @@ impl FireboltWs { state: PlatformState, secure: bool, internal_app_id: Option, + service_manager: Arc>>, ) { // Create the event loop and TCP listener we'll accept connections on. let try_socket = TcpListener::bind(&server_addr).await; //create the server on the address @@ -275,6 +290,8 @@ impl FireboltWs { // Let's spawn the handling of each connection in a separate task. while let Ok((stream, client_addr)) = listener.accept().await { let (connect_tx, connect_rx) = oneshot::channel::(); + let service_manager_clone = Arc::clone(&service_manager); + let service_connection_state = Arc::new(std::sync::Mutex::new(None)); let cfg = ConnectionCallbackConfig { next: connect_tx, app_state: app_state.clone(), @@ -283,6 +300,7 @@ impl FireboltWs { secure, internal_app_id: internal_app_id.clone(), extns: extns.clone(), + service_connection: service_connection_state.clone(), }; match ripple_sdk::tokio_tungstenite::accept_hdr_async(stream, ConnectionCallback(cfg)) .await @@ -291,23 +309,330 @@ impl FireboltWs { error!("websocket connection error {:?}", e); } Ok(ws_stream) => { - trace!("websocket connection success"); - let state_for_connection_c = state_for_connection.clone(); - tokio::spawn(async move { - FireboltWs::handle_connection( - client_addr, - ws_stream, - connect_rx, - state_for_connection_c.clone(), - secure, - ) - .await; - }); + // let (mut send , mut recv) = ws_stream.split();; + //let c = send; + let connection = service_connection_state.clone(); + let service_connection_state = connection.lock().unwrap().clone(); + //let service_connections = service_connection_state.clone(); + + match service_connection_state { + Some(service_connection) => { + let service_connection = service_connection.clone(); + let service_id = service_connection.service_id.clone(); + + info!("Service connection for service_id={:?}", service_id); + tokio::spawn(async move { + FireboltWs::handle_service_connection( + service_id, + service_manager_clone, + ws_stream, + ) + .await; + }); + } + None => { + let state_for_connection_c = state_for_connection.clone(); + tokio::spawn(async move { + FireboltWs::handle_connection( + client_addr, + ws_stream, + connect_rx, + state_for_connection_c.clone(), + secure, + ) + .await; + }); + } + } } } } } + async fn handle_service_connection( + service_id: ServiceId, + service_manager: Arc>>, + ws_stream: WebSocketStream, + ) { + info!("handle_service_connection"); + { + let mut guard = service_manager.lock().await; + guard.service_connect(service_id, ws_stream).await.unwrap(); + } + } + #[cfg(feature = "ssda")] + async fn send_t2_event(state: &PlatformState, payload: String) { + use ripple_sdk::api::gateway::rpc_gateway_api::CallContext; + let rpc_request = RpcRequest::new( + String::from("telemetry.event"), + serde_json::json!({ + "payload": payload, + }) + .to_string(), + CallContext::default(), + ); + + state + .endpoint_state + .handle_brokerage(rpc_request, None, None, vec![], None, vec![]) + .await; + } + + #[cfg(not(feature = "ssda"))] + async fn handle_connection( + _client_addr: SocketAddr, + ws_stream: WebSocketStream, + connect_rx: oneshot::Receiver, + state: PlatformState, + gateway_secure: bool, + ) { + use crate::service::ripple_service::service_controller_state::ServiceControllerState; + let identity = connect_rx.await.unwrap(); + + // Generate a unique connection ID + let connection_id = Uuid::new_v4().to_string(); + + if let Some(symbol) = identity.service_info.clone() { + // Handle service connection + ServiceControllerState::handle_service_connection( + _client_addr, + ws_stream, + state, + identity, + connection_id, + symbol, + ) + .await; + } else { + // Handle app connection + Self::handle_app_connection( + _client_addr, + ws_stream, + state, + identity, + connection_id, + gateway_secure, + ) + .await; + } + } + #[cfg(feature = "ssda")] + async fn handle_connection( + _client_addr: SocketAddr, + ws_stream: WebSocketStream, + connect_rx: oneshot::Receiver, + state: PlatformState, + gateway_secure: bool, + ) { + let identity = connect_rx.await.unwrap(); + let client = state.get_client(); + let app_id = identity.app_id.clone(); + let (session_tx, mut resp_rx) = mpsc::channel(32); + let ctx = ClientContext { + session_id: identity.session_id.clone(), + app_id: app_id.clone(), + gateway_secure, + }; + let session = Session::new(identity.app_id.clone(), Some(session_tx.clone())); + let app_id_c = app_id.clone(); + let session_id_c = identity.session_id.clone(); + + let connection_id = Uuid::new_v4().to_string(); + + let connection_id_c = connection_id.clone(); + let mut is_service = false; + if let Some(symbol) = identity.service_info.clone() { + info!( + "Creating new service connection_id={} app_id={} session_id={}, gateway_secure={}, port={}", + connection_id, + app_id_c, + session_id_c, + gateway_secure, + _client_addr.port() + ); + is_service = true; + let id = session_id_c.clone(); + if let Some(sender) = session.get_sender() { + // Gateway will probably not necessarily be ready when extensions start + state.session_state.add_session(id, session.clone()); + client + .get_extn_client() + .add_sender(app_id_c.clone(), symbol, sender); + } + } else { + info!( + "Creating new connection_id={} app_id={} session_id={}, gateway_secure={}, port={}", + connection_id, + app_id_c, + session_id_c, + gateway_secure, + _client_addr.port() + ); + let msg = FireboltGatewayCommand::RegisterSession { + session_id: connection_id.clone(), + session, + }; + if let Err(e) = client.send_gateway_command(msg) { + error!("Error registering the connection {:?}", e); + return; + } + if !gateway_secure + && PermissionHandler::fetch_and_store(&state, &app_id, false) + .await + .is_err() + { + error!("Couldnt pre cache permissions"); + } + } + let mut context = vec![]; + if identity.rpc_v2 { + context.push(RPC_V2.to_string()); + } + + let rpc_context: Arc>> = Arc::new(RwLock::new(context)); + let (mut sender, mut receiver) = ws_stream.split(); + let mut platform_state = state.clone(); + let context_clone = ctx.clone(); + let spawn_state = state.clone(); + + tokio::spawn(async move { + while let Some(api_message) = resp_rx.recv().await { + let send_result = sender + .send(Message::Text(api_message.jsonrpc_msg.clone())) + .await; + match send_result { + Ok(_) => { + if is_service { + ripple_sdk::log::trace!( + "Sent Service response {}", + api_message.jsonrpc_msg + ); + continue; + } + platform_state + .metrics + .update_api_stage(&api_message.request_id, "response"); + + LogSignal::new( + "sent_firebolt_response".to_string(), + "firebolt message sent".to_string(), + context_clone.clone(), + ) + .with_diagnostic_context_item("cid", &connection_id_c.clone()) + .with_diagnostic_context_item("result", &api_message.jsonrpc_msg.clone()) + .emit_debug(); + if let Some(stats) = platform_state + .metrics + .get_api_stats(&api_message.request_id) + { + info!( + "Sending Firebolt response: {:?},{}", + stats.stats_ref, + stats.stats.get_total_time() + ); + if let Some(stats_ref) = stats.stats_ref { + let split = format!( + "Full Firebolt Split: {:?},{}", + stats_ref.clone(), + stats.stats.get_stage_durations() + ); + Self::send_t2_event(&spawn_state, split).await; + } + platform_state + .metrics + .remove_api_stats(&api_message.request_id); + } + + info!( + "Sent Firebolt response cid={} msg={}", + connection_id_c.clone(), + api_message.jsonrpc_msg + ); + } + Err(err) => error!("{:?}", err), + } + } + debug!( + "api msg rx closed {} {} {}", + app_id_c.clone(), + session_id_c.clone(), + connection_id_c.clone() + ); + }); + + let session_id_c = identity.session_id.clone(); + let app_id_c = identity.app_id.clone(); + while let Some(msg) = receiver.next().await { + match msg { + Ok(msg) => { + if msg.is_text() && !msg.is_empty() { + debug!("Received JsonRpc Request {}", msg); + let req_id = Uuid::new_v4().to_string(); + let req_text = String::from(msg.to_text().unwrap()); + if is_service { + match ExtnMessage::try_from(req_text.clone()) { + Ok(v) => { + state.get_client().get_extn_client().handle_message(v); + } + Err(e) => { + error!("failed to parse extn message {:?}", e); + return_invalid_service_error_message(&state, &connection_id, e) + .await; + } + } + continue; + } + let context = { rpc_context.read().unwrap().clone() }; + if let Ok(request) = RpcRequest::parse( + req_text.clone(), + app_id_c.clone(), + session_id_c.clone(), + req_id.clone(), + Some(connection_id.clone()), + gateway_secure, + context, + ) { + info!("Received Firebolt request {}", request.params_json); + let msg = FireboltGatewayCommand::HandleRpc { request }; + if let Err(e) = client.clone().send_gateway_command(msg) { + error!("failed to send request {:?}", e); + } + } else if let Some(response) = JsonRpcApiResponse::get_response(&req_text) { + let msg = FireboltGatewayCommand::HandleResponse { response }; + if let Err(e) = client.clone().send_gateway_command(msg) { + error!("failed to send request {:?}", e); + } + } else { + return_invalid_format_error_message(req_id, &state, &connection_id) + .await; + error!("invalid message {}", req_text) + } + } + } + Err(e) => { + error!("ws error cid={} error={:?}", connection_id, e); + } + } + } + debug!("SESSION DEBUG Unregistering {}", connection_id); + if let Some(symbol) = identity.service_info { + info!( + "Unregistering service connection_id={} app_id={} session_id={}", + connection_id, app_id_c, session_id_c + ); + client + .get_extn_client() + .remove_sender(app_id_c.clone(), symbol); + } + let msg = FireboltGatewayCommand::UnregisterSession { + session_id: identity.session_id.clone(), + cid: connection_id, + }; + if let Err(e) = client.send_gateway_command(msg) { + error!("Error Unregistering {:?}", e); + } + } + #[cfg(not(feature = "ssda"))] async fn handle_app_connection( _client_addr: SocketAddr, ws_stream: WebSocketStream, @@ -469,46 +794,8 @@ impl FireboltWs { error!("Error Unregistering {:?}", e); } } - - async fn handle_connection( - _client_addr: SocketAddr, - ws_stream: WebSocketStream, - connect_rx: oneshot::Receiver, - state: PlatformState, - gateway_secure: bool, - ) { - let identity = connect_rx.await.unwrap(); - - // Generate a unique connection ID - let connection_id = Uuid::new_v4().to_string(); - - if let Some(symbol) = identity.service_info.clone() { - // Handle service connection - ServiceControllerState::handle_service_connection( - _client_addr, - ws_stream, - state, - identity, - connection_id, - symbol, - ) - .await; - } else { - // Handle app connection - Self::handle_app_connection( - _client_addr, - ws_stream, - state, - identity, - connection_id, - gateway_secure, - ) - .await; - } - } } - -/* +#[cfg(feature = "ssda")] async fn return_invalid_service_error_message( state: &PlatformState, connection_id: &str, @@ -534,7 +821,7 @@ async fn return_invalid_service_error_message( let _ = session.send_json_rpc(msg.into()).await; } } -*/ + async fn return_invalid_format_error_message( req_id: String, state: &PlatformState, diff --git a/core/main/src/service/apps/delegated_launcher_handler.rs b/core/main/src/service/apps/delegated_launcher_handler.rs index 6680c39e2..147b00705 100644 --- a/core/main/src/service/apps/delegated_launcher_handler.rs +++ b/core/main/src/service/apps/delegated_launcher_handler.rs @@ -829,6 +829,7 @@ impl DelegatedLauncherHandler { if platform_state .endpoint_state .has_rule("ripple.reportSessionUpdate") + .await { if let Ok(AppManagerResponse::Session(a)) = app_manager_response { let params = serde_json::to_value(a).unwrap(); @@ -849,6 +850,7 @@ impl DelegatedLauncherHandler { if platform_state .endpoint_state .has_rule("ripple.reportLifecycleStateChange") + .await { let previous_state = platform_state.app_manager_state.get_internal_state(app_id); diff --git a/core/main/src/state/bootstrap_state.rs b/core/main/src/state/bootstrap_state.rs index f46c8d423..db0f2b00b 100644 --- a/core/main/src/state/bootstrap_state.rs +++ b/core/main/src/state/bootstrap_state.rs @@ -15,24 +15,33 @@ // SPDX-License-Identifier: Apache-2.0 // -use std::time::Instant; +use std::{sync::Arc, time::Instant}; use ripple_sdk::{ - api::{apps::AppRequest, manifest::ripple_manifest_loader::RippleManifestLoader}, + api::{ + apps::AppRequest, + manifest::ripple_manifest_loader::RippleManifestLoader, + rules_engine::{RuleEngine, RuleEngineProvider}, + }, + // async_channel::{unbounded, Receiver as CReceiver, Sender as CSender}, + // extn::ffi::ffi_message::CExtnMessage, framework::bootstrap::TransientChannel, log::{error, info, warn}, - tokio::sync::mpsc::{self, Receiver, Sender}, + tokio::{ + self, + sync::mpsc::{self, Receiver, Sender}, + }, utils::error::RippleError, }; +use ssda_types::gateway::ApiGatewayServer; use crate::{ - bootstrap::manifest::apps::LoadAppLibraryStep, - broker::endpoint_broker::{BrokerOutput, BROKER_CHANNEL_BUFFER_SIZE}, - firebolt::firebolt_gateway::FireboltGatewayCommand, - service::extn::ripple_client::RippleClient, + bootstrap::manifest::apps::LoadAppLibraryStep, broker::endpoint_broker::BrokerOutput, + firebolt::firebolt_gateway::FireboltGatewayCommand, service::extn::ripple_client::RippleClient, + state::platform_state::PlatformState, }; -use super::platform_state::PlatformState; +//use super::{extn_state::ExtnState, platform_state::PlatformState}; use env_file_reader::read_file; #[derive(Debug, Clone)] @@ -46,7 +55,7 @@ impl ChannelsState { pub fn new() -> ChannelsState { let (gateway_tx, gateway_tr) = mpsc::channel(32); let (app_req_tx, app_req_tr) = mpsc::channel(32); - let (broker_tx, broker_rx) = mpsc::channel(BROKER_CHANNEL_BUFFER_SIZE); + let (broker_tx, broker_rx) = mpsc::channel(10); ChannelsState { gateway_channel: TransientChannel::new(gateway_tx, gateway_tr), @@ -102,12 +111,24 @@ impl BootstrapState { return Err(RippleError::BootstrapError); }; let app_manifest_result = LoadAppLibraryStep::load_app_library(); + let rules_engine: Arc>> = + Arc::new(tokio::sync::RwLock::new(Box::new(RuleEngine::build( + &extn_manifest, + )))); + + let api_gateway_state: Arc>> = + Arc::new(tokio::sync::Mutex::new(Box::new( + ssda_service::ApiGateway::new(rules_engine.clone()), + ))); + let platform_state = PlatformState::new( extn_manifest, device_manifest, client, app_manifest_result, ripple_version_from_etc(), + api_gateway_state.clone(), + rules_engine.clone(), ); fn ripple_version_from_etc() -> Option { diff --git a/core/main/src/state/platform_state.rs b/core/main/src/state/platform_state.rs index ea84587d3..03db85860 100644 --- a/core/main/src/state/platform_state.rs +++ b/core/main/src/state/platform_state.rs @@ -25,6 +25,7 @@ use ripple_sdk::{ exclusory::ExclusoryImpl, extn_manifest::ExtnManifest, }, + rules_engine::RuleEngineProvider, session::SessionAdjective, }, extn::{ @@ -35,10 +36,12 @@ use ripple_sdk::{ utils::error::RippleError, uuid::Uuid, }; -use std::{collections::HashMap, sync::Arc}; + +use ssda_types::gateway::ApiGatewayServer; +use std::{collections::HashMap, fmt, sync::Arc}; use crate::{ - broker::{endpoint_broker::EndpointBrokerState, rules::rules_engine::RuleEngine}, + broker::endpoint_broker::EndpointBrokerState, firebolt::rpc_router::RouterState, service::{ apps::{ @@ -72,6 +75,19 @@ pub struct DeviceSessionIdentifier { pub device_session_id: Uuid, } +/// A wrapper for `Arc>>` +/// that implements the `Debug` trait. +#[derive(Clone)] +pub struct DebuggableApiGatewayServer( + pub Arc>>, +); + +impl fmt::Debug for DebuggableApiGatewayServer { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "DebuggableApiGatewayServer") + } +} + impl Default for DeviceSessionIdentifier { fn default() -> Self { Self { @@ -92,10 +108,10 @@ impl From for DeviceSessionIdentifier { } } -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct PlatformState { - pub extn_manifest: Arc, - device_manifest: Arc, + pub extn_manifest: ExtnManifest, + device_manifest: DeviceManifest, pub ripple_client: RippleClient, pub app_library_state: AppLibraryState, pub session_state: SessionState, @@ -112,8 +128,55 @@ pub struct PlatformState { pub endpoint_state: EndpointBrokerState, pub lifecycle2_app_state: AppManagerState2_0, pub service_controller_state: ServiceControllerState, + pub services_gateway_api: + Arc>>, +} +impl std::fmt::Debug for PlatformState { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("PlatformState") + .field("extn_manifest", &self.extn_manifest) + .field("device_manifest", &self.device_manifest) + .field("ripple_client", &self.ripple_client) + .field("app_library_state", &self.app_library_state) + .field("session_state", &self.session_state) + .field("cap_state", &self.cap_state) + .field("app_events_state", &self.app_events_state) + .field("provider_broker_state", &self.provider_broker_state) + .field("app_manager_state", &self.app_manager_state) + .field("open_rpc_state", &self.open_rpc_state) + .field("router_state", &self.router_state) + .field("metrics", &self.metrics) + .field("device_session_id", &self.device_session_id) + .finish() + } } +#[cfg(test)] +impl Default for PlatformState { + fn default() -> Self { + use crate::state::bootstrap_state::ChannelsState; + use ripple_sdk::api::rules_engine::RuleEngine; + use ripple_sdk::tokio::sync::Mutex; + use ripple_sdk::tokio::sync::RwLock; + let extn_manifest = ExtnManifest::default(); + let rules_engine: Arc>> = + Arc::new(RwLock::new(Box::new(RuleEngine::build(&extn_manifest)))); + + let api_gateway: Arc>> = + Arc::new(Mutex::new(Box::new(ssda_service::ApiGateway::new( + rules_engine.clone(), + )))); + PlatformState::new( + ExtnManifest::default(), + DeviceManifest::default(), + RippleClient::new(ChannelsState::default()), + Vec::new(), + None, + api_gateway, + rules_engine, + ) + } +} impl PlatformState { pub fn new( extn_manifest: ExtnManifest, @@ -121,23 +184,28 @@ impl PlatformState { client: RippleClient, app_library: Vec, version: Option, + services_gateway_api: Arc< + ripple_sdk::tokio::sync::Mutex>, + >, + rule_engine: Arc< + ripple_sdk::tokio::sync::RwLock>, + >, ) -> PlatformState { let exclusory = ExclusoryImpl::get(&manifest); let broker_sender = client.get_broker_sender(); - let rule_engine = RuleEngine::build(&extn_manifest); let extn_sdks = extn_manifest.extn_sdks.clone(); let provider_registations = extn_manifest.provider_registrations.clone(); let metrics_state = OpMetricState::default(); Self { - extn_manifest: Arc::new(extn_manifest), + extn_manifest, cap_state: CapState::new(manifest.clone()), session_state: SessionState::default(), - device_manifest: Arc::new(manifest.clone()), + device_manifest: manifest.clone(), ripple_client: client.clone(), app_library_state: AppLibraryState::new(app_library), app_events_state: AppEventsState::default(), provider_broker_state: ProviderBrokerState::default(), - app_manager_state: AppManagerState::new(&manifest.configuration.saved_dir.clone()), + app_manager_state: AppManagerState::new(&manifest.configuration.saved_dir), open_rpc_state: OpenRpcState::new(Some(exclusory), extn_sdks, provider_registations), router_state: RouterState::new(), metrics: metrics_state.clone(), @@ -151,6 +219,7 @@ impl PlatformState { client, ), lifecycle2_app_state: AppManagerState2_0::new(), + services_gateway_api: services_gateway_api.clone(), service_controller_state: ServiceControllerState::default(), } } @@ -168,15 +237,15 @@ impl PlatformState { } pub fn get_manifest(&self) -> ExtnManifest { - (*self.extn_manifest).clone() + self.extn_manifest.clone() } pub fn get_rpc_aliases(&self) -> HashMap> { - self.extn_manifest.rpc_aliases.clone() + self.extn_manifest.clone().rpc_aliases } pub fn get_device_manifest(&self) -> DeviceManifest { - (*self.device_manifest).clone() + self.device_manifest.clone() } pub fn get_client(&self) -> RippleClient { @@ -230,7 +299,7 @@ impl PlatformState { #[cfg(test)] mod tests { use super::*; - use ripple_sdk::api::manifest::extn_manifest::default_providers; + use ripple_sdk::api::{manifest::extn_manifest::default_providers, rules_engine::RuleEngine}; use ripple_tdk::utils::test_utils::Mockable; impl Mockable for PlatformState { @@ -248,12 +317,25 @@ mod tests { ) .unwrap(); extn_manifest.provider_registrations = default_providers(); + let rules_engine: Arc< + ripple_sdk::tokio::sync::RwLock>, + > = Arc::new(ripple_sdk::tokio::sync::RwLock::new(Box::new( + RuleEngine::build(&extn_manifest), + ))); + + let api_gateway_state: Arc< + ripple_sdk::tokio::sync::Mutex>, + > = Arc::new(ripple_sdk::tokio::sync::Mutex::new(Box::new( + ssda_service::ApiGateway::new(rules_engine.clone()), + ))); Self::new( extn_manifest, manifest, RippleClient::new(ChannelsState::new()), vec![], None, + api_gateway_state.clone(), + rules_engine.clone(), ) } } diff --git a/core/sdk/Cargo.toml b/core/sdk/Cargo.toml index 8c0b59f87..70c5e9d0d 100644 --- a/core/sdk/Cargo.toml +++ b/core/sdk/Cargo.toml @@ -24,12 +24,14 @@ repository = "https://github.com/rdkcentral/Ripple" local_dev = ["jsonrpsee", "sysinfo"] default = [] rpc = ["jsonrpsee"] +jsonrpsee-server = ["jsonrpsee"] tdk = [] full = ["rpc"] sysd = [] test = ["jsonrpsee", "mock"] mock_service = ["mock_app_gw/mock_service"] mock = [] +sdk = ["jsonrpsee-server"] http_contract_tests = [ ] @@ -44,8 +46,7 @@ contract_tests = [ #dependencies inherited from workspace futures-channel.workspace = true -serde.workspace = true -serde_json.workspace = true + chrono.workspace = true tokio = { workspace = true, features = [ "macros", @@ -55,7 +56,7 @@ tokio = { workspace = true, features = [ "time", ] } futures.workspace = true -jsonrpsee = { workspace = true, features=["server"], optional = true } + regex.workspace = true uuid = { workspace = true, features = ["serde", "v5", "v4"] } @@ -73,6 +74,14 @@ tokio-tungstenite = { workspace = true, features = ["handshake", "connect"]} url.workspace = true futures-util = { version = "0.3.28", features = ["sink", "std"], default-features = false} mock_app_gw = { path = "src/service/mock_app_gw", optional = true} +jaq-interpret = { version = "1.5.0", default-features = false } +jaq-parse = { version = "1.0.2", default-features = false } +jaq-core = "1.5.0" +jaq-std = { version = "1.5.1", default-features = false } +mockall = "0.13.1" +jsonrpsee = { workspace = true, features=["server"], optional = true } +serde_json = { version = "1.0", default-features = false} +serde = { version = "1.0", features = ["derive"], default-features = false } sysinfo = {version = "0.30", optional = true } [dev-dependencies] diff --git a/core/sdk/src/api/gateway/rpc_gateway_api.rs b/core/sdk/src/api/gateway/rpc_gateway_api.rs index f3bd92b6b..f3f021b67 100644 --- a/core/sdk/src/api/gateway/rpc_gateway_api.rs +++ b/core/sdk/src/api/gateway/rpc_gateway_api.rs @@ -17,7 +17,7 @@ use jsonrpsee::types::ErrorObject; use serde::{Deserialize, Serialize}; -use serde_json::{json, Value}; +use serde_json::{json, Map, Value}; use tokio::sync::{mpsc, oneshot}; use uuid::Uuid; @@ -259,6 +259,29 @@ impl JsonRpcApiRequest { self.id = Some(id); self } + pub fn as_json(&self) -> serde_json::Value { + serde_json::to_value(self).unwrap_or_default() + } +} +impl From for RpcRequest { + fn from(request: JsonRpcApiRequest) -> Self { + RpcRequest { + ctx: CallContext::default(), + method: request.method, + params_json: serde_json::to_string(&request.params.unwrap_or_default()) + .unwrap_or_default(), + } + } +} +impl From for JsonRpcApiRequest { + fn from(request: RpcRequest) -> Self { + JsonRpcApiRequest { + jsonrpc: "2.0".to_owned(), + id: Some(request.clone().ctx.call_id), + method: request.clone().method, + params: request.get_params_as_map(), + } + } } #[derive(Clone, Default, Debug)] @@ -392,6 +415,9 @@ impl JsonRpcApiResponse { params: None, } } + pub fn from_value(value: Value) -> Result { + serde_json::from_value::(value) + } pub fn update_event_message(&mut self, request: &RpcRequest) { if request.is_rpc_v2() { @@ -523,6 +549,14 @@ impl RpcRequest { self.ctx.cid = Some(cid); self } + pub fn as_json_rpc(&self) -> JsonRpcApiRequest { + JsonRpcApiRequest { + jsonrpc: "2.0".to_owned(), + id: Some(self.ctx.call_id), + method: self.method.clone(), + params: self.get_params_as_map(), + } + } } impl ExtnPayloadProvider for RpcRequest { fn get_extn_payload(&self) -> ExtnPayload { @@ -657,6 +691,14 @@ impl RpcRequest { } None } + pub fn get_params_as_map(&self) -> Option { + if let Ok(params) = serde_json::from_str::(&self.params_json.clone()) { + let params_map: Map = params.as_object().unwrap_or(&Map::new()).clone(); + + return Some(Value::Object(params_map)); + } + None + } pub fn get_new_internal(method: String, params: Option) -> Self { let ctx = CallContext::new( @@ -1055,4 +1097,14 @@ mod tests { let request = serde_json::from_str::(&new.params_json).unwrap(); assert!(!request.listen); } + #[test] + fn test_params_as_map() { + let mut rpc_request = RpcRequest::mock(); + rpc_request.params_json = r#"{"param1": {"key": "value"}}"#.to_string(); + let params = rpc_request.get_params_as_map().unwrap(); + assert!(params.is_object()); + let map = params.as_object().unwrap(); + assert!(map.contains_key("param1")); + assert_eq!(map.get("param1").unwrap(), &json!({"key": "value"})); + } } diff --git a/core/sdk/src/api/mod.rs b/core/sdk/src/api/mod.rs index 2e4e18e1d..5a1f98038 100644 --- a/core/sdk/src/api/mod.rs +++ b/core/sdk/src/api/mod.rs @@ -25,6 +25,7 @@ pub mod default_storage_properties; pub mod device; pub mod manifest; pub mod ripple_cache; +pub mod rules_engine; pub mod session; pub mod settings; pub mod status_update; @@ -69,3 +70,4 @@ pub mod observability { pub mod metrics_util; pub mod operational_metrics; } +pub mod rules; diff --git a/core/main/src/broker/rules/mod.rs b/core/sdk/src/api/rules/mod.rs similarity index 96% rename from core/main/src/broker/rules/mod.rs rename to core/sdk/src/api/rules/mod.rs index 31f7c23dd..4287dc764 100644 --- a/core/main/src/broker/rules/mod.rs +++ b/core/sdk/src/api/rules/mod.rs @@ -15,5 +15,4 @@ // SPDX-License-Identifier: Apache-2.0 // -pub mod rules_engine; pub mod rules_functions; diff --git a/core/main/src/broker/rules/rules_functions.rs b/core/sdk/src/api/rules/rules_functions.rs similarity index 99% rename from core/main/src/broker/rules/rules_functions.rs rename to core/sdk/src/api/rules/rules_functions.rs index 2b6325b00..06ad75db2 100644 --- a/core/main/src/broker/rules/rules_functions.rs +++ b/core/sdk/src/api/rules/rules_functions.rs @@ -17,7 +17,7 @@ use std::collections::HashMap; -use ripple_sdk::{log::error, utils::error::RippleError}; +use crate::{log::error, utils::error::RippleError}; use serde::Deserialize; #[derive(Debug, Clone, Default, Deserialize)] diff --git a/core/main/src/broker/rules/rules_engine.rs b/core/sdk/src/api/rules_engine/mod.rs similarity index 81% rename from core/main/src/broker/rules/rules_engine.rs rename to core/sdk/src/api/rules_engine/mod.rs index 654b2163e..6e9215768 100644 --- a/core/main/src/broker/rules/rules_engine.rs +++ b/core/sdk/src/api/rules_engine/mod.rs @@ -1,3 +1,14 @@ +use std::collections::HashMap; + +use chrono::Utc; +use log::{debug, error, info, trace, warn}; +use serde::{Deserialize, Serialize}; +use serde_json::Value; + +use crate::utils::error::RippleError; + +use super::{gateway::rpc_gateway_api::RpcRequest, manifest::extn_manifest::ExtnManifest}; + // Copyright 2023 Comcast Cable Communications Management, LLC // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,23 +26,23 @@ // SPDX-License-Identifier: Apache-2.0 // use jaq_interpret::{Ctx, FilterT, ParseCtx, RcIter, Val}; -use ripple_sdk::api::{ - gateway::rpc_gateway_api::RpcRequest, manifest::extn_manifest::ExtnManifest, -}; - -use ripple_sdk::{ - chrono::Utc, - log::{debug, error, info, trace, warn}, - serde_json::Value, - utils::error::RippleError, -}; -use serde::{Deserialize, Serialize}; -use std::collections::HashMap; +// use ripple_sdk::api::{ +// gateway::rpc_gateway_api::RpcRequest, manifest::extn_manifest::ExtnManifest, +// }; + +// use ripple_sdk::{ +// chrono::Utc, +// log::{debug, error, info, trace, warn}, +// serde_json::Value, +// utils::error::RippleError, +// }; +// use serde::{Deserialize, Serialize}; +// use std::collections::HashMap; use std::sync::{Mutex, MutexGuard, Once}; use std::{fs, path::Path}; -use super::rules_functions::{apply_functions, RulesFunction, RulesImport}; +use crate::api::rules::rules_functions::{apply_functions, RulesFunction, RulesImport}; static BASE_PARSE_CTX_INIT: Once = Once::new(); static mut BASE_PARSE_CTX_PTR: Option> = None; @@ -104,7 +115,7 @@ pub enum RuleEndpointProtocol { Extn, Service, } -#[derive(Debug, Default, Clone, Serialize, Deserialize)] +#[derive(Debug, Default, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)] pub struct JsonDataSource { // configurable namespace to "stuff" an in individual result payload into pub namespace: Option, @@ -112,14 +123,14 @@ pub struct JsonDataSource { pub params: Option, } -#[derive(Debug, Default, Clone, Serialize, Deserialize)] +#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)] pub struct EventHandler { pub method: String, #[serde(skip_serializing_if = "Option::is_none")] pub params: Option, } -#[derive(Debug, Default, Clone, Serialize, Deserialize)] +#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)] pub struct Rule { pub alias: String, // Not every rule needs transform @@ -193,7 +204,7 @@ impl Rule { } } -#[derive(Debug, Default, Clone, Serialize, Deserialize)] +#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] pub struct RuleTransform { #[serde(skip_serializing_if = "Option::is_none")] pub request: Option, @@ -223,7 +234,6 @@ impl RuleTransform { output } - pub fn apply_functions(&mut self, imports: &HashMap) { if let Some(transform) = self.request.take() { if let Ok(transformed) = apply_functions(&transform, imports) { @@ -237,7 +247,6 @@ impl RuleTransform { } } } - pub fn apply_variables(&mut self, rpc_request: &RpcRequest) -> &mut Self { if let Some(value) = self.request.take() { let _ = self @@ -265,6 +274,27 @@ impl RuleTransform { self } + pub fn apply_context(&mut self, rpc_request: &RpcRequest) -> &mut Self { + if let Some(value) = self.request.take() { + let _ = self + .request + .insert(self.check_and_replace(&value, rpc_request)); + } + + if let Some(value) = self.event.take() { + let _ = self + .event + .insert(self.check_and_replace(&value, rpc_request)); + } + + if let Some(value) = self.rpcv2_event.take() { + let _ = self + .rpcv2_event + .insert(self.check_and_replace(&value, rpc_request)); + } + self + } + pub fn get_transform_data(&self, typ: RuleTransformType) -> Option { match typ { RuleTransformType::Request => self.request.clone(), @@ -410,8 +440,13 @@ impl RuleEngine { match filtered_rules.len() { 1 => Ok(RuleRetrieved::WildcardMatch(filtered_rules[0].clone())), - 0 => Err(RuleRetrievalError::RuleNotFoundAsWildcard), - _ => Err(RuleRetrievalError::TooManyWildcardMatches), + 0 => Err(RuleRetrievalError::RuleNotFoundAsWildcard( + method.to_string(), + )), + _ => Err(RuleRetrievalError::TooManyWildcardMatches(format!( + "too many matches for method={}. Rules that match={:?}", + method, filtered_rules + ))), } } @@ -423,9 +458,11 @@ impl RuleEngine { rule.transform.apply_variables(rpc_request); } - pub fn get_rule(&self, rpc_request: &RpcRequest) -> Result { + pub fn retrieve_rule( + &self, + rpc_request: &RpcRequest, + ) -> Result { let method = rpc_request.method.to_lowercase(); - /* match directly from method name */ @@ -442,7 +479,7 @@ impl RuleEngine { } } - pub fn get_rule_by_method(&self, method: &str) -> Option { + pub fn retrieve_rule_by_method(&self, method: &str) -> Option { self.rules.rules.get(&method.to_lowercase()).cloned() } } @@ -463,8 +500,8 @@ impl From for Rule { #[derive(Debug)] pub enum RuleRetrievalError { RuleNotFound(String), - RuleNotFoundAsWildcard, - TooManyWildcardMatches, + RuleNotFoundAsWildcard(String), + TooManyWildcardMatches(String), } /// Compiles and executes a JQ filter on a given JSON input value. @@ -492,7 +529,7 @@ pub enum RuleRetrievalError { /// ``` /// use serde_json::json; /// use ripple_sdk::utils::error::RippleError; -/// use crate::jq_compile; +/// use ripple_sdk::api::rules_engine::jq_compile; /// /// let filter = "if .success then .stbVersion else { code: -32100, message: \"couldn't get version\" } end"; /// let input = json!({ @@ -586,11 +623,83 @@ pub fn compose_json_values(values: Vec) -> Value { pub fn make_name_json_safe(name: &str) -> String { name.replace([' ', '.', ','], "_") } +use mockall::automock; +#[automock] +#[async_trait::async_trait] +pub trait RuleEngineProvider: Send + Sync { + fn add_rules(&mut self, rules: RuleSet); + fn add_rule(&mut self, rule: Rule); + fn remove_rule(&mut self, alias: &str); + fn has_rule(&self, request: &str) -> bool; + fn wildcard_match(&self, rule_name: &str, method: &str) -> bool; + fn find_wildcard_rule( + &self, + rules: &HashMap, + method: &str, + ) -> Result; + fn get_rule(&self, rpc_request: &RpcRequest) -> Result; + fn get_rule_by_method(&self, method: &str) -> Option; + fn get_rules(&self) -> RuleSet; +} + +#[async_trait::async_trait] +impl RuleEngineProvider for RuleEngine { + fn add_rules(&mut self, rules: RuleSet) { + self.rules.append(rules); + } + fn add_rule(&mut self, rule: Rule) { + debug!("Adding rule: {:?}", rule); + self.rules.rules.insert(rule.alias.clone(), rule); + } + fn remove_rule(&mut self, alias: &str) { + debug!("Removing rule: {}", alias); + self.rules.rules.remove(alias); + } + fn has_rule(&self, request: &str) -> bool { + self.rules.rules.contains_key(&request.to_lowercase()) + } + fn wildcard_match(&self, rule_name: &str, method: &str) -> bool { + rule_name.ends_with(".*") && method.starts_with(&rule_name[..rule_name.len() - 2]) + } + fn find_wildcard_rule( + &self, + rules: &HashMap, + method: &str, + ) -> Result { + let filtered_rules: Vec<&Rule> = rules + .iter() + .filter(|(rule_name, _)| Self::wildcard_match(rule_name, method)) + .map(|(_, rule)| rule) + .collect(); + + match filtered_rules.len() { + 1 => Ok(RuleRetrieved::WildcardMatch(filtered_rules[0].clone())), + 0 => Err(RuleRetrievalError::RuleNotFoundAsWildcard( + method.to_string(), + )), + _ => Err(RuleRetrievalError::TooManyWildcardMatches( + method.to_string(), + )), + } + } + fn get_rule(&self, rpc_request: &RpcRequest) -> Result { + self.retrieve_rule(rpc_request) + } + fn get_rule_by_method(&self, method: &str) -> Option { + self.retrieve_rule_by_method(method) + } + fn get_rules(&self) -> RuleSet { + self.rules.clone() + } +} #[cfg(test)] mod tests { + + use crate::api::gateway::rpc_gateway_api::CallContext; + use super::*; - use ripple_sdk::api::gateway::rpc_gateway_api::RpcRequest; + use ripple_sdk::serde_json::json; #[test] @@ -694,7 +803,6 @@ mod tests { .unwrap() .contains("nested")); } - use ripple_sdk::api::gateway::rpc_gateway_api::CallContext; #[test] fn test_get_rule_exact_match() { @@ -766,17 +874,43 @@ mod tests { #[test] fn test_get_rule_no_match() { - let rule_set = RuleSet::default(); + let rule_engine = RuleEngine::default(); + + let rpc_request = RpcRequest { + method: "nonexistent.method".to_string(), + ctx: CallContext { + app_id: "test_app".to_string(), + method: "nonexistent.method".to_string(), + ..Default::default() + }, + ..Default::default() + }; + + let result = rule_engine.get_rule(&rpc_request); + println!("Result: {:?}", result); + assert!( + matches!(result, Err(RuleRetrievalError::RuleNotFoundAsWildcard(method)) if method == "nonexistent.method") + ); + } + + #[test] + fn test_get_rule_multiple_wildcard_matches() { + let mut rule_set = RuleSet::default(); + rule_set + .rules + .insert("api.v1.*".to_string(), Rule::default()); + rule_set.rules.insert("api.*".to_string(), Rule::default()); + let rule_engine = RuleEngine { rules: rule_set, functions: HashMap::default(), }; let rpc_request = RpcRequest { - method: "nonexistent.method".to_string(), + method: "api.v1.get".to_string(), ctx: CallContext { app_id: "test_app".to_string(), - method: "nonexistent.method".to_string(), + method: "api.v1.get".to_string(), ..Default::default() }, ..Default::default() @@ -785,7 +919,7 @@ mod tests { let result = rule_engine.get_rule(&rpc_request); assert!(matches!( result, - Err(RuleRetrievalError::RuleNotFoundAsWildcard) + Err(RuleRetrievalError::TooManyWildcardMatches(_)) )); } } diff --git a/core/sdk/src/lib.rs b/core/sdk/src/lib.rs index 089be03ce..a496c961b 100644 --- a/core/sdk/src/lib.rs +++ b/core/sdk/src/lib.rs @@ -38,10 +38,11 @@ pub extern crate serde_yaml; pub extern crate tokio; pub extern crate tokio_tungstenite; pub extern crate uuid; - pub trait Mockable { fn mock() -> Self; } +#[cfg(feature = "jsonrpsee-server")] +pub use jsonrpsee; pub type JsonRpcErrorType = jsonrpsee::core::error::Error; pub type JsonRpcErrorCode = jsonrpsee::types::error::ErrorCode; diff --git a/device/thunder_ripple_sdk/Cargo.toml b/device/thunder_ripple_sdk/Cargo.toml index bda7b0629..171940856 100644 --- a/device/thunder_ripple_sdk/Cargo.toml +++ b/device/thunder_ripple_sdk/Cargo.toml @@ -54,8 +54,10 @@ contract_tests = [ local_dev=[] mock=[] + [dependencies] base64 = { workspace = true, default-features = false, features=["alloc"] } +base64ct = {version = "=1.7.3"} ripple_tdk = { path = "../../core/tdk" } ripple_sdk.workspace = true regex.workspace = true @@ -68,7 +70,7 @@ futures.workspace = true strum = { version = "0.24", default-features = false } strum_macros = "0.24" -pact_consumer = { version = "1.0.0", optional = true } +pact_consumer = { version = "=1.0.0", optional = true } reqwest = { version = "0.11", optional = true, default-features = false } expectest = { version = "0.12.0", optional = true } maplit = { version = "1.0.2", optional = true } @@ -82,4 +84,4 @@ futures-util = { version = "0.3.28", features = ["sink", "std"], default-feature [dev-dependencies] ripple_sdk = { path = "../../core/sdk", features = ["tdk"] } -rstest = "0.18.2" \ No newline at end of file +rstest = "0.18.2" diff --git a/examples/tm_extn/src/tm_ffi.rs b/examples/tm_extn/src/tm_ffi.rs index 9a27b0fde..1e20f023a 100644 --- a/examples/tm_extn/src/tm_ffi.rs +++ b/examples/tm_extn/src/tm_ffi.rs @@ -61,7 +61,7 @@ export_extn_metadata!(CExtnMetadata, init_library); fn start_launcher(sender: ExtnSender, receiver: CReceiver) { let _ = init_logger("tm".into()); info!("Starting tm channel"); - let mut client = ExtnClient::new(receiver, sender); + let mut client = ExtnClient::new_extn(receiver); if !client.check_contract_permitted(RippleContract::OperationalMetricListener) { let _ = client.event(ExtnStatus::Error); diff --git a/ssda/.gitignore b/ssda/.gitignore new file mode 100644 index 000000000..ea8c4bf7f --- /dev/null +++ b/ssda/.gitignore @@ -0,0 +1 @@ +/target diff --git a/ssda/Cargo.toml b/ssda/Cargo.toml new file mode 100644 index 000000000..c353a9b97 --- /dev/null +++ b/ssda/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] +resolver = "2" +members = [ "example-service", "ssda_client","ssda_service", "ssda_types"] + + diff --git a/ssda/README.md b/ssda/README.md new file mode 100644 index 000000000..d88d2c701 --- /dev/null +++ b/ssda/README.md @@ -0,0 +1,4 @@ +#update ripple_sdk git dependency +``` +cargo update -p ripple_sdk +``` diff --git a/ssda/example-service/Cargo.toml b/ssda/example-service/Cargo.toml new file mode 100644 index 000000000..f2d595994 --- /dev/null +++ b/ssda/example-service/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "example-service" +version = "0.1.0" +edition = "2021" + +[dependencies] +async-trait = "0.1.88" +serde_json = "1.0.140" +ssda_types = {path="../ssda_types"} +ssda_client = {path = "../ssda_client"} +tokio = "1.44.2" +mockall = "0.13.1" +tokio-websockets = "0.11.4" +log = "0.4.27" +ripple_sdk = { git = "https://github.com/rdkcentral/Ripple.git", branch = "ssda", subdir = "core/sdk" } +#jsonrpsee-server = "0.24.9" +#jsonrpsee-types = "0.24.9" +#jsonrpsee-proc-macros = "0.24.9" +#jsonrpsee-core = "0.24.9" +jsonrpsee = {version= "0.24.9", features= ["server", "macros"] } +pprof = { version = "0.15", features = ["flamegraph"] } +#jsonrpsee = { version = "0.16.3", features = ["server", "macros"] } + +[profile.release] +strip=true +opt-level="z" +codegen-units=1 +lto=true +panic="abort" diff --git a/ssda/example-service/flamegraph.svg b/ssda/example-service/flamegraph.svg new file mode 100644 index 000000000..e69de29bb diff --git a/ssda/example-service/src/main.rs b/ssda/example-service/src/main.rs new file mode 100644 index 000000000..38752f8d7 --- /dev/null +++ b/ssda/example-service/src/main.rs @@ -0,0 +1,121 @@ +use std::fs::File; +use std::{sync::Arc, vec}; + +use async_trait::async_trait; +use jsonrpsee::core::RpcResult; +use jsonrpsee::proc_macros::rpc; +use log::info; +use serde_json::json; +use ssda_client::APIGatewayClientBuilder; +use ssda_types::client::ServiceRequestHandlerImpl; +use ssda_types::service::{ + FireboltMethodHandlerAPIRegistration, FireboltMethodHandlerRegistration, ServiceCall, + ServiceCallErrorResponse, ServiceCallSuccessResponse, ServiceRegistration, +}; +use ssda_types::{JqRule, ServiceId}; + +#[rpc(server)] +pub trait ExampleServiceRpc { + #[method(name = "device.audio")] + async fn audio(&self) -> RpcResult; + #[method(name = "device.make")] + async fn make(&self) -> RpcResult; +} + +#[derive(Debug, Clone)] +pub struct ExampleService {} + +impl ExampleService { + pub fn new() -> Self { + Self {} + } +} + +#[async_trait] +impl ExampleServiceRpcServer for ExampleService { + async fn audio(&self) -> RpcResult { + Ok("Audio response".to_string()) + } + async fn make(&self) -> RpcResult { + Ok("Make response".to_string()) + } +} + +#[async_trait::async_trait] +impl ServiceRequestHandlerImpl for ExampleService { + fn register(&self) -> Vec { + vec![] + } + fn handle_request( + &self, + request: ServiceCall, + ) -> Result { + // Handle the request and return a response + info!("handle_request: handling request {:?}", request); + match request.method.as_str() { + "device.audio" => Ok(ServiceCallSuccessResponse { + request_id: request.request_id, + response: serde_json::json!({ "status": "success" }), + }), + bad_method => { + return Err(ServiceCallErrorResponse { + request_id: request.request_id, + error: format!("Unknown method: {}", bad_method), + }); + } + } + } + fn on_connected(&self) { + println!("example connected") + } + fn on_disconnected(&self) { + println!("disconnected") + } + fn healthy(&self) -> bool { + todo!() + } +} +#[tokio::main] +async fn main() { + let module = ExampleService::new().into_rpc(); + // let request = json!({ + // "jsonrpc": "2.0", + // "id": 1, + // "method": "device.aasdf", + // "params": [] + // }); + // let request= serde_json::to_string(&request).unwrap(); + // let f = module.raw_json_request(&request, 1).await.unwrap(); + // println!("Response: {:?}", f); + + let my_handler = ExampleService::new(); + let mut firebolt_handlers = Vec::new(); + + firebolt_handlers.push(FireboltMethodHandlerAPIRegistration { + firebolt_method: "device.audio".to_string(), + jq_rule: Some(JqRule { + alias: "device.audio".to_string(), + rule: "jq_type".to_string(), + }), + }); + firebolt_handlers.push(FireboltMethodHandlerAPIRegistration { + firebolt_method: "device.make".to_string(), + jq_rule: Some(JqRule { + alias: "device.make".to_string(), + rule: "jq_type".to_string(), + }), + }); + + let registration = ServiceRegistration { + service_id: ServiceId { + service_id: "example".to_string(), + }, + firebolt_handlers: firebolt_handlers, + }; + + let _ = APIGatewayClientBuilder::::new(registration) + .websocket() + .build(module) + .start() + .await; +} diff --git a/ssda/rust-toolchain.toml b/ssda/rust-toolchain.toml new file mode 100644 index 000000000..2e2b8c852 --- /dev/null +++ b/ssda/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "1.82.0" diff --git a/ssda/ssda_client/Cargo.toml b/ssda/ssda_client/Cargo.toml new file mode 100644 index 000000000..23a554036 --- /dev/null +++ b/ssda/ssda_client/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "ssda_client" +version = "0.1.0" +edition = "2021" + +[dependencies] +tokio = { version = "1", features = ["full"] } +ws = "0.9.2" +log = "0.4" +serde = { version = "1.0", features = ["derive"] } +ssda_types = {path = "../ssda_types"} +url = "2.5.4" +ripple_sdk = { path="../../core/sdk" } +jsonrpsee = {version= "0.24.9", features= ["server", "macros"] } \ No newline at end of file diff --git a/ssda/ssda_client/src/lib.rs b/ssda/ssda_client/src/lib.rs new file mode 100644 index 000000000..b7af0e0e1 --- /dev/null +++ b/ssda/ssda_client/src/lib.rs @@ -0,0 +1,41 @@ +use jsonrpsee::RpcModule; +use ssda_types::{service::ServiceRegistration, APIGatewayClient, WebsocketAPIGatewayClient}; +pub enum APIGatewayClientTransport { + WebSocket, +} +pub struct APIGatewayClientBuilder { + transport: APIGatewayClientTransport, + service_registration: ServiceRegistration, +} + +impl APIGatewayClientBuilder { + pub fn new(service_registration: ServiceRegistration) -> Self { + APIGatewayClientBuilder { + transport: APIGatewayClientTransport::WebSocket, + service_registration, + } + } + pub fn with_service_registration( + &mut self, + service_registration: ServiceRegistration, + ) -> &mut Self { + self.service_registration = service_registration; + self + } + + pub fn websocket(&mut self) -> &mut Self { + // Initialize WebSocket client + self.transport = APIGatewayClientTransport::WebSocket; + self + } + + pub fn build(&self, rpc_server: RpcModule) -> Box { + let methods = rpc_server.clone(); + match self.transport { + APIGatewayClientTransport::WebSocket => Box::new(WebsocketAPIGatewayClient::new( + methods, + self.service_registration.service_id.clone(), + )), + } + } +} diff --git a/ssda/ssda_service/Cargo.toml b/ssda/ssda_service/Cargo.toml new file mode 100644 index 000000000..73737a838 --- /dev/null +++ b/ssda/ssda_service/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "ssda_service" +version = "0.1.0" +edition = "2021" + +[dependencies] +async-trait = "0.1.88" +serde_json = "1.0.140" +ssda_types = {path="../ssda_types"} + +ripple_sdk = { path="../../core/sdk" } + +tokio = "1.44.2" +mockall = "0.13.1" +tokio-tungstenite = "0.20.1" +http = "0.2.12" +url = "2.5.4" +log = "0.4.27" +futures-util = "0.3.31" +env_logger = "0.11.8" diff --git a/ssda/ssda_service/src/lib.rs b/ssda/ssda_service/src/lib.rs new file mode 100644 index 000000000..9f1d0637c --- /dev/null +++ b/ssda/ssda_service/src/lib.rs @@ -0,0 +1,731 @@ +/* +This is the API gateway, and it meant to be hosted in the main ripple process +*/ + +use std::{collections::HashMap, f32::consts::E, sync::Arc}; + +use futures_util::stream::SplitSink; +use futures_util::stream::StreamExt; +use futures_util::SinkExt; +use http::Uri; +use log::debug; +use log::{error, info}; + +use ripple_sdk::api::gateway::rpc_gateway_api::JsonRpcApiRequest; +use ripple_sdk::api::rules_engine::{Rule, RuleEngineProvider, RuleTransform}; +use tokio::sync::{mpsc, RwLock}; +pub struct ServiceMap { + service_map: HashMap>, +} +impl ServiceMap { + fn new() -> Self { + Self { + service_map: HashMap::new(), + } + } + fn add_service( + &mut self, + service_id: ServiceId, + registrations: Vec, + ) { + self.service_map.insert(service_id, registrations); + } + fn remove_service(&mut self, service_id: &ServiceId) { + self.service_map.remove(service_id); + } + fn get_registrations( + &self, + service_id: &ServiceId, + ) -> Vec { + self.service_map.get(service_id).unwrap_or(&vec![]).clone() + } + fn get_service_for_method( + &self, + method: &str, + ) -> Option<(ServiceId, FireboltMethodHandlerAPIRegistration)> { + for (service_id, registrations) in self.service_map.iter() { + for registration in registrations.iter() { + if registration.firebolt_method == method { + return Some((service_id.clone(), registration.clone())); + } + } + } + None + } +} +pub struct WebSocketChannels { + pub tx: mpsc::Sender, + pub rx: mpsc::Receiver, +} +pub struct ApiGateway { + pub service_endpoints: Arc>>>, + pub rules_engine: Arc>>, + pub methods_2_services: Arc>, + pub broker_sender: mpsc::Sender, + pub services_2_rxs: + Arc>>>, + pub reply_to_tx: mpsc::Sender, +} +pub enum APIGatewayClientState { + Failed(String), + Connecting, + Registering(APIGatewayServiceRegistrationRequest), + Connected, + Closed, + Message(ServiceRequestId, Value), + ServiceCallFailed(ServiceRequestId, String), +} +type RequestIds2SendersType = Arc< + RwLock< + HashMap>>>>, + >, +>; + +impl ApiGateway { + pub fn new( + rules_engine: Arc>>, + ) -> Self { + let (tx, rx) = mpsc::channel::(32); + let (reply_to_tx, _) = mpsc::channel::(32); + let services_2_rxs = Arc::new(tokio::sync::RwLock::new(HashMap::new())); + + let me = Self { + service_endpoints: Arc::new(tokio::sync::RwLock::new(HashMap::new())), + rules_engine, + methods_2_services: Arc::new(tokio::sync::RwLock::new(ServiceMap::new())), + broker_sender: tx.clone(), + services_2_rxs: services_2_rxs.clone(), + reply_to_tx, + }; + + me.start(rx, Arc::clone(&services_2_rxs)); + + me + } + /* + start the task that handles the service routing requests, and routes them to the appropriate service + */ + + pub fn start( + &self, + mut rx: mpsc::Receiver, + services_2_rxes: Arc< + tokio::sync::RwLock>>, + >, + ) { + let methods_2_services = self.methods_2_services.clone(); + let services_2_rxs = Arc::clone(&services_2_rxes); + tokio::spawn(async move { + while let Some(request) = rx.recv().await { + let methods_2_services = methods_2_services.read().await; + let method = request.payload.method.clone(); + match methods_2_services.get_service_for_method(method.as_str()) { + Some((service_id, _registration)) => { + /* + send the request to the websocket handler for the service + */ + if let Some(service) = services_2_rxs.read().await.get(&service_id) { + let (send, receive) = oneshot::channel::(); + let method = request.payload.method.clone(); + let payload = request.payload.clone(); + debug!("sending rpc request to service: {:?}", payload); + + let payload: JsonRpcApiRequest = payload.into(); + info!( + "------------------ JSONRPC Request: {:?}, from rpc_request: {:?}", + payload, payload + ); + let websocket_request = WebsocketServiceRequest { + method: method.clone(), + request_id: request.request_id.clone(), + payload: payload.as_json(), + respond_to: send, + }; + info!( + "------------------ Sending WebsocketServiceRequest to service:{:?}", + websocket_request + ); + let _ = service.send(websocket_request).await; + match receive.await { + Ok(response) => { + info!("start Received response from service: {:?}", response); + match response { + WebsocketServiceResponse::Success(request_id, result) => { + info!("Received success response: {:?}", result); + // Send the response back to the broker + let _ = request.respond_to.send( + ServiceRoutingResponse::Success( + ServiceRoutingSuccessResponse { + request_id, + response: result, + }, + ), + ); + } + WebsocketServiceResponse::Error(request_id, error) => { + info!("Received error response: {:?}", error); + // Send the error response back to the broker + let _ = request.respond_to.send( + ServiceRoutingResponse::Error( + ServiceRoutingErrorResponse { + request_id, + error, + }, + ), + ); + } + } + } + Err(e) => { + error!("Failed to receive response from service: {:?}", e); + } + } + } else { + error!( + "else No service sender found for method: {:?} in {:?} ", + method, + services_2_rxs.read().await + ); + } + } + + None => { + error!("None No service found for method: {:?}", method); + } + } + } + }); + } + /// This function is used to check if the given URI is a service URL + pub fn is_apigateway_connection( + uri: &http::Uri, + ) -> Result { + // Check if the URI is a service URL + // This is a placeholder implementation + if !uri.path().starts_with("/apigateway") { + return Err(APIGatewayServiceConnectionError::NotAService); + } + + if let Some(query) = uri.query() { + let query_pairs = form_urlencoded::parse(query.as_bytes()); + for (key, value) in query_pairs { + if key == "serviceId" { + return Ok(APIGatewayServiceConnectionDisposition::Accept( + ServiceId::new(value.to_string()), + )); + } + } + } + Err(APIGatewayServiceConnectionError::NotAService) + } + pub fn classify_message(message: &Message) -> APIGatewayClientState { + // Classify the message based on its content + // This is a placeholder implementation + if let Message::Text(text) = message { + let parsed: Result = serde_json::from_str(text); + info!("Parsed message: {:?}", parsed); + if let Ok(msg) = parsed { + match msg { + APIClientMessages::Register(registration) => { + return APIGatewayClientState::Registering(registration); + } + + APIClientMessages::ServiceCallSuccessResponse(succcess) => { + return APIGatewayClientState::Message( + succcess.request_id, + succcess.response, + ) + } + APIClientMessages::ServiceCallErrorResponse(error) => { + return APIGatewayClientState::ServiceCallFailed( + error.request_id, + error.error, + ); + } + e => { + info!("Classified message: {:?}", e); + return APIGatewayClientState::Failed(format!( + "Unknown message type: {:?}", + e + )); + } + } + } + } else if let Message::Close(_) = message { + return APIGatewayClientState::Closed; + } + + APIGatewayClientState::Failed(format!("Failed to parse message: {}", message)) + } + fn jq_rule_to_string(jq_rule: Option) -> Option { + if let Some(rule) = jq_rule { + return Some(rule.rule); + } + None + } + async fn handle_registration( + service_id: ServiceId, + rule_engine: Arc>>, + registration: &APIGatewayServiceRegistrationRequest, + methods_2_services: Arc>, + ) { + let rules = ®istration.firebolt_handlers; + + let mut rule_engine = rule_engine.write().await; + + for handle_rule in rules { + let rule = Rule { + alias: handle_rule.firebolt_method.clone(), + transform: RuleTransform::default(), + filter: Self::jq_rule_to_string(handle_rule.jq_rule.clone()), + event_handler: None, + endpoint: Some("service".to_string()), + sources: None, + }; + /* + todo, save off existing rule for rollback (if it exists) + */ + rule_engine.add_rule(rule); + } + methods_2_services + .write() + .await + .add_service(service_id.clone(), rules.clone()); + } + async fn handle_unregister( + service_id: &ServiceId, + methods_2_services: Arc>, + rule_engine: Arc>>, + ) { + let mut rule_engine = rule_engine.write().await; + let aliases = methods_2_services + .read() + .await + .get_registrations(service_id); + for handle_rule in aliases { + rule_engine.remove_rule(&handle_rule.firebolt_method); + } + methods_2_services.write().await.remove_service(service_id); + } + + async fn handle_message( + message: Result, + tx: &mut SplitSink, Message>, + service_id: &ServiceId, + rule_engine: Arc>>, + methods_2_services: Arc>, + services_2_rxes: Arc< + tokio::sync::RwLock>>, + >, + bridge_tx: mpsc::Sender, + ) { + match message { + Ok(msg) => { + info!("gateway server received websocket message: {:?}", msg); + match Self::classify_message(&msg) { + APIGatewayClientState::Registering(registration) => { + info!("Registering service: {:?}", registration); + Self::handle_unregister( + service_id, + methods_2_services.clone(), + rule_engine.clone(), + ) + .await; + + Self::handle_registration( + service_id.clone(), + rule_engine.clone(), + ®istration, + methods_2_services.clone(), + ) + .await; + + let response: APIGatewayServiceRegistrationResponse = registration.into(); + let response = APIClientMessages::Registered(response); + let msg = serde_json::to_string(&response); + match msg { + Ok(msg) => { + let _ = tx.send(Message::Text(msg.clone())).await; + info!("Sending registration response: {:?}", msg); + } + Err(e) => { + error!("Failed to serialize registration response: {:?}", e); + } + } + + info!("Sent registration response: {:?}", response) + } + APIGatewayClientState::Failed(e) => { + error!("Failed to classify message {} err {},{}", msg, E, e); + + let _ = tx.send(Message::Close(None)).await; + let _ = tx.close().await; + } + APIGatewayClientState::Closed => { + info!("Client closed connection"); + Self::handle_unregister(service_id, methods_2_services, rule_engine).await; + let _ = tx.close().await; + } + APIGatewayClientState::Message(request_id, msg) => { + info!( + "got msg from websocket: {} for request {:?}", + msg, request_id + ); + let _ = bridge_tx + .send(WebsocketServiceResponse::Success(request_id, msg)) + .await; + } + APIGatewayClientState::ServiceCallFailed(id, error) => { + info!("Service call failed: {:?}", error); + let fail = WebsocketServiceResponse::Error(id, error); + let _ = bridge_tx.send(fail).await; + } + + _ => { + info!("handle_message: Unknown message type: {:?}", msg); + let _ = tx.send(Message::Close(None)).await; + let _ = tx.close().await; + } + } + } + Err(e) => { + /* + for now, just treat all errors as fatal and cleanup, drop tx and let client reconnect + */ + info!("Client closed connection. Error: {:?}", e); + info!("Unregistering service: {:?}", service_id); + + Self::handle_unregister(service_id, methods_2_services, rule_engine.clone()).await; + let _ = services_2_rxes.write().await.remove(service_id); + + let _ = tx.close().await; + } + } + } + /* + Spawn a task to handle exactly one service connection over websocket (for now, maybe dbus later) + */ + + async fn handle_service_connection( + service_id: ServiceId, + ws_stream: tokio_tungstenite::WebSocketStream, + rule_engine: Arc>>, + methods_2_services: Arc>, + mut websocket_service_request_rx: mpsc::Receiver, + service_2_rxes: Arc< + tokio::sync::RwLock>>, + >, + ) -> Result<(), APIGatewayServiceConnectionError> { + info!("Handling service connection: {:?}", service_id); + + let (mut websocket_tx, mut websocket_rx) = ws_stream.split(); + + let (bridge_tx, mut bridge_rx) = mpsc::channel::(32); + + let requests_2_requestors: RequestIds2SendersType = Arc::new(RwLock::new(HashMap::new())); + + loop { + tokio::select! { + // Handle incoming messages from the WebSocket stream + Some(message) = websocket_rx.next() => { + info!("gateway Received websocket message: {:?}", message); + Self::handle_message( + message, + &mut websocket_tx, + &service_id, + rule_engine.clone(), + methods_2_services.clone(), + service_2_rxes.clone(), + bridge_tx.clone(), + ).await + } + // this is a request from the main api gateway thread that needs to be sent to the service + //via the websocket + Some(request) = websocket_service_request_rx.recv() => { + let request_id = request.request_id.clone(); + let sender_entry = Arc::new(Mutex::new(Some(request.respond_to))); + requests_2_requestors.write().await.insert(request_id.clone(), sender_entry.clone()); + info!("Received request to send to service: {:?} with method: {:?} and payload: {:?}", service_id, request.method, request.payload); + + let service_call = APIClientMessages::ServiceCall( + ServiceCall { + method: request.method.clone(), + request_id, + payload: request.payload, + } + ); + debug!("Sending service call: {:?}", service_call); + match serde_json::to_string(&service_call) { + Ok(service_call) => { + let _ = websocket_tx.send(Message::Text(service_call)).await; + } + Err(e) => { + error!("Failed to serialize service call: {:?}", e); + let fail = WebsocketServiceResponse::Error(request.request_id, e.to_string()); + let _ = bridge_tx.send(fail).await; + continue; + } + } + } + Some(bridge_message) = bridge_rx.recv() => { + info!("Received message from bridge: {:?}", bridge_message); + + let id = bridge_message.get_id(); + + if let Some(requestor) = requests_2_requestors.write().await.remove(&id) { + let mut requestor_lock = requestor.lock().await; + if let Some(sender) = requestor_lock.take() { + let _ = sender.send(bridge_message.clone()); + } + } + } + else => { + info!("Service connection closed: {:?}", service_id); + // Handle service disconnection + // Clean up resources, etc. + Self::handle_unregister( + &service_id, + methods_2_services.clone(), + rule_engine.clone(), + ).await; + let _ = service_2_rxes + .write() + .await + .remove(&service_id); + break Ok(()); + } + } + } + } +} +use serde_json::Value; +use ssda_types::gateway::{ + APIGatewayServiceConnectionDisposition, APIGatewayServiceConnectionError, ApiGatewayServer, + ServiceRoutingErrorResponse, ServiceRoutingRequest, ServiceRoutingResponse, + ServiceRoutingSuccessResponse, +}; +use ssda_types::service::{ + APIClientMessages, APIGatewayServiceRegistrationRequest, APIGatewayServiceRegistrationResponse, + FireboltMethodHandlerAPIRegistration, ServiceCall, WebsocketServiceRequest, + WebsocketServiceResponse, +}; +use ssda_types::{JqRule, ServiceId, ServiceRequestId}; +use tokio::net::TcpStream; +use tokio::sync::{oneshot, Mutex}; +use tokio_tungstenite::tungstenite::{Error, Message}; +use tokio_tungstenite::WebSocketStream; +use url::form_urlencoded; + +pub struct WebsocketHandler { + pub service_id: ServiceId, +} +#[async_trait::async_trait] +impl ApiGatewayServer for ApiGateway { + async fn is_service_connect( + &self, + uri: Uri, + ) -> Result { + Self::is_apigateway_connection(&uri) + } + async fn service_connect( + &mut self, + service_id: ServiceId, + ws_stream: tokio_tungstenite::WebSocketStream, + ) -> Result { + info!("new Service connected: {:?}", service_id); + + let spawn_service_id = service_id.clone(); + let rule_engine_for_service = self.rules_engine.clone(); + /* + The broker_rx is a channel that receives service routing requests from the broker. It is the responsibility + of this (APIGateway) to handle the service routing requests and send them to the appropriate service. + Each service connection will have its own broker_rx channel, and the APIGateway will handle the routing + */ + let (websocket_handler_sender, websocket_handler_receiver) = + mpsc::channel::(32); + + /* + map sender for the websocket handler to the service id for later use + */ + { + let mut updater = self.services_2_rxs.write().await; + updater.insert(service_id.clone(), websocket_handler_sender); + } + let methods_2_services = self.methods_2_services.clone(); + let services_2_rxs = Arc::clone(&self.services_2_rxs); + tokio::spawn(async move { + let _ = Self::handle_service_connection( + spawn_service_id, + ws_stream, + rule_engine_for_service, + methods_2_services.clone(), + websocket_handler_receiver, + services_2_rxs.clone(), + ) + .await; + }); + + Ok(APIGatewayServiceConnectionDisposition::Accept(service_id)) + } + fn get_sender(&self) -> tokio::sync::mpsc::Sender { + self.broker_sender.clone() + } +} +/* +write unit tests for ApiGateway +*/ +#[cfg(test)] +mod tests { + use super::*; + + use mockall::predicate::eq; + + use std::sync::Arc; + + use ripple_sdk::api::rules_engine::MockRuleEngineProvider; + + use ssda_types::service::{ + APIClientMessages, APIGatewayServiceRegistrationRequest, + FireboltMethodHandlerAPIRegistration, + }; + use ssda_types::{JqRule, ServiceId}; + use tokio::sync::RwLock; + + fn make_registration(method: &str) -> APIGatewayServiceRegistrationRequest { + APIGatewayServiceRegistrationRequest { + firebolt_handlers: vec![FireboltMethodHandlerAPIRegistration { + firebolt_method: method.to_string(), + jq_rule: Some(JqRule { + alias: "foo".to_string(), + rule: ".foo".to_string(), + }), + }], + } + } + + #[tokio::test] + async fn test_service_map_add_and_get() { + let mut map = ServiceMap::new(); + let service_id = ServiceId::new("svc1".to_string()); + let reg = FireboltMethodHandlerAPIRegistration { + firebolt_method: "foo.bar".to_string(), + jq_rule: None, + }; + map.add_service(service_id.clone(), vec![reg.clone()]); + let regs = map.get_registrations(&service_id); + assert_eq!(regs.len(), 1); + assert_eq!(regs[0].firebolt_method, "foo.bar"); + } + + #[tokio::test] + async fn test_service_map_remove() { + let mut map = ServiceMap::new(); + let service_id = ServiceId::new("svc2".to_string()); + let reg = FireboltMethodHandlerAPIRegistration { + firebolt_method: "foo.baz".to_string(), + jq_rule: None, + }; + map.add_service(service_id.clone(), vec![reg]); + map.remove_service(&service_id); + let regs = map.get_registrations(&service_id); + assert!(regs.is_empty()); + } + + #[tokio::test] + async fn test_service_map_get_service_for_method() { + let mut map = ServiceMap::new(); + let service_id = ServiceId::new("svc3".to_string()); + let reg = FireboltMethodHandlerAPIRegistration { + firebolt_method: "foo.qux".to_string(), + jq_rule: None, + }; + map.add_service(service_id.clone(), vec![reg.clone()]); + let found = map.get_service_for_method("foo.qux"); + assert!(found.is_some()); + let (sid, r) = found.unwrap(); + assert_eq!(sid, service_id); + assert_eq!(r.firebolt_method, "foo.qux"); + } + + #[tokio::test] + async fn test_is_apigateway_connection_accept() { + let uri: http::Uri = "/apigateway?serviceId=testsvc".parse().unwrap(); + let res = ApiGateway::is_apigateway_connection(&uri); + assert!(matches!( + res, + Ok(APIGatewayServiceConnectionDisposition::Accept(_)) + )); + } + + #[tokio::test] + async fn test_is_apigateway_connection_reject() { + let uri: http::Uri = "/notgateway".parse().unwrap(); + let res = ApiGateway::is_apigateway_connection(&uri); + assert!(matches!( + res, + Err(APIGatewayServiceConnectionError::NotAService) + )); + } + + #[tokio::test] + async fn test_classify_message_register() { + let reg = APIClientMessages::Register(make_registration("foo.bar")); + let msg = Message::Text(serde_json::to_string(®).unwrap()); + let state = ApiGateway::classify_message(&msg); + match state { + APIGatewayClientState::Registering(_) => {} + _ => panic!("Expected Registering"), + } + } + + #[tokio::test] + async fn test_classify_message_close() { + let msg = Message::Close(None); + let state = ApiGateway::classify_message(&msg); + assert!(matches!(state, APIGatewayClientState::Closed)); + } + + #[tokio::test] + async fn test_handle_registration_and_unregister() { + let service_id = ServiceId::new("svc4".to_string()); + let mut mock = MockRuleEngineProvider::new(); + let r = Rule { + alias: "foo.bar".to_string(), + filter: Some(".foo".to_string()), + endpoint: Some("service".to_string()), + ..Default::default() + }; + mock.expect_add_rule() + .with(eq(r.clone())) + .times(1) + .return_const(()); + mock.expect_remove_rule().times(1).return_const(()); + + let rule_engine: Arc< + RwLock>, + > = Arc::new(RwLock::new(Box::new(mock))); + + let methods_2_services = Arc::new(RwLock::new(ServiceMap::new())); + let registration = make_registration("foo.bar"); + ApiGateway::handle_registration( + service_id.clone(), + rule_engine.clone(), + ®istration, + methods_2_services.clone(), + ) + .await; + let regs = methods_2_services + .read() + .await + .get_registrations(&service_id); + assert_eq!(regs.len(), 1); + + ApiGateway::handle_unregister(&service_id, methods_2_services.clone(), rule_engine.clone()) + .await; + let regs = methods_2_services + .read() + .await + .get_registrations(&service_id); + assert!(regs.is_empty()); + } +} diff --git a/ssda/ssda_types/Cargo.toml b/ssda/ssda_types/Cargo.toml new file mode 100644 index 000000000..b66344b63 --- /dev/null +++ b/ssda/ssda_types/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "ssda_types" +version = "0.1.0" +edition = "2021" + +[dependencies] + +ripple_sdk = { path="../../core/sdk" } + +async-trait = "0.1.88" +mockall = "0.13.1" +serde = "1.0.219" +serde_json = "1.0.140" +tokio = { version = "1", features = ["full"] } +tokio-tungstenite = "0.20.1" +futures-util = "0.3" +url = "2.5" +http = "0.2.12" +jsonrpsee = {version= "0.24.9", features= ["server", "macros"] } +pprof = { version = "0.15", features = ["flamegraph"] } +env_logger = "0.11.8" diff --git a/ssda/ssda_types/src/api_gateway_client.rs b/ssda/ssda_types/src/api_gateway_client.rs new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/ssda/ssda_types/src/api_gateway_client.rs @@ -0,0 +1 @@ + diff --git a/ssda/ssda_types/src/api_gateway_server.rs b/ssda/ssda_types/src/api_gateway_server.rs new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/ssda/ssda_types/src/api_gateway_server.rs @@ -0,0 +1 @@ + diff --git a/ssda/ssda_types/src/lib.rs b/ssda/ssda_types/src/lib.rs new file mode 100644 index 000000000..028b25db2 --- /dev/null +++ b/ssda/ssda_types/src/lib.rs @@ -0,0 +1,1074 @@ +use futures_util::{SinkExt, StreamExt}; +use gateway::ServiceRoutingRequest; +use jsonrpsee::Methods; +use ripple_sdk::api::rules_engine::{Rule, RuleTransform}; +use ripple_sdk::log::{debug, error, info}; +use serde::{Deserialize, Serialize}; +use std::time::Duration; + +use service::{APIClientMessages, APIGatewayServiceRegistrationRequest}; + +/* +Who what why +# API Gateway +The API gateway is a standalone binary component +that listens for Firebolt requests on a websocket. Upon receiving a firebolt +request, the gateway will look up a handler service (based on the method name) in it's +runtime (dynamically created) registry, and (assumign a service is registered to for the method), +will wrap the request with metadata, and dispatch the request to the handler service. Upon receiving +a response from the service, the API Gateway will translate the service response (using the rules engine) +into a Firebolt compatible (success or failure) result. + +# Servicesprintln +ServiceRequestHandler. This design is motivated by the need to free the Service from as much connection oriented +detail and let the developer focus on business logic. The API Gateway client is instanced as a crate that can be +consumed by a service at the highest possible level of abstraction (and ease of use) - it should only requuire a bit of +bootstrapping, and a ServiceRequestHandler instance/implementation, and then it should manage all the details of the +interactions between the Service and the API gateway with the Service being as blissfully ignorant of the details +as possible. +*/ +/* + +register: Service -> API Gateway Client -> API Gateway + +*/ + +pub mod api_gateway_client; +pub mod api_gateway_server; +pub mod service_api; + +#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)] + +pub struct JqRule { + pub alias: String, + pub rule: String, +} + +#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)] +pub struct StaticRule { + pub alias: String, + pub rule: String, +} +#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)] + +pub struct HandlerId { + pub handler_id: String, +} +#[derive(Debug, Clone, Default, Serialize, Deserialize)] + +pub struct ServiceHandler { + pub handler_id: HandlerId, + pub handler_type: Handler, +} + +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub enum Handler { + #[default] + None, + JqRule(JqRule), + StaticRule(StaticRule), +} +impl std::fmt::Display for Handler { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Handler::None => write!(f, "none"), + Handler::JqRule(jq_rule) => write!(f, "{}", jq_rule.alias), + Handler::StaticRule(static_rule) => write!(f, "{}", static_rule.alias), + } + } +} + +impl From for ripple_sdk::api::rules_engine::Rule { + fn from(handler: Handler) -> Self { + match handler { + Handler::None => Rule { + alias: "none".to_string(), + filter: None, + transform: RuleTransform::default(), + event_handler: None, + endpoint: None, + sources: None, + }, + Handler::JqRule(jq_rule) => Rule { + alias: jq_rule.alias, + filter: Some(jq_rule.rule), + ..Default::default() + }, + Handler::StaticRule(static_rule) => Rule { + alias: static_rule.alias, + filter: Some(static_rule.rule), + ..Default::default() + }, + } + } +} + +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct ServiceId { + pub service_id: String, +} +impl ServiceId { + pub fn new(service_id: String) -> Self { + ServiceId { service_id } + } +} + +impl PartialEq for ServiceId { + fn eq(&self, other: &Self) -> bool { + self.service_id == other.service_id + } +} + +impl Eq for ServiceId {} + +impl std::hash::Hash for ServiceId { + fn hash(&self, state: &mut H) { + self.service_id.hash(state); + } +} +impl std::fmt::Display for ServiceId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.service_id) + } +} + +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct ServiceRequestId { + pub request_id: u64, +} +impl PartialEq for ServiceRequestId { + fn eq(&self, other: &Self) -> bool { + self.request_id == other.request_id + } +} + +impl Eq for ServiceRequestId {} + +impl std::hash::Hash for ServiceRequestId { + fn hash(&self, state: &mut H) { + self.request_id.hash(state); + } +} +impl ServiceRequestId { + pub fn new(request_id: u64) -> Self { + ServiceRequestId { request_id } + } +} + +/* +gateway messages: from endpoint broker to api gateway and back. +*/ +pub mod gateway { + use crate::ServiceId; + use http::Uri; + use ripple_sdk::api::gateway::rpc_gateway_api::{ + JsonRpcApiError, JsonRpcApiResponse, RpcRequest, + }; + use serde::{Deserialize, Serialize}; + use serde_json::Value; + use tokio::sync::oneshot::Sender; + + use crate::ServiceRequestId; + + /* + ServiceRequest is the request that is sent from the API Gateway to the service + */ + #[derive(Debug)] + pub struct ServiceRoutingRequest { + pub request_id: ServiceRequestId, + pub payload: RpcRequest, + pub respond_to: Sender, + } + #[derive(Debug, Default, Clone, Serialize, Deserialize)] + pub struct ServiceRoutingSuccessResponse { + pub request_id: ServiceRequestId, + pub response: Value, + } + impl From for JsonRpcApiResponse { + fn from(response: ServiceRoutingResponse) -> Self { + match response { + ServiceRoutingResponse::Error(_error) => JsonRpcApiError::default().into(), + ServiceRoutingResponse::Success(success) => JsonRpcApiResponse { + id: Some(success.request_id.request_id), + jsonrpc: "2.0".to_string(), + result: Some(success.response), + error: None, + method: None, + params: None, + }, + } + } + } + #[derive(Debug, Clone, Default, Serialize, Deserialize)] + pub struct ServiceRoutingErrorResponse { + pub request_id: ServiceRequestId, + pub error: String, + } + #[derive(Debug, Clone, Serialize, Deserialize)] + pub enum ServiceRoutingResponse { + Success(ServiceRoutingSuccessResponse), + Error(ServiceRoutingErrorResponse), + } + #[derive(Debug)] + pub enum APIGatewayServiceConnectionDisposition { + Accept(ServiceId), + Connected(ServiceId), + } + #[derive(Debug)] + pub enum APIGatewayServiceConnectionError { + ConnectionError, + NotAService, + } + + /* + This is the API gateway, and it meant to be hosted in the main ripple process + */ + + #[async_trait::async_trait] + pub trait ApiGatewayServer: Send + Sync { + async fn is_service_connect( + &self, + uri: Uri, + ) -> Result; + async fn service_connect( + &mut self, + service_id: ServiceId, + ws_stream: tokio_tungstenite::WebSocketStream, + ) -> Result; + fn get_sender(&self) -> tokio::sync::mpsc::Sender; + } +} +/* +service message: from api gateawy to services and back (over websockets) +*/ +pub mod service { + + use ripple_sdk::api::gateway::rpc_gateway_api::JsonRpcApiRequest; + use serde::{Deserialize, Serialize}; + use serde_json::Value; + + use crate::{Handler, JqRule, ServiceId, ServiceRequestId}; + + #[derive(Debug, Clone, Default, Serialize, Deserialize)] + pub struct FireboltMethodHandlerRegistration { + pub firebolt_method: Handler, + } + #[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)] + pub struct FireboltMethodHandlerAPIRegistration { + pub firebolt_method: String, + pub jq_rule: Option, + } + #[derive(Debug, Clone, Serialize, Deserialize, Default)] + pub struct APIGatewayServiceRegistrationRequest { + pub firebolt_handlers: Vec, + } + #[derive(Debug, Clone, Serialize, Deserialize, Default)] + pub struct APIGatewayServiceRegistrationResponse { + pub firebolt_handlers: Vec, + } + impl From for APIGatewayServiceRegistrationResponse { + fn from( + request: APIGatewayServiceRegistrationRequest, + ) -> APIGatewayServiceRegistrationResponse { + APIGatewayServiceRegistrationResponse { + firebolt_handlers: request.firebolt_handlers, + } + } + } + + #[derive(Debug, Clone, Default, Serialize, Deserialize)] + pub struct ServiceRequest { + pub service_id: ServiceId, + pub firebolt_method: Handler, + pub payload: serde_json::Value, + } + #[derive(Debug, Clone, Default, Serialize, Deserialize)] + pub struct ServiceErrorResponse { + pub service_id: ServiceId, + pub firebolt_method: Handler, + pub error: String, + } + #[derive(Debug, Clone, Default, Serialize, Deserialize)] + pub struct ServiceSuccessResponse { + pub service_id: ServiceId, + pub firebolt_method: Handler, + pub payload: serde_json::Value, + } + + /* + send by api client to the api gateway over websocket + + */ + #[derive(Debug, Clone, Default, Serialize, Deserialize)] + pub struct APIClientRegistration { + pub firebolt_handlers: Vec, + } + /* + Sent during callback registration in service + */ + #[derive(Debug, Clone, Default, Serialize, Deserialize)] + pub struct ServiceRegistration { + pub service_id: ServiceId, + pub firebolt_handlers: Vec, + } + + impl ServiceRegistration { + pub fn new( + service_id: ServiceId, + firebolt_handlers: Vec, + ) -> Self { + ServiceRegistration { + service_id, + firebolt_handlers, + } + } + pub fn get_rule_registrations(&self) -> Vec { + self.firebolt_handlers.clone() + } + } + + pub struct ServiceRegistrationBuilder { + service_id: ServiceId, + firebolt_handlers: Vec, + } + impl ServiceRegistrationBuilder { + pub fn new(service_id: ServiceId) -> Self { + ServiceRegistrationBuilder { + service_id, + firebolt_handlers: Vec::new(), + } + } + pub fn add_handler(&mut self, firebolt_method: Handler) -> &mut Self { + self.firebolt_handlers.push(match firebolt_method { + Handler::None => todo!(), + Handler::JqRule(jq_rule) => FireboltMethodHandlerAPIRegistration { + firebolt_method: jq_rule.alias.clone(), + jq_rule: Some(jq_rule), + }, + Handler::StaticRule(static_rule) => FireboltMethodHandlerAPIRegistration { + firebolt_method: static_rule.alias.clone(), + jq_rule: Some(JqRule { + alias: static_rule.alias, + rule: static_rule.rule, + }), + }, + }); + self + } + pub fn build(&self) -> ServiceRegistration { + ServiceRegistration { + service_id: self.service_id.clone(), + firebolt_handlers: self.firebolt_handlers.clone(), + } + } + } + /* + ServiceCall is the request that is presented to a callback handler. + */ + #[derive(Debug, Clone, Serialize, Deserialize)] + pub struct ServiceCall { + pub request_id: ServiceRequestId, + pub method: String, + pub payload: serde_json::Value, + } + impl From for JsonRpcApiRequest { + fn from(call: ServiceCall) -> Self { + JsonRpcApiRequest { + id: Some(call.request_id.request_id), + jsonrpc: "2.0".to_string(), + method: call.method, + params: Some(call.payload), + } + } + } + #[derive(Debug, Clone, Serialize, Deserialize)] + pub struct ServiceCallSuccessResponse { + pub request_id: ServiceRequestId, + pub response: serde_json::Value, + } + #[derive(Debug, Clone, Serialize, Deserialize)] + pub struct ServiceCallErrorResponse { + pub request_id: ServiceRequestId, + pub error: String, + } + #[derive(Debug, Clone, Serialize, Deserialize)] + pub enum ServiceCallResponse { + Success(ServiceCallSuccessResponse), + Error(ServiceCallErrorResponse), + } + #[derive(Debug, Clone, Serialize, Deserialize)] + pub enum APIClientMessages { + Register(APIGatewayServiceRegistrationRequest), + Registered(APIGatewayServiceRegistrationResponse), + Error(String), + Unregister(ServiceId), + ServiceCall(ServiceCall), + ServiceCallSuccessResponse(ServiceCallSuccessResponse), + ServiceCallErrorResponse(ServiceCallErrorResponse), + } + impl Default for APIClientMessages { + fn default() -> Self { + APIClientMessages::Register(APIGatewayServiceRegistrationRequest::default()) + } + } + + pub struct ServiceRegistrationResponse { + pub service_id: ServiceId, + } + pub struct ServiceRegistrationFailure { + pub service_id: ServiceId, + pub error: String, + } + #[derive(Debug, Clone, Serialize, Deserialize)] + pub enum WebsocketServiceResponse { + Success(ServiceRequestId, serde_json::Value), + Error(ServiceRequestId, String), + } + impl WebsocketServiceResponse { + pub fn get_id(&self) -> ServiceRequestId { + match self { + WebsocketServiceResponse::Success(id, _) => id.clone(), + WebsocketServiceResponse::Error(id, _) => id.clone(), + } + } + } + #[derive(Debug)] + pub struct WebsocketServiceRequest { + pub request_id: ServiceRequestId, + pub method: String, + pub payload: Value, + pub respond_to: tokio::sync::oneshot::Sender, + } + + /* + individiual services implement this trait to handle requests from the API gateway + This trait should faciliate fun testing of actual implementations + */ + + pub trait ServiceRequestHandler: Send + Sync { + /* + called by the client to allow the ServiceRequestHander return a Vec + to be used by the client to route requests. This is a blocking call, and will be called + after the client `on_connect`s + **/ + fn register(&self) -> Vec; + fn handle_request( + &self, + request: ServiceCall, + ) -> Result; + fn on_connected(&self); + fn on_disconnected(&self); + fn healthy(&self) -> bool; + } +} +pub mod client { + use mockall::automock; + + use crate::{HandlerId, ServiceId}; + + use crate::service::{ + FireboltMethodHandlerRegistration, ServiceCall, ServiceCallErrorResponse, + ServiceCallSuccessResponse, ServiceErrorResponse, ServiceRegistration, + ServiceRegistrationFailure, ServiceRegistrationResponse, ServiceRequest, + ServiceRequestHandler, ServiceSuccessResponse, + }; + + #[async_trait::async_trait] + /* + this is a trait that is implemented by a service client. + There will probably only be 2 of these for the foreseeable future: + 1) a real, websocket client + 2) a mock, for unit/integration testing + the point of this trait , and it's implementers, is to abstract the details related to auth, connection, etc. from the service (business logic) + that needs it. + + the methods in the actual interface are concerned with what to do during lifecycle transition events. the graph/steps/etc. of the lifecyle and + when to call these methods (and any state needed to call them) is the responsiblity of the concrete implementation. + */ + #[automock] + pub trait ServiceClientTrait: Send + Sync { + // this is a request to the service to register itself with the API gateway + fn register( + &self, + registraton: Box, + ) -> Result; + fn set_handler(&mut self, handler: Box); + fn unregister_service(&mut self, service_id: ServiceId) -> Result<(), String>; + fn register_handler( + &mut self, + handler: FireboltMethodHandlerRegistration, + ) -> Result<(), String>; + fn unregister_handler(&mut self, handler_id: HandlerId) -> Result<(), String>; + async fn invoke_handler( + &mut self, + request: ServiceRequest, + ) -> Result; + } + impl ServiceRequestHandler for T + where + T: Send + Sync + Clone + 'static + ServiceRequestHandlerImpl, + { + fn register(&self) -> Vec { + self.register() + } + + fn handle_request( + &self, + request: ServiceCall, + ) -> Result { + self.handle_request(request) + } + + fn on_connected(&self) { + self.on_connected() + } + + fn on_disconnected(&self) { + self.on_disconnected() + } + + fn healthy(&self) -> bool { + self.healthy() + } + } + + pub trait ServiceRequestHandlerImpl: Send + Sync + Clone { + fn register(&self) -> Vec; + fn handle_request( + &self, + request: ServiceCall, + ) -> Result; + fn on_connected(&self); + fn on_disconnected(&self); + fn healthy(&self) -> bool; + } +} +/* +This is the alternative API surface that services can use to communicate with the API gateway. +Firebolt calls are bidirectional, so the API gateway can send messages to the service, and the service can send messages to the API gateway. +*/ +mod api_surface { + use serde::{Deserialize, Serialize}; + #[derive(Debug, Clone, Serialize, Deserialize)] + pub struct FireboltRequest { + pub request_id: String, + pub payload: String, + } + #[derive(Debug, Clone, Serialize, Deserialize)] + pub struct FireboltResponse { + pub request_id: String, + pub payload: String, + } +} + +/* +This trait represents the layer that will actually "talk" to the API gateway. it handles transports, etc. +This is a trait to : +1) Enable testing +2) Abstract the details of the transport from the service +3) Allow for different transports (websocket, http, etc.)z +*/ +#[async_trait::async_trait] +pub trait APIGatewayClient { + //fn connect(&self); + //fn disconnect(&self); + fn dispatch(&self, request: gateway::ServiceRoutingRequest); + async fn start(&self); + // async fn stop(&self); +} +pub struct WebsocketAPIGatewayClient { + rpc_server: Methods, + service_id: ServiceId, +} + +use tokio_tungstenite::{connect_async, tungstenite::Message}; +use url::Url; + +use crate::service::{ + FireboltMethodHandlerAPIRegistration, ServiceCallErrorResponse, ServiceCallSuccessResponse, +}; + +impl WebsocketAPIGatewayClient { + pub fn new(rpc_server: Methods, service_id: ServiceId) -> WebsocketAPIGatewayClient { + WebsocketAPIGatewayClient { + rpc_server, + service_id, + } + } + pub async fn handle_messages( + stream: tokio_tungstenite::WebSocketStream< + tokio_tungstenite::MaybeTlsStream, + >, + rpc_server: Methods, + _api_gateway_clientfirebolt_handlers: Vec, + // handler: Arc, + //registration: ServiceRegistration, + ) { + let (mut tx, mut rx) = stream.split(); + let firebolt_handlers: Vec = rpc_server + .method_names() + .map(|method| { + info!("Registered method: {}", method); + FireboltMethodHandlerAPIRegistration { + firebolt_method: method.to_string(), + jq_rule: None, // Assuming no jq rule for now, can be modified as needed + } + }) + .collect(); + + let registration_request = APIGatewayServiceRegistrationRequest { + firebolt_handlers: firebolt_handlers.clone(), + }; + let msg = serde_json::to_string(&APIClientMessages::Register(registration_request.clone())); + if let Ok(msg) = msg { + let _ = tx.send(Message::Text(msg)).await; + } else { + error!("websocket: Failed to serialize registration request"); + } + + while let Some(message) = rx.next().await { + match message { + Ok(msg) => { + /* + attempt to marshal the message into a ServiceRequest + if it fails, log the error and continue + */ + info!("websocket: Received message via websocket: {:?}", msg); + let msg = msg.into_text().unwrap_or_default(); + if let Ok(request) = serde_json::from_str::(&msg) { + match request { + APIClientMessages::Register(registration_request) => { + info!("Received registration request: {:?}", registration_request); + } + APIClientMessages::Registered(registration_request) => { + info!("Received registered response: {:?}", registration_request); + //handler.on_connected(); + } + APIClientMessages::Error(error) => { + info!("Received error: {:?}", error); + } + APIClientMessages::Unregister(service_id) => { + info!("Received unregister request: {:?}", service_id); + } + APIClientMessages::ServiceCall(service_call) => { + debug!( + "Received service call: {:?}", + service_call.payload.to_string() + ); + let request_id = service_call.request_id.clone(); + let json_rpc_request = service_call.payload.clone(); + debug!("Raw JSON-RPC request: {:?}", json_rpc_request); + let json_rpc_request = + serde_json::to_value(json_rpc_request).unwrap().to_string(); + debug!("sending request to RPC server: {:?}", json_rpc_request); + + match rpc_server.raw_json_request(&json_rpc_request, 1).await { + Ok(okie) => { + debug!("Received response from RPC server: {:?}", okie); + let raw_response = okie.0.clone(); + debug!("Raw response: {:?}", raw_response); + let response = serde_json::from_str::( + &raw_response, + ); + if let Err(e) = response { + debug!("Failed to parse response: {:?}", e); + let error_response = + APIClientMessages::ServiceCallErrorResponse( + ServiceCallErrorResponse { + request_id, + error: e.to_string(), + }, + ); + let error_response = + serde_json::to_string(&error_response); + if let Ok(error_response) = error_response { + let _ = tx + .send(Message::Text(error_response.clone())) + .await; + debug!( + "Sending error response: {:?}", + error_response + ); + } else { + debug!("Failed to serialize error response"); + } + return; + } else { + let response = response.unwrap(); + info!( + "Parsed response from called service: {:?}", + response + ); + let success_response = ServiceCallSuccessResponse { + request_id, + response, + }; + let success_response = + APIClientMessages::ServiceCallSuccessResponse( + success_response, + ); + let success_response = + serde_json::to_string(&success_response); + if let Ok(success_response) = success_response { + let _ = tx + .send(Message::Text(success_response.clone())) + .await; + debug!( + "Sending success response: {:?}", + success_response + ); + } else { + error!("Failed to serialize success response"); + } + } + } + Err(nope) => { + error!("nope! {:?}", nope); + let error_response = + APIClientMessages::ServiceCallErrorResponse( + ServiceCallErrorResponse { + request_id, + error: nope.to_string(), + }, + ); + let error_response = serde_json::to_string(&error_response); + if let Ok(error_response) = error_response { + let _ = tx + .send(Message::Text(error_response.clone())) + .await; + debug!("Sending error response: {:?}", error_response); + } else { + error!("Failed to serialize error response"); + } + } + } + } + _ => {} + } + } else { + error!(" I don't understand this message: {:?}", msg); + } + } + Err(err) => { + error!("Error receiving message: {:?}", err); + // Handle the error (e.g., log it, retry, etc.) + // You might want to break the loop or handle reconnection logic here + // break; + } + } + } + } + pub async fn connect( + &self, + endpoint_url: Option, + service_id: ServiceId, + ) -> Result<(), Box> { + let url = Url::parse(&endpoint_url.unwrap_or_else(|| { + format!( + "ws://localhost:3474/apigateway?serviceId={}", + &service_id.service_id + ) + }))?; + let mut backoff = Duration::from_secs(1); + println!("Connecting to WebSocket at {}", url); + + loop { + match connect_async(url.clone()).await { + Ok((ws_stream, _)) => { + let rpc_server = self.rpc_server.clone(); + println!("✅ Connected to WebSocket"); + + // Reset backoff after successful connection + backoff = Duration::from_secs(1); + + // This handles the message loop and returns on disconnect + Self::handle_messages(ws_stream, rpc_server, vec![]).await; + info!("🔌 Disconnected, retrying..."); + } + Err(err) => { + error!("Error connecting to WebSocket: {:?}", err); + + // Exponential backoff (up to 1 minute) + tokio::time::sleep(backoff).await; + if backoff >= Duration::from_secs(60) { + backoff = Duration::from_secs(1); + } + + backoff = std::cmp::min(backoff * 2, Duration::from_secs(60)); + } + } + } + } +} + +#[async_trait::async_trait] +impl APIGatewayClient for WebsocketAPIGatewayClient { + // fn connect(&self) { + // // connect to the API gateway + // } + // fn disconnect(&self) { + // // disconnect from the API gateway + // } + fn dispatch(&self, _request: ServiceRoutingRequest) { + // send a request to the API gateway + } + + async fn start(&self) { + env_logger::init(); + info!("starting websocket client"); + self.connect(None, self.service_id.clone()).await.unwrap(); + // self.handler.on_connected(); + } + + // async fn stop(&self) { + // todo!() + // } +} +#[cfg(test)] +pub mod tests { + use std::collections::HashSet; + + use ripple_sdk::api::rules_engine::Rule; + use serde_json::json; + + use crate::{ + service, Handler, HandlerId, JqRule, ServiceHandler, ServiceId, ServiceRequestId, + StaticRule, + }; + + #[test] + fn test_service_id_equality() { + let id1 = ServiceId::new("service1".to_string()); + let id2 = ServiceId::new("service1".to_string()); + let id3 = ServiceId::new("service2".to_string()); + assert_eq!(id1, id2); + assert_ne!(id1, id3); + } + + #[test] + fn test_service_id_hash() { + let id1 = ServiceId::new("service1".to_string()); + let id2 = ServiceId::new("service1".to_string()); + let mut set = HashSet::new(); + set.insert(id1); + assert!(set.contains(&id2)); + } + + #[test] + fn test_service_id_display() { + let id = ServiceId::new("abc".to_string()); + assert_eq!(format!("{}", id), "abc"); + } + + #[test] + fn test_service_request_id_equality() { + let id1 = ServiceRequestId::new(1); + let id2 = ServiceRequestId::new(1); + let id3 = ServiceRequestId::new(2); + assert_eq!(id1, id2); + assert_ne!(id1, id3); + } + + #[test] + fn test_service_request_id_hash() { + let id1 = ServiceRequestId::new(1); + let id2 = ServiceRequestId::new(1); + let mut set = HashSet::new(); + set.insert(id1); + assert!(set.contains(&id2)); + } + + #[test] + fn test_handler_display() { + let jq = Handler::JqRule(JqRule { + alias: "jq".to_string(), + rule: ".foo".to_string(), + }); + let static_rule = Handler::StaticRule(StaticRule { + alias: "static".to_string(), + rule: "bar".to_string(), + }); + let none = Handler::None; + assert_eq!(format!("{}", jq), "jq"); + assert_eq!(format!("{}", static_rule), "static"); + assert_eq!(format!("{}", none), "none"); + } + + #[test] + fn test_handler_into_rule() { + let jq = Handler::JqRule(JqRule { + alias: "jq".to_string(), + rule: ".foo".to_string(), + }); + let rule: Rule = jq.clone().into(); + assert_eq!(rule.alias, "jq"); + assert_eq!(rule.filter, Some(".foo".to_string())); + + let static_rule = Handler::StaticRule(StaticRule { + alias: "static".to_string(), + rule: "bar".to_string(), + }); + let rule: Rule = static_rule.clone().into(); + assert_eq!(rule.alias, "static"); + assert_eq!(rule.filter, Some("bar".to_string())); + + let none = Handler::None; + let rule: Rule = none.into(); + assert_eq!(rule.alias, "none"); + assert!(rule.filter.is_none()); + } + + #[test] + fn test_service_registration_builder() { + let service_id = ServiceId::new("svc".to_string()); + let mut builder = service::ServiceRegistrationBuilder::new(service_id.clone()); + builder.add_handler(Handler::JqRule(JqRule { + alias: "jq".to_string(), + rule: ".foo".to_string(), + })); + let reg = builder.build(); + assert_eq!(reg.service_id, service_id); + assert_eq!(reg.firebolt_handlers.len(), 1); + assert_eq!(reg.firebolt_handlers[0].firebolt_method, "jq"); + } + + #[test] + fn test_service_registration_get_rule_registrations() { + let reg = service::ServiceRegistration { + service_id: ServiceId::new("svc".to_string()), + firebolt_handlers: vec![service::FireboltMethodHandlerAPIRegistration { + firebolt_method: "foo".to_string(), + jq_rule: None, + }], + }; + let rules = reg.get_rule_registrations(); + assert_eq!(rules.len(), 1); + assert_eq!(rules[0].firebolt_method, "foo"); + } + + #[test] + fn test_service_call_into_jsonrpc_api_request() { + let call = service::ServiceCall { + request_id: ServiceRequestId::new(42), + method: "test".to_string(), + payload: json!({"foo": "bar"}), + }; + let req: ripple_sdk::api::gateway::rpc_gateway_api::JsonRpcApiRequest = call.into(); + assert_eq!(req.id, Some(42)); + assert_eq!(req.method, "test"); + assert_eq!(req.params, Some(json!({"foo": "bar"}))); + } + + #[test] + fn test_apigatewayserviceregistrationrequest_into_response() { + let req = service::APIGatewayServiceRegistrationRequest { + firebolt_handlers: vec![service::FireboltMethodHandlerAPIRegistration { + firebolt_method: "foo".to_string(), + jq_rule: None, + }], + }; + let resp: service::APIGatewayServiceRegistrationResponse = req.clone().into(); + assert_eq!(resp.firebolt_handlers, req.firebolt_handlers); + } + + #[test] + fn test_websocketserviceresponse_get_id() { + let id = ServiceRequestId::new(7); + let resp = service::WebsocketServiceResponse::Success(id.clone(), json!({})); + assert_eq!(resp.get_id(), id); + let resp = service::WebsocketServiceResponse::Error(id.clone(), "err".to_string()); + assert_eq!(resp.get_id(), id); + } + + #[test] + fn test_apiclientmessages_default() { + let msg = service::APIClientMessages::default(); + match msg { + service::APIClientMessages::Register(_) => {} + _ => panic!("Default should be Register"), + } + } + + #[test] + fn test_servicecallresponse_serialization() { + let resp = service::ServiceCallResponse::Success(service::ServiceCallSuccessResponse { + request_id: ServiceRequestId::new(1), + response: json!({"foo": "bar"}), + }); + let s = serde_json::to_string(&resp).unwrap(); + let de: service::ServiceCallResponse = serde_json::from_str(&s).unwrap(); + match de { + service::ServiceCallResponse::Success(success) => { + assert_eq!(success.request_id, ServiceRequestId::new(1)); + assert_eq!(success.response, json!({"foo": "bar"})); + } + _ => panic!("Expected Success"), + } + } + + #[test] + fn test_servicecallerrorresponse_serialization() { + let resp = service::ServiceCallResponse::Error(service::ServiceCallErrorResponse { + request_id: ServiceRequestId::new(2), + error: "fail".to_string(), + }); + let s = serde_json::to_string(&resp).unwrap(); + let de: service::ServiceCallResponse = serde_json::from_str(&s).unwrap(); + match de { + service::ServiceCallResponse::Error(err) => { + assert_eq!(err.request_id, ServiceRequestId::new(2)); + assert_eq!(err.error, "fail"); + } + _ => panic!("Expected Error"), + } + } + + #[test] + fn test_handlerid_serialize_deserialize() { + let handler_id = HandlerId { + handler_id: "abc".to_string(), + }; + let s = serde_json::to_string(&handler_id).unwrap(); + let de: HandlerId = serde_json::from_str(&s).unwrap(); + assert_eq!(handler_id, de); + } + + #[test] + fn test_jqrule_serialize_deserialize() { + let jq = JqRule { + alias: "a".to_string(), + rule: "b".to_string(), + }; + let s = serde_json::to_string(&jq).unwrap(); + let de: JqRule = serde_json::from_str(&s).unwrap(); + assert_eq!(jq, de); + } + + #[test] + fn test_statirule_serialize_deserialize() { + let sr = StaticRule { + alias: "a".to_string(), + rule: "b".to_string(), + }; + let s = serde_json::to_string(&sr).unwrap(); + let de: StaticRule = serde_json::from_str(&s).unwrap(); + assert_eq!(sr, de); + } + + #[test] + fn test_servicehandler_serialize_deserialize() { + let handler = ServiceHandler { + handler_id: HandlerId { + handler_id: "id".to_string(), + }, + handler_type: Handler::None, + }; + let s = serde_json::to_string(&handler).unwrap(); + let de: ServiceHandler = serde_json::from_str(&s).unwrap(); + assert_eq!(handler.handler_id, de.handler_id); + match de.handler_type { + Handler::None => {} + _ => panic!("Expected None"), + } + } +} diff --git a/ssda/ssda_types/src/service_api.rs b/ssda/ssda_types/src/service_api.rs new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/ssda/ssda_types/src/service_api.rs @@ -0,0 +1 @@ +