diff --git a/Cargo.lock b/Cargo.lock index b54f74c..a8935c6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,9 +19,9 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aho-corasick" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea5d730647d4fadd988536d06fecce94b7b4f2a7efdae548f1cf4b63205518ab" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" dependencies = [ "memchr", ] @@ -112,7 +112,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" dependencies = [ "concurrent-queue", - "event-listener", + "event-listener 2.5.3", "futures-core", ] @@ -159,7 +159,7 @@ dependencies = [ "log", "parking", "polling", - "rustix 0.37.24", + "rustix 0.37.25", "slab", "socket2 0.4.9", "waker-fn", @@ -171,7 +171,7 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" dependencies = [ - "event-listener", + "event-listener 2.5.3", ] [[package]] @@ -188,19 +188,36 @@ dependencies = [ [[package]] name = "async-process" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a9d28b1d97e08915212e2e45310d47854eafa69600756fc735fb788f75199c9" +checksum = "ea6438ba0a08d81529c69b36700fa2f95837bfe3e776ab39cde9c14d9149da88" dependencies = [ "async-io", "async-lock", - "autocfg", + "async-signal", "blocking", "cfg-if", - "event-listener", + "event-listener 3.0.0", "futures-lite", - "rustix 0.37.24", - "signal-hook", + "rustix 0.38.19", + "windows-sys", +] + +[[package]] +name = "async-signal" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2a5415b7abcdc9cd7d63d6badba5288b2ca017e3fbd4173b8f405449f1a2399" +dependencies = [ + "async-io", + "async-lock", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix 0.38.19", + "signal-hook-registry", + "slab", "windows-sys", ] @@ -232,6 +249,28 @@ dependencies = [ "wasm-bindgen-futures", ] +[[package]] +name = "async-stream" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] + [[package]] name = "async-task" version = "4.4.1" @@ -246,7 +285,7 @@ checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -375,9 +414,9 @@ dependencies = [ [[package]] name = "blocking" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94c4ef1f913d78636d78d538eec1f18de81e481f44b1be0a81060090530846e1" +checksum = "8c36a4d0d48574b3dd360b4b7d95cc651d2b6557b6402848a27d4b228a473e2a" dependencies = [ "async-channel", "async-lock", @@ -424,34 +463,31 @@ dependencies = [ "anyhow", "async-trait", "chirpstack_api", + "chirpstack_integration", "clap", "futures", "handlebars", - "lazy_static", - "prost", "pulsar", - "redis", "regex", "serde", "serde_json", "tokio", "toml", "tracing", - "tracing-subscriber", ] [[package]] name = "chirpstack_api" -version = "4.5.0" +version = "4.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a69954c5d87011bf448f76a9ab0c1f822b5374b31642791bd92126c95356fee" +checksum = "7894af85c12ffb05d1f15a0b3ecabd4b9a365ce8d727e51379d13ea6ac13d929" dependencies = [ "hex", "pbjson", "pbjson-build", "pbjson-types", - "prost", - "prost-types", + "prost 0.12.1", + "prost-types 0.12.1", "rand", "serde", "tokio", @@ -459,6 +495,25 @@ dependencies = [ "tonic-build", ] +[[package]] +name = "chirpstack_integration" +version = "4.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc6025caf2698da9698296b7667636f5ba1d858eca4e95092a03751ac425d312" +dependencies = [ + "anyhow", + "async-trait", + "chirpstack_api", + "lazy_static", + "redis", + "serde", + "serde_json", + "tokio", + "toml", + "tracing", + "tracing-subscriber", +] + [[package]] name = "chrono" version = "0.4.31" @@ -505,7 +560,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -691,30 +746,30 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "add4f07d43996f76ef320709726a556a9d4f965d9410d8d0271132d2f8293480" +checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860" dependencies = [ - "errno-dragonfly", "libc", "windows-sys", ] [[package]] -name = "errno-dragonfly" -version = "0.1.2" +name = "event-listener" +version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] name = "event-listener" -version = "2.5.3" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" +checksum = "29e56284f00d94c1bc7fd3c77027b4623c88c1f53d8d2394c6199f2921dea325" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] [[package]] name = "fastrand" @@ -739,9 +794,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.27" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6c98ee8095e9d1dcbf2fcc6d95acccb90d1c81db1e44725c6a984b1dbdfb010" +checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" dependencies = [ "crc32fast", "miniz_oxide", @@ -848,7 +903,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -1178,6 +1233,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.9" @@ -1186,9 +1250,9 @@ checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "jobserver" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" +checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" dependencies = [ "libc", ] @@ -1219,9 +1283,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.148" +version = "0.2.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" +checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" [[package]] name = "linux-raw-sys" @@ -1231,9 +1295,9 @@ checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "linux-raw-sys" -version = "0.4.8" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3852614a3bd9ca9804678ba6be5e3b8ce76dfc902cae004e3e0c44051b6e88db" +checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" [[package]] name = "log" @@ -1375,9 +1439,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", ] @@ -1436,7 +1500,7 @@ dependencies = [ "base64 0.13.1", "chrono", "http", - "itertools", + "itertools 0.10.5", "log", "num-bigint", "oauth2", @@ -1477,7 +1541,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -1500,9 +1564,9 @@ dependencies = [ [[package]] name = "ordered-float" -version = "2.10.0" +version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7940cf2ca942593318d07fcf2596cdca60a85c9e7fab408a5e21a4f9dcd40d87" +checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" dependencies = [ "num-traits", ] @@ -1521,38 +1585,38 @@ checksum = "e52c774a4c39359c1d1c52e43f73dd91a75a614652c825408eec30c95a9b2067" [[package]] name = "pbjson" -version = "0.5.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "048f9ac93c1eab514f9470c4bc8d97ca2a0a236b84f45cc19d69a59fc11467f6" +checksum = "1030c719b0ec2a2d25a5df729d6cff1acf3cc230bf766f4f97833591f7577b90" dependencies = [ - "base64 0.13.1", + "base64 0.21.4", "serde", ] [[package]] name = "pbjson-build" -version = "0.5.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdbb7b706f2afc610f3853550cdbbf6372fd324824a087806bd4480ea4996e24" +checksum = "2580e33f2292d34be285c5bc3dba5259542b083cfad6037b6d70345f24dcb735" dependencies = [ "heck", - "itertools", - "prost", - "prost-types", + "itertools 0.11.0", + "prost 0.12.1", + "prost-types 0.12.1", ] [[package]] name = "pbjson-types" -version = "0.5.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a88c8d87f99a4ac14325e7a4c24af190fca261956e3b82dd7ed67e77e6c7043" +checksum = "18f596653ba4ac51bdecbb4ef6773bc7f56042dc13927910de1684ad3d32aa12" dependencies = [ "bytes", "chrono", "pbjson", "pbjson-build", - "prost", - "prost-build", + "prost 0.12.1", + "prost-build 0.12.1", "serde", ] @@ -1602,7 +1666,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -1643,7 +1707,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -1707,11 +1771,21 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "prettyplease" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d" +dependencies = [ + "proc-macro2", + "syn 2.0.38", +] + [[package]] name = "proc-macro2" -version = "1.0.67" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] @@ -1723,7 +1797,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" dependencies = [ "bytes", - "prost-derive", + "prost-derive 0.11.9", +] + +[[package]] +name = "prost" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4fdd22f3b9c31b53c060df4a0613a1c7f062d4115a2b984dd15b1858f7e340d" +dependencies = [ + "bytes", + "prost-derive 0.12.1", ] [[package]] @@ -1734,20 +1818,42 @@ checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270" dependencies = [ "bytes", "heck", - "itertools", + "itertools 0.10.5", "lazy_static", "log", "multimap", "petgraph", - "prettyplease", - "prost", - "prost-types", + "prettyplease 0.1.25", + "prost 0.11.9", + "prost-types 0.11.9", "regex", "syn 1.0.109", "tempfile", "which", ] +[[package]] +name = "prost-build" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bdf592881d821b83d471f8af290226c8d51402259e9bb5be7f9f8bdebbb11ac" +dependencies = [ + "bytes", + "heck", + "itertools 0.11.0", + "log", + "multimap", + "once_cell", + "petgraph", + "prettyplease 0.2.15", + "prost 0.12.1", + "prost-types 0.12.1", + "regex", + "syn 2.0.38", + "tempfile", + "which", +] + [[package]] name = "prost-derive" version = "0.11.9" @@ -1755,19 +1861,41 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" dependencies = [ "anyhow", - "itertools", + "itertools 0.10.5", "proc-macro2", "quote", "syn 1.0.109", ] +[[package]] +name = "prost-derive" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "265baba7fabd416cf5078179f7d2cbeca4ce7a9041111900675ea7c4cb8a4c32" +dependencies = [ + "anyhow", + "itertools 0.11.0", + "proc-macro2", + "quote", + "syn 2.0.38", +] + [[package]] name = "prost-types" version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" dependencies = [ - "prost", + "prost 0.11.9", +] + +[[package]] +name = "prost-types" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e081b29f63d83a4bc75cfc9f3fe424f9156cf92d8a4f0c9407cce9a1b67327cf" +dependencies = [ + "prost 0.12.1", ] [[package]] @@ -1796,9 +1924,9 @@ dependencies = [ "oauth2", "openidconnect", "pem", - "prost", - "prost-build", - "prost-derive", + "prost 0.11.9", + "prost-build 0.11.9", + "prost-derive 0.11.9", "rand", "regex", "serde", @@ -1865,15 +1993,16 @@ dependencies = [ "futures-util", "itoa", "log", - "native-tls", "percent-encoding", "pin-project-lite", "rand", + "rustls", + "rustls-native-certs", "ryu", "sha1_smol", "socket2 0.4.9", "tokio", - "tokio-native-tls", + "tokio-rustls", "tokio-util", "url", ] @@ -1889,9 +2018,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.9.6" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebee201405406dbf528b8b672104ae6d6d63e6d118cb10e4d51abbc7b58044ff" +checksum = "d119d7c7ca818f8a53c300863d4f87566aac09943aef5b355bb83969dae75d87" dependencies = [ "aho-corasick", "memchr", @@ -1901,9 +2030,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.9" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59b23e92ee4318893fa3fe3e6fb365258efbfe6ac6ab30f090cdcbb7aa37efa9" +checksum = "465c6fc0621e4abc4187a2bda0937bfd4f722c2730b29562e19689ea796c9a4b" dependencies = [ "aho-corasick", "memchr", @@ -1912,9 +2041,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.7.5" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" +checksum = "56d84fdd47036b038fc80dd333d10b6aab10d5d31f4a366e20014def75328d33" [[package]] name = "reqwest" @@ -1979,9 +2108,9 @@ checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustix" -version = "0.37.24" +version = "0.37.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4279d76516df406a8bd37e7dff53fd37d1a093f997a3c34a5c21658c126db06d" +checksum = "d4eb579851244c2c03e7c24f501c3432bed80b8f720af1d6e5b0e0f01555a035" dependencies = [ "bitflags 1.3.2", "errno", @@ -1993,14 +2122,14 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.15" +version = "0.38.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2f9da0cbd88f9f09e7814e388301c8414c51c62aa6ce1e4b5c551d49d96e531" +checksum = "745ecfa778e66b2b63c88a61cb36e0eea109e803b0b86bf9879fbc77c70e86ed" dependencies = [ "bitflags 2.4.0", "errno", "libc", - "linux-raw-sys 0.4.8", + "linux-raw-sys 0.4.10", "windows-sys", ] @@ -2016,6 +2145,18 @@ dependencies = [ "sct", ] +[[package]] +name = "rustls-native-certs" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" +dependencies = [ + "openssl-probe", + "rustls-pemfile", + "schannel", + "security-framework", +] + [[package]] name = "rustls-pemfile" version = "1.0.3" @@ -2091,9 +2232,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.188" +version = "1.0.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" +checksum = "8e422a44e74ad4001bdc8eede9a4570ab52f71190e9c076d14369f38b9200537" dependencies = [ "serde_derive", ] @@ -2110,13 +2251,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.188" +version = "1.0.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" +checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -2211,23 +2352,13 @@ dependencies = [ [[package]] name = "sharded-slab" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1b21f559e07218024e7e9f90f96f601825397de0e25420135f7f952453fed0b" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" dependencies = [ "lazy_static", ] -[[package]] -name = "signal-hook" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" -dependencies = [ - "libc", - "signal-hook-registry", -] - [[package]] name = "signal-hook-registry" version = "1.4.1" @@ -2309,9 +2440,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.37" +version = "2.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8" +checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" dependencies = [ "proc-macro2", "quote", @@ -2354,7 +2485,7 @@ dependencies = [ "cfg-if", "fastrand 2.0.1", "redox_syscall", - "rustix 0.38.15", + "rustix 0.38.19", "windows-sys", ] @@ -2375,7 +2506,7 @@ checksum = "10712f02019e9288794769fba95cd6847df9874d49d871d062172f9dd41bc4cc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -2405,9 +2536,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.32.0" +version = "1.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9" +checksum = "4f38200e3ef7995e5ef13baec2f432a6da0aa9ac495b2c0e8f3b7eec2c92d653" dependencies = [ "backtrace", "bytes", @@ -2438,7 +2569,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -2522,16 +2653,15 @@ dependencies = [ [[package]] name = "tonic" -version = "0.9.2" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3082666a3a6433f7f511c7192923fa1fe07c69332d3c6a2e6bb040b569199d5a" +checksum = "d560933a0de61cf715926b9cac824d4c883c2c43142f787595e48280c40a1d0e" dependencies = [ + "async-stream", "async-trait", "axum", "base64 0.21.4", "bytes", - "futures-core", - "futures-util", "h2", "http", "http-body", @@ -2539,7 +2669,7 @@ dependencies = [ "hyper-timeout", "percent-encoding", "pin-project", - "prost", + "prost 0.12.1", "tokio", "tokio-stream", "tower", @@ -2550,15 +2680,15 @@ dependencies = [ [[package]] name = "tonic-build" -version = "0.9.2" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6fdaae4c2c638bb70fe42803a26fbd6fc6ac8c72f5c59f67ecc2a2dcabf4b07" +checksum = "9d021fc044c18582b9a2408cd0dd05b1596e3ecdb5c4df822bb0183545683889" dependencies = [ - "prettyplease", + "prettyplease 0.2.15", "proc-macro2", - "prost-build", + "prost-build 0.12.1", "quote", - "syn 1.0.109", + "syn 2.0.38", ] [[package]] @@ -2613,7 +2743,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -2803,7 +2933,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", "wasm-bindgen-shared", ] @@ -2837,7 +2967,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2873,7 +3003,7 @@ dependencies = [ "either", "home", "once_cell", - "rustix 0.38.15", + "rustix 0.38.19", ] [[package]] @@ -2975,9 +3105,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "winnow" -version = "0.5.15" +version = "0.5.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc" +checksum = "037711d82167854aff2018dfd193aa0fef5370f456732f0d5a0c59b0f1b4b907" dependencies = [ "memchr", ] @@ -3013,11 +3143,10 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.8+zstd.1.5.5" +version = "2.0.9+zstd.1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5556e6ee25d32df2586c098bbfa278803692a20d0ab9565e049480d52707ec8c" +checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656" dependencies = [ "cc", - "libc", "pkg-config", ] diff --git a/Cargo.toml b/Cargo.toml index 60b8e41..32ae1ef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,24 +12,14 @@ [dependencies] clap = { version = "4.4", features = ["derive"] } pulsar = { version = "6.0" } - redis = { version = "0.23", features = [ - "cluster-async", - "tokio-native-tls-comp", - ] } anyhow = "1.0" tracing = "0.1" - tracing-subscriber = { version = "0.3", features = [ - "fmt", - "ansi", - "json", - ], default-features = true } async-trait = "0.1.73" chirpstack_api = "4.5" + chirpstack_integration = "4.5" serde = { version = "1.0", features = ["derive"] } tokio = { version = "1.32", features = ["macros", "rt-multi-thread"] } handlebars = "4.4" - prost = "0.11" - lazy_static = "1.4" serde_json = "1.0" toml = "0.7" diff --git a/src/config.rs b/src/config.rs index f49b56c..a041914 100644 --- a/src/config.rs +++ b/src/config.rs @@ -3,13 +3,11 @@ use std::{env, fs}; use anyhow::Result; use serde::Deserialize; -use crate::integration; - #[derive(Default, Deserialize, Clone)] #[serde(default)] pub struct Configuration { #[serde(flatten)] - pub integration: integration::Configuration, + pub integration: chirpstack_integration::Configuration, pub pulsar: Pulsar, } diff --git a/src/integration.rs b/src/integration.rs deleted file mode 100644 index 8473190..0000000 --- a/src/integration.rs +++ /dev/null @@ -1,692 +0,0 @@ -use std::io::Cursor; -use std::str::FromStr; - -use anyhow::Result; -use async_trait::async_trait; -use prost::Message; -use serde::Deserialize; -use tokio::sync::RwLock; -use tracing::{error, info, warn, Level}; -use tracing_subscriber::{filter, prelude::*}; - -use chirpstack_api::integration as integration_pb; - -lazy_static! { - static ref INTEGRATION: RwLock>> = - RwLock::new(None); -} - -#[derive(Default, Deserialize, Clone)] -#[serde(default)] -pub struct Configuration { - pub logging: Logging, - pub redis: Redis, -} - -#[derive(Deserialize, Clone)] -#[serde(default)] -pub struct Logging { - pub level: String, -} - -impl Default for Logging { - fn default() -> Self { - Logging { - level: "info".into(), - } - } -} - -#[derive(Deserialize, Clone)] -#[serde(default)] -pub struct Redis { - pub servers: Vec, - pub cluster: bool, - pub key_prefix: String, - pub consumer_group: String, - pub consumer_name: String, -} - -impl Default for Redis { - fn default() -> Self { - Redis { - servers: vec!["redis://127.0.0.1/".into()], - cluster: false, - key_prefix: "".into(), - consumer_group: "integration_pulsar".into(), - consumer_name: "main".into(), - } - } -} - -#[async_trait] -pub trait IntegrationTrait { - async fn uplink_event(&self, pl: &integration_pb::UplinkEvent) -> Result<()>; - - async fn join_event(&self, pl: &integration_pb::JoinEvent) -> Result<()>; - - async fn ack_event(&self, pl: &integration_pb::AckEvent) -> Result<()>; - - async fn txack_event(&self, pl: &integration_pb::TxAckEvent) -> Result<()>; - - async fn log_event(&self, pl: &integration_pb::LogEvent) -> Result<()>; - - async fn status_event(&self, pl: &integration_pb::StatusEvent) -> Result<()>; - - async fn location_event(&self, pl: &integration_pb::LocationEvent) -> Result<()>; - - async fn integration_event(&self, pl: &integration_pb::IntegrationEvent) -> Result<()>; -} - -struct Integration { - redis_client: RedisClient, - key_prefix: String, - consumer_group: String, - consumer_name: String, -} - -enum RedisClient { - Client(redis::Client), - ClusterClient(redis::cluster::ClusterClient), -} - -impl RedisClient { - async fn get_async_connection(&self) -> Result { - match self { - RedisClient::Client(c) => Ok(RedisConnection::Client(c.get_async_connection().await?)), - RedisClient::ClusterClient(c) => Ok(RedisConnection::ClusterClient( - c.get_async_connection().await?, - )), - } - } -} - -enum RedisConnection { - Client(redis::aio::Connection), - ClusterClient(redis::cluster_async::ClusterConnection), -} - -impl redis::aio::ConnectionLike for RedisConnection { - fn get_db(&self) -> i64 { - match self { - RedisConnection::Client(c) => c.get_db(), - RedisConnection::ClusterClient(c) => c.get_db(), - } - } - - fn req_packed_command<'a>( - &'a mut self, - cmd: &'a redis::Cmd, - ) -> redis::RedisFuture<'a, redis::Value> { - match self { - RedisConnection::Client(c) => c.req_packed_command(cmd), - RedisConnection::ClusterClient(c) => c.req_packed_command(cmd), - } - } - - fn req_packed_commands<'a>( - &'a mut self, - cmd: &'a redis::Pipeline, - offset: usize, - count: usize, - ) -> redis::RedisFuture<'a, Vec> { - match self { - RedisConnection::Client(c) => c.req_packed_commands(cmd, offset, count), - RedisConnection::ClusterClient(c) => c.req_packed_commands(cmd, offset, count), - } - } -} - -impl Integration { - fn new(conf: &Configuration) -> Result { - info!("Initializing ChirpStack Integration backend"); - - let redis_client = if conf.redis.cluster { - info!("Setting up Redis Cluster client"); - RedisClient::ClusterClient( - redis::cluster::ClusterClientBuilder::new(conf.redis.servers.clone()).build()?, - ) - } else { - info!(server = %conf.redis.servers[0], "Setting up Redis client"); - RedisClient::Client(redis::Client::open(conf.redis.servers[0].clone())?) - }; - - Ok(Integration { - redis_client, - key_prefix: conf.redis.key_prefix.clone(), - consumer_group: conf.redis.consumer_group.clone(), - consumer_name: conf.redis.consumer_name.clone(), - }) - } - - async fn start(&self) -> Result<()> { - info!("Getting Redis connection"); - let mut redis_conn = self.redis_client.get_async_connection().await?; - - let key = format!("{}device:stream:event", self.key_prefix); - - // Try to create the consumer group. This will fail in case the consumer group already exists. - let _: usize = match redis::cmd("XGROUP") - .arg("CREATE") - .arg(&key) - .arg(&self.consumer_group) - .arg(0) - .arg("MKSTREAM") - .query_async(&mut redis_conn) - .await - { - Ok(v) => v, - Err(e) => { - warn!(error = %e, "Could not create Redis consumer group, ignore this error if the group already exists"); - 0 - } - }; - - loop { - let srr: redis::streams::StreamReadReply = redis::cmd("XREADGROUP") - .arg("GROUP") - .arg(&self.consumer_group) - .arg(&self.consumer_name) - .arg("COUNT") - .arg(10) - .arg("BLOCK") - .arg(1000) - .arg("STREAMS") - .arg(&key) - .arg(">") - .query_async(&mut redis_conn) - .await?; - - for stream_key in &srr.keys { - for stream_id in &stream_key.ids { - redis::cmd("XACK") - .arg(&key) - .arg(&self.consumer_group) - .arg(&stream_id.id) - .query_async(&mut redis_conn) - .await?; - - for (k, v) in &stream_id.map { - let res = || -> Result<()> { - info!(key = %k, "Event received from Redis stream"); - match k.as_ref() { - "up" => { - if let redis::Value::Data(b) = v { - let pl = integration_pb::UplinkEvent::decode( - &mut Cursor::new(b), - )?; - tokio::spawn(uplink_event(pl)); - } - } - "join" => { - if let redis::Value::Data(b) = v { - let pl = - integration_pb::JoinEvent::decode(&mut Cursor::new(b))?; - tokio::spawn(join_event(pl)); - } - } - "ack" => { - if let redis::Value::Data(b) = v { - let pl = - integration_pb::AckEvent::decode(&mut Cursor::new(b))?; - tokio::spawn(ack_event(pl)); - } - } - "txack" => { - if let redis::Value::Data(b) = v { - let pl = integration_pb::TxAckEvent::decode( - &mut Cursor::new(b), - )?; - tokio::spawn(txack_event(pl)); - } - } - "status" => { - if let redis::Value::Data(b) = v { - let pl = integration_pb::StatusEvent::decode( - &mut Cursor::new(b), - )?; - tokio::spawn(status_event(pl)); - } - } - "log" => { - if let redis::Value::Data(b) = v { - let pl = - integration_pb::LogEvent::decode(&mut Cursor::new(b))?; - tokio::spawn(log_event(pl)); - } - } - "location" => { - if let redis::Value::Data(b) = v { - let pl = integration_pb::LocationEvent::decode( - &mut Cursor::new(b), - )?; - tokio::spawn(location_event(pl)); - } - } - "integration" => { - if let redis::Value::Data(b) = v { - let pl = integration_pb::IntegrationEvent::decode( - &mut Cursor::new(b), - )?; - tokio::spawn(integration_event(pl)); - } - } - _ => { - error!(key = %k, "Unexpected event key"); - } - } - - Ok(()) - }(); - - if let Err(e) = res { - error!(key = %k, error = %e, "Parsing event error"); - } - } - } - } - } - } -} - -pub fn setup_log(conf: &Configuration) -> Result<()> { - let filter = filter::LevelFilter::from_level(Level::from_str(&conf.logging.level).unwrap()); - - tracing_subscriber::registry() - .with(tracing_subscriber::fmt::layer()) - .with(filter) - .init(); - - info!( - "Starting {} (version: {}, docs: {})", - env!("CARGO_PKG_DESCRIPTION"), - env!("CARGO_PKG_VERSION"), - env!("CARGO_PKG_HOMEPAGE") - ); - - Ok(()) -} - -pub async fn register(integration: Box) { - let mut int = INTEGRATION.write().await; - *int = Some(integration); -} - -pub async fn start(conf: Configuration) -> Result<()> { - let int = Integration::new(&conf)?; - int.start().await -} - -async fn uplink_event(pl: integration_pb::UplinkEvent) { - let integration = INTEGRATION.read().await; - if let Err(e) = integration.as_ref().unwrap().uplink_event(&pl).await { - error!(error = %e, "Uplink event error"); - } -} - -async fn join_event(pl: integration_pb::JoinEvent) { - let integration = INTEGRATION.read().await; - if let Err(e) = integration.as_ref().unwrap().join_event(&pl).await { - error!(error = %e, "Join event error"); - } -} - -async fn ack_event(pl: integration_pb::AckEvent) { - let integration = INTEGRATION.read().await; - if let Err(e) = integration.as_ref().unwrap().ack_event(&pl).await { - error!(error = %e, "Ack event error"); - } -} - -async fn txack_event(pl: integration_pb::TxAckEvent) { - let integration = INTEGRATION.read().await; - if let Err(e) = integration.as_ref().unwrap().txack_event(&pl).await { - error!(error = %e, "Tx ack event error"); - } -} - -async fn status_event(pl: integration_pb::StatusEvent) { - let integration = INTEGRATION.read().await; - if let Err(e) = integration.as_ref().unwrap().status_event(&pl).await { - error!(error = %e, "Status event error"); - } -} - -async fn log_event(pl: integration_pb::LogEvent) { - let integration = INTEGRATION.read().await; - if let Err(e) = integration.as_ref().unwrap().log_event(&pl).await { - error!(error = %e, "Log event error"); - } -} - -async fn location_event(pl: integration_pb::LocationEvent) { - let integration = INTEGRATION.read().await; - if let Err(e) = integration.as_ref().unwrap().location_event(&pl).await { - error!(error = %e, "Location event error"); - } -} - -async fn integration_event(pl: integration_pb::IntegrationEvent) { - let integration = INTEGRATION.read().await; - if let Err(e) = integration.as_ref().unwrap().integration_event(&pl).await { - error!(error = %e, "Integration event error"); - } -} - -#[cfg(test)] -mod test { - use super::*; - use std::env; - use std::time::Duration; - - use tokio::sync::RwLock; - use tokio::time::sleep; - - lazy_static! { - static ref UPLINK_EVENTS: RwLock> = - RwLock::new(Vec::new()); - static ref JOIN_EVENTS: RwLock> = RwLock::new(Vec::new()); - static ref ACK_EVENTS: RwLock> = RwLock::new(Vec::new()); - static ref TXACK_EVENTS: RwLock> = RwLock::new(Vec::new()); - static ref LOG_EVENTS: RwLock> = RwLock::new(Vec::new()); - static ref STATUS_EVENTS: RwLock> = - RwLock::new(Vec::new()); - static ref LOCATION_EVENTS: RwLock> = - RwLock::new(Vec::new()); - static ref INTEGRATION_EVENTS: RwLock> = - RwLock::new(Vec::new()); - } - - struct MockIntegration {} - - #[async_trait] - impl IntegrationTrait for MockIntegration { - async fn uplink_event(&self, pl: &integration_pb::UplinkEvent) -> Result<()> { - UPLINK_EVENTS.write().await.push(pl.clone()); - Ok(()) - } - - async fn join_event(&self, pl: &integration_pb::JoinEvent) -> Result<()> { - JOIN_EVENTS.write().await.push(pl.clone()); - Ok(()) - } - - async fn ack_event(&self, pl: &integration_pb::AckEvent) -> Result<()> { - ACK_EVENTS.write().await.push(pl.clone()); - Ok(()) - } - - async fn txack_event(&self, pl: &integration_pb::TxAckEvent) -> Result<()> { - TXACK_EVENTS.write().await.push(pl.clone()); - Ok(()) - } - - async fn log_event(&self, pl: &integration_pb::LogEvent) -> Result<()> { - LOG_EVENTS.write().await.push(pl.clone()); - Ok(()) - } - - async fn status_event(&self, pl: &integration_pb::StatusEvent) -> Result<()> { - STATUS_EVENTS.write().await.push(pl.clone()); - Ok(()) - } - - async fn location_event(&self, pl: &integration_pb::LocationEvent) -> Result<()> { - LOCATION_EVENTS.write().await.push(pl.clone()); - Ok(()) - } - - async fn integration_event(&self, pl: &integration_pb::IntegrationEvent) -> Result<()> { - INTEGRATION_EVENTS.write().await.push(pl.clone()); - Ok(()) - } - } - - #[tokio::test] - async fn test_integration() { - let redis_url = env::var("TEST_REDIS_URL").unwrap_or("redis://127.0.0.1/1".to_string()); - - setup_log(&Configuration::default()).unwrap(); - register(Box::new(MockIntegration {})).await; - - let conf = Configuration { - redis: Redis { - servers: vec![redis_url.clone()], - consumer_group: "test_group".into(), - consumer_name: "test_consumer".into(), - ..Default::default() - }, - ..Default::default() - }; - - tokio::spawn(start(conf)); - sleep(Duration::from_millis(100)).await; - - let redis_client = redis::Client::open(redis_url).unwrap(); - let mut redis_conn = redis_client.get_async_connection().await.unwrap(); - - println!("Uplink"); - - // uplink - let pl = integration_pb::UplinkEvent::default(); - let _: String = redis::cmd("XADD") - .arg("device:stream:event") - .arg("MAXLEN") - .arg(1) - .arg("*") - .arg("up") - .arg(pl.encode_to_vec()) - .query_async(&mut redis_conn) - .await - .unwrap(); - - sleep(Duration::from_millis(100)).await; - - let pl_recv = UPLINK_EVENTS - .write() - .await - .drain(0..1) - .collect::>() - .first() - .cloned() - .unwrap(); - - assert_eq!(pl, pl_recv); - - println!("Join"); - - // join - let pl = integration_pb::JoinEvent::default(); - let _: String = redis::cmd("XADD") - .arg("device:stream:event") - .arg("MAXLEN") - .arg(1) - .arg("*") - .arg("join") - .arg(pl.encode_to_vec()) - .query_async(&mut redis_conn) - .await - .unwrap(); - - sleep(Duration::from_millis(100)).await; - - let pl_recv = JOIN_EVENTS - .write() - .await - .drain(0..1) - .collect::>() - .first() - .cloned() - .unwrap(); - - assert_eq!(pl, pl_recv); - - println!("Ack"); - - // ack - let pl = integration_pb::AckEvent::default(); - let _: String = redis::cmd("XADD") - .arg("device:stream:event") - .arg("MAXLEN") - .arg(1) - .arg("*") - .arg("ack") - .arg(pl.encode_to_vec()) - .query_async(&mut redis_conn) - .await - .unwrap(); - - sleep(Duration::from_millis(100)).await; - - let pl_recv = ACK_EVENTS - .write() - .await - .drain(0..1) - .collect::>() - .first() - .cloned() - .unwrap(); - - assert_eq!(pl, pl_recv); - - println!("TxAck"); - - // txack - let pl = integration_pb::TxAckEvent::default(); - let _: String = redis::cmd("XADD") - .arg("device:stream:event") - .arg("MAXLEN") - .arg(1) - .arg("*") - .arg("txack") - .arg(pl.encode_to_vec()) - .query_async(&mut redis_conn) - .await - .unwrap(); - - sleep(Duration::from_millis(100)).await; - - let pl_recv = TXACK_EVENTS - .write() - .await - .drain(0..1) - .collect::>() - .first() - .cloned() - .unwrap(); - - assert_eq!(pl, pl_recv); - - println!("Log"); - - // log - let pl = integration_pb::LogEvent::default(); - let _: String = redis::cmd("XADD") - .arg("device:stream:event") - .arg("MAXLEN") - .arg(1) - .arg("*") - .arg("log") - .arg(pl.encode_to_vec()) - .query_async(&mut redis_conn) - .await - .unwrap(); - - sleep(Duration::from_millis(100)).await; - - let pl_recv = LOG_EVENTS - .write() - .await - .drain(0..1) - .collect::>() - .first() - .cloned() - .unwrap(); - - assert_eq!(pl, pl_recv); - - println!("Status"); - - // status - let pl = integration_pb::StatusEvent::default(); - let _: String = redis::cmd("XADD") - .arg("device:stream:event") - .arg("MAXLEN") - .arg(1) - .arg("*") - .arg("status") - .arg(pl.encode_to_vec()) - .query_async(&mut redis_conn) - .await - .unwrap(); - - sleep(Duration::from_millis(100)).await; - - let pl_recv = STATUS_EVENTS - .write() - .await - .drain(0..1) - .collect::>() - .first() - .cloned() - .unwrap(); - - assert_eq!(pl, pl_recv); - - println!("Location"); - - // location - let pl = integration_pb::LocationEvent::default(); - let _: String = redis::cmd("XADD") - .arg("device:stream:event") - .arg("MAXLEN") - .arg(1) - .arg("*") - .arg("location") - .arg(pl.encode_to_vec()) - .query_async(&mut redis_conn) - .await - .unwrap(); - - sleep(Duration::from_millis(100)).await; - - let pl_recv = LOCATION_EVENTS - .write() - .await - .drain(0..1) - .collect::>() - .first() - .cloned() - .unwrap(); - - assert_eq!(pl, pl_recv); - - println!("Integration"); - - // integration - let pl = integration_pb::IntegrationEvent::default(); - let _: String = redis::cmd("XADD") - .arg("device:stream:event") - .arg("MAXLEN") - .arg(1) - .arg("*") - .arg("integration") - .arg(pl.encode_to_vec()) - .query_async(&mut redis_conn) - .await - .unwrap(); - - sleep(Duration::from_millis(100)).await; - - let pl_recv = INTEGRATION_EVENTS - .write() - .await - .drain(0..1) - .collect::>() - .first() - .cloned() - .unwrap(); - - assert_eq!(pl, pl_recv); - } -} diff --git a/src/main.rs b/src/main.rs index bdee3ac..a66579e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,8 @@ #[macro_use] -extern crate lazy_static; -#[macro_use] extern crate anyhow; use clap::Parser; mod config; -mod integration; mod pulsar; #[derive(Parser)] @@ -20,11 +17,13 @@ async fn main() { let cli = Cli::parse(); let conf = config::Configuration::load(&cli.config).unwrap(); - integration::setup_log(&conf.integration).unwrap(); - integration::register(Box::new( + chirpstack_integration::setup_log(&conf.integration).unwrap(); + chirpstack_integration::register(Box::new( pulsar::Integration::new(conf.pulsar.clone()).await.unwrap(), )) .await; - integration::start(conf.integration).await.unwrap(); + chirpstack_integration::start(conf.integration) + .await + .unwrap(); } diff --git a/src/pulsar.rs b/src/pulsar.rs index 58af55a..865307f 100644 --- a/src/pulsar.rs +++ b/src/pulsar.rs @@ -1,14 +1,13 @@ use anyhow::Result; use async_trait::async_trait; use handlebars::Handlebars; -use prost::Message; use pulsar::Pulsar; use serde::Serialize; use tracing::{info, trace}; use crate::config; -use crate::integration::IntegrationTrait; -use chirpstack_api::integration as integration_pb; +use chirpstack_api::{integration as integration_pb, prost::Message}; +use chirpstack_integration::IntegrationTrait; #[derive(Serialize)] struct EventTopicContext {