From 88b18413a15b479a68272c60f7825294d44d1cd9 Mon Sep 17 00:00:00 2001 From: Dmitry Shlagoff Date: Thu, 18 May 2023 11:49:45 +0200 Subject: [PATCH 01/35] Add endpoint for ban initialization --- ...0230517104549_add_ban_account_op_table.sql | 5 ++ sqlx-data.json | 48 ++++++++++++ src/app/api/v1/account/ban.rs | 75 +++++++++++++++++++ src/app/api/v1/{account.rs => account/mod.rs} | 4 + src/app/error.rs | 14 ++++ src/app/http.rs | 1 + src/db/ban_account_op.rs | 38 ++++++++++ src/db/mod.rs | 17 +++-- 8 files changed, 194 insertions(+), 8 deletions(-) create mode 100644 migrations/20230517104549_add_ban_account_op_table.sql create mode 100644 src/app/api/v1/account/ban.rs rename src/app/api/v1/{account.rs => account/mod.rs} (99%) create mode 100644 src/db/ban_account_op.rs diff --git a/migrations/20230517104549_add_ban_account_op_table.sql b/migrations/20230517104549_add_ban_account_op_table.sql new file mode 100644 index 00000000..f903ec3f --- /dev/null +++ b/migrations/20230517104549_add_ban_account_op_table.sql @@ -0,0 +1,5 @@ +CREATE TABLE IF NOT EXISTS ban_account_op ( + user_account account_id PRIMARY KEY, + last_op_id uuid NOT NULL, + last_op_done boolean NOT NULL DEFAULT false +); diff --git a/sqlx-data.json b/sqlx-data.json index 4e3929fd..f6091d8d 100644 --- a/sqlx-data.json +++ b/sqlx-data.json @@ -1526,6 +1526,54 @@ }, "query": "\n SELECT\n id::text AS \"id!: String\"\n FROM class\n WHERE conference_room_id = $1\n " }, + "822f926f43aa41ec0c5f772fb842755f8283283edf3c2c769506b3ed08a79607": { + "describe": { + "columns": [ + { + "name": "user_account: _", + "ordinal": 0, + "type_info": { + "Custom": { + "kind": { + "Composite": [ + [ + "label", + "Text" + ], + [ + "audience", + "Text" + ] + ] + }, + "name": "account_id" + } + } + }, + { + "name": "last_op_id: _", + "ordinal": 1, + "type_info": "Uuid" + }, + { + "name": "last_op_done", + "ordinal": 2, + "type_info": "Bool" + } + ], + "nullable": [ + false, + false, + false + ], + "parameters": { + "Left": [ + "Record" + ] + } + }, + "query": "\n SELECT\n user_account AS \"user_account: _\",\n last_op_id AS \"last_op_id: _\",\n last_op_done\n FROM ban_account_op\n WHERE\n user_account = $1\n LIMIT 1;\n " + }, "ac4ac9431173165543dbc459bb3b2b9b4461f7016aa6d6967cc9f5f832f208bb": { "describe": { "columns": [ diff --git a/src/app/api/v1/account/ban.rs b/src/app/api/v1/account/ban.rs new file mode 100644 index 00000000..09829137 --- /dev/null +++ b/src/app/api/v1/account/ban.rs @@ -0,0 +1,75 @@ +use std::sync::Arc; + +use axum::{extract::Path, response::Response, Extension, Json}; +use hyper::Body; +use serde_derive::Deserialize; +use svc_authn::AccountId; +use uuid::Uuid; + +use svc_utils::extractors::AccountIdExtractor; + +use crate::{ + app::{ + api::v1::find_class, + error::{ErrorExt, ErrorKind as AppErrorKind}, + metrics::AuthorizeMetrics, + AppContext, AuthzObject, + }, + db::ban_account_op, +}; + +use super::AppResult; + +#[derive(Deserialize)] +pub struct BanPayload { + last_seen_op_id: Uuid, + ban: bool, + class_id: Uuid, +} + +pub async fn ban( + Extension(ctx): Extension>, + Path(account_to_ban): Path, + AccountIdExtractor(account_id): AccountIdExtractor, + Json(payload): Json, +) -> AppResult { + let class = find_class(ctx.as_ref(), payload.class_id) + .await + .error(AppErrorKind::ClassNotFound)?; + + let object = AuthzObject::new(&["classrooms", &class.id().to_string()]).into(); + ctx.authz() + .authorize( + class.audience().to_owned(), + account_id.clone(), + object, + "update".into(), + ) + .await + .measure()?; + + let mut conn = ctx + .get_conn() + .await + .error(AppErrorKind::DbConnAcquisitionFailed)?; + + let last_ban_account_op = ban_account_op::ReadQuery::by_id(&account_to_ban) + .execute(&mut conn) + .await + .error(AppErrorKind::DbQueryFailed)?; + + if let Some(op) = last_ban_account_op { + if op.last_op_id != payload.last_seen_op_id { + return Err(AppErrorKind::OperationIdObsolete.into()); + } + + if !op.last_op_done { + return Err(AppErrorKind::OperationInProgress.into()); + } + } + + // schedule task to ourselves + // update ban account op + + Ok(Response::builder().status(200).body(Body::empty()).unwrap()) +} diff --git a/src/app/api/v1/account.rs b/src/app/api/v1/account/mod.rs similarity index 99% rename from src/app/api/v1/account.rs rename to src/app/api/v1/account/mod.rs index 6d47b694..e28d2596 100644 --- a/src/app/api/v1/account.rs +++ b/src/app/api/v1/account/mod.rs @@ -4,6 +4,10 @@ use axum::{extract::Path, Extension}; use svc_agent::AccountId; use svc_utils::extractors::AccountIdExtractor; +mod ban; + +pub use ban::ban; + use crate::{ app::{ api::IntoJsonResponse, diff --git a/src/app/error.rs b/src/app/error.rs index 30fed9f3..746abc93 100644 --- a/src/app/error.rs +++ b/src/app/error.rs @@ -40,6 +40,8 @@ pub enum ErrorKind { InternalFailure, CreationWhiteboardFailed, ClassAlreadyEstablished, + OperationIdObsolete, + OperationInProgress, } impl ErrorKind { @@ -191,6 +193,18 @@ impl From for ErrorKindProperties { title: "Class already established", is_notify_sentry: false, }, + ErrorKind::OperationIdObsolete => ErrorKindProperties { + status: ResponseStatus::CONFLICT, + kind: "operation_id_obsolete", + title: "Operation id obsolete, should fetch latest state", + is_notify_sentry: false, + }, + ErrorKind::OperationInProgress => ErrorKindProperties { + status: ResponseStatus::CONFLICT, + kind: "operation_in_progress", + title: "Operation is not completed yet, retry later", + is_notify_sentry: false, + }, } } } diff --git a/src/app/http.rs b/src/app/http.rs index 3a6f0e89..00c6b0e5 100644 --- a/src/app/http.rs +++ b/src/app/http.rs @@ -157,6 +157,7 @@ fn utils_router() -> Router { "/api/v1/account/properties/:property_id", get(account::read_property).put(account::update_property), ) + .metered_route("/api/v1/account/:id/ban", post(account::ban)) .metered_route( "/api/v1/transcoding/minigroup/:id/restart", post(restart_transcoding_minigroup), diff --git a/src/db/ban_account_op.rs b/src/db/ban_account_op.rs new file mode 100644 index 00000000..b9dc8556 --- /dev/null +++ b/src/db/ban_account_op.rs @@ -0,0 +1,38 @@ +use sqlx::PgConnection; +use svc_authn::AccountId; +use uuid::Uuid; + +pub struct Object { + pub user_account: AccountId, + pub last_op_id: Uuid, + pub last_op_done: bool, +} + +pub struct ReadQuery<'a> { + user_account: &'a AccountId, +} + +impl<'a> ReadQuery<'a> { + pub fn by_id(user_account: &'a AccountId) -> Self { + Self { user_account } + } + + pub async fn execute(self, conn: &mut PgConnection) -> sqlx::Result> { + sqlx::query_as!( + Object, + r#" + SELECT + user_account AS "user_account: _", + last_op_id AS "last_op_id: _", + last_op_done + FROM ban_account_op + WHERE + user_account = $1 + LIMIT 1; + "#, + self.user_account as &AccountId, + ) + .fetch_optional(conn) + .await + } +} diff --git a/src/db/mod.rs b/src/db/mod.rs index b40625e6..3426bf4d 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -2,7 +2,7 @@ use std::time::Duration; use sqlx::postgres::{PgPool, PgPoolOptions}; -pub(crate) async fn create_pool( +pub async fn create_pool( url: &str, size: u32, idle_size: Option, @@ -19,10 +19,11 @@ pub(crate) async fn create_pool( .expect("Failed to create sqlx database pool") } -pub(crate) mod account; -pub(crate) mod authz; -pub(crate) mod class; -pub(crate) mod frontend; -pub(crate) mod record_timestamp; -pub(crate) mod recording; -pub(crate) mod scope; +pub mod account; +pub mod authz; +pub mod ban_account_op; +pub mod class; +pub mod frontend; +pub mod record_timestamp; +pub mod recording; +pub mod scope; From 5af42da611b029b855958723f54af45a992e86ab Mon Sep 17 00:00:00 2001 From: Dmitry Shlagoff Date: Thu, 18 May 2023 11:50:52 +0200 Subject: [PATCH 02/35] Add NatsClient to AppContext --- Cargo.lock | 1142 +++++++++++++++++++++++++++++++------ Cargo.toml | 1 + rust-toolchain.toml | 2 +- src/app/mod.rs | 13 + src/app/tide_state/mod.rs | 15 + src/config.rs | 1 + src/test_helpers/state.rs | 27 + 7 files changed, 1031 insertions(+), 170 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 93335813..1cc13a58 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -28,11 +28,22 @@ dependencies = [ "version_check", ] +[[package]] +name = "ahash" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", +] + [[package]] name = "aho-corasick" -version = "0.7.18" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" dependencies = [ "memchr", ] @@ -72,6 +83,51 @@ dependencies = [ "futures-core", ] +[[package]] +name = "async-nats" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1174495e436c928905018f10a36160f7a8a6786450f50f4ce7fba05d1539704c" +dependencies = [ + "async-nats-tokio-rustls-deps", + "base64 0.13.0", + "base64-url", + "bytes", + "futures", + "http", + "itoa 1.0.1", + "memchr", + "nkeys", + "nuid", + "once_cell", + "rand", + "regex", + "ring", + "rustls-native-certs", + "rustls-pemfile", + "serde", + "serde_json", + "serde_nanos", + "serde_repr", + "thiserror", + "time 0.3.21", + "tokio", + "tokio-retry", + "tracing", + "url 2.2.2", +] + +[[package]] +name = "async-nats-tokio-rustls-deps" +version = "0.24.0-ALPHA.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cdefe54cd7867d937c0a507d2a3a830af410044282cd3e4002b5b7860e1892e" +dependencies = [ + "rustls 0.21.1", + "tokio", + "webpki 0.22.0", +] + [[package]] name = "async-trait" version = "0.1.50" @@ -79,8 +135,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b98e84bbb4cbcdd97da190ba0c58a1bb0de2c1fdf67d159e192ed766aeca722" dependencies = [ "proc-macro2", - "quote 1.0.21", - "syn 1.0.99", + "quote 1.0.27", + "syn 1.0.109", ] [[package]] @@ -92,11 +148,20 @@ dependencies = [ "num-traits", ] +[[package]] +name = "atoi" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c57d12312ff59c811c0643f4d80830505833c9ffaebd193d819392b265be8e" +dependencies = [ + "num-traits", +] + [[package]] name = "autocfg" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "axum" @@ -171,6 +236,27 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +[[package]] +name = "base64" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" + +[[package]] +name = "base64-url" +version = "1.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a99c239d0c7e77c85dddfa9cebce48704b3c49550fcd3b84dd637e4484899f" +dependencies = [ + "base64 0.13.0", +] + +[[package]] +name = "base64ct" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6b4d9b1225d28d360ec6a231d65af1fd99a2a095154c8040689617290569c5c" + [[package]] name = "bigdecimal" version = "0.2.2" @@ -188,6 +274,15 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + [[package]] name = "block-buffer" version = "0.10.2" @@ -211,9 +306,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.2.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" [[package]] name = "cache-padded" @@ -280,6 +375,12 @@ dependencies = [ "toml", ] +[[package]] +name = "const-oid" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d6f2aa4d0537bcc1c74df8755072bd31c1ef1a3a1b85a68e8404a8c353b7b8b" + [[package]] name = "core-foundation" version = "0.9.1" @@ -311,7 +412,16 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49fc9a695bca7f35f5f4c15cddc84415f66a74ea78eef08e90c5024f2b540e23" dependencies = [ - "crc-catalog", + "crc-catalog 1.1.1", +] + +[[package]] +name = "crc" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe" +dependencies = [ + "crc-catalog 2.2.0", ] [[package]] @@ -320,6 +430,12 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccaeedb56da03b09f598226e25e80088cb4cd25f316e6e4df7d695f0feeb1403" +[[package]] +name = "crc-catalog" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cace84e55f07e7301bae1c519df89cdad8cc3cd868413d3fdbdeca9ff3db484" + [[package]] name = "crossbeam-channel" version = "0.5.1" @@ -360,6 +476,25 @@ dependencies = [ "typenum", ] +[[package]] +name = "curve25519-dalek" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.5.1", + "subtle", + "zeroize", +] + +[[package]] +name = "data-encoding" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb" + [[package]] name = "debug_stub_derive" version = "0.3.0" @@ -387,7 +522,39 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" dependencies = [ "serde", - "uuid 1.1.2", + "uuid 1.3.3", +] + +[[package]] +name = "der" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79b71cca7d95d7681a4b3b9cdf63c8dbc3730d0584c2c74e31416d64a90493f4" +dependencies = [ + "const-oid", +] + +[[package]] +name = "diesel" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b28135ecf6b7d446b43e27e225622a038cc4e2930a1022f51cdb97ada19b8e4d" +dependencies = [ + "bitflags", + "byteorder", + "diesel_derives", + "pq-sys", +] + +[[package]] +name = "diesel_derives" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45f5098f628d02a7a0f68ddba586fb61e80edec3bdc1be3b921f4ceec60858d3" +dependencies = [ + "proc-macro2", + "quote 1.0.27", + "syn 1.0.109", ] [[package]] @@ -396,13 +563,22 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + [[package]] name = "digest" version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" dependencies = [ - "block-buffer", + "block-buffer 0.10.2", "crypto-common", "subtle", ] @@ -461,11 +637,12 @@ dependencies = [ "serde_qs", "signal-hook", "signal-hook-tokio", - "sqlx", - "svc-agent", - "svc-authn", + "sqlx 0.5.9", + "svc-agent 0.19.4", + "svc-authn 0.7.0", "svc-authz", "svc-error 0.4.0", + "svc-nats-client", "svc-utils", "tokio", "tower", @@ -487,6 +664,12 @@ version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" +[[package]] +name = "dotenvy" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" + [[package]] name = "downcast" version = "0.10.0" @@ -499,6 +682,27 @@ version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0" +[[package]] +name = "ed25519" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" +dependencies = [ + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" +dependencies = [ + "curve25519-dalek", + "ed25519", + "sha2 0.9.9", + "zeroize", +] + [[package]] name = "either" version = "1.6.1" @@ -571,9 +775,9 @@ checksum = "69a039c3498dc930fe810151a34ba0c1c70b02b8625035592e74432f678591f2" [[package]] name = "futures" -version = "0.3.24" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f21eda599937fba36daeb58a22e8f5cee2d14c4a17b5b7739c7c8e5e3b8230c" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" dependencies = [ "futures-channel", "futures-core", @@ -586,9 +790,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.24" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" dependencies = [ "futures-core", "futures-sink", @@ -596,15 +800,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.24" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" [[package]] name = "futures-executor" -version = "0.3.24" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ff63c23854bee61b6e9cd331d523909f238fc7636290b96826e9cfa5faa00ab" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" dependencies = [ "futures-core", "futures-task", @@ -624,38 +828,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.24" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbf4d2a7a308fd4578637c0b17c7e1c7ba127b8f6ba00b29f717e9655d85eb68" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" [[package]] name = "futures-macro" -version = "0.3.24" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42cd15d1c7456c04dbdf7e88bcd69760d74f3a798d6444e16974b505b0e62f17" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", - "quote 1.0.21", - "syn 1.0.99", + "quote 1.0.27", + "syn 2.0.16", ] [[package]] name = "futures-sink" -version = "0.3.24" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b20ba5a92e727ba30e72834706623d94ac93a725410b6a6b6fbc1b07f7ba56" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" [[package]] name = "futures-task" -version = "0.3.24" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" [[package]] name = "futures-util" -version = "0.3.24" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ "futures-channel", "futures-core", @@ -727,7 +931,16 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" dependencies = [ - "ahash", + "ahash 0.7.6", +] + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash 0.8.3", ] [[package]] @@ -739,6 +952,15 @@ dependencies = [ "hashbrown 0.11.2", ] +[[package]] +name = "hashlink" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0761a1b9491c4f2e3d66aa0f62d0fba0af9a0e2852e4d48ea506632a4b56e6aa" +dependencies = [ + "hashbrown 0.13.2", +] + [[package]] name = "hashring" version = "0.3.0" @@ -812,7 +1034,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest", + "digest 0.10.3", ] [[package]] @@ -828,9 +1050,9 @@ dependencies = [ [[package]] name = "http" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" dependencies = [ "bytes", "fnv", @@ -1018,9 +1240,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.121" +version = "0.2.144" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efaa7b300f3b5fe8eb6bf21ce3895e1751d9665086af2d64b42f19701015ff4f" +checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" [[package]] name = "lock_api" @@ -1073,7 +1295,7 @@ version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "66b48670c893079d3c2ed79114e3644b7004df1c361a4e0ad52e2e6940d07c3d" dependencies = [ - "digest", + "digest 0.10.3", ] [[package]] @@ -1121,25 +1343,14 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.2" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52da4364ffb0e4fe33a9841a98a3f3014fb964045ce4f7a45a398243c8d6b0c9" +checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" dependencies = [ "libc", "log", - "miow", - "ntapi", "wasi 0.11.0+wasi-snapshot-preview1", - "winapi", -] - -[[package]] -name = "miow" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" -dependencies = [ - "winapi", + "windows-sys 0.45.0", ] [[package]] @@ -1165,8 +1376,8 @@ checksum = "5dd4234635bca06fc96c7368d038061e0aae1b00a764dc817e900dc974e3deea" dependencies = [ "cfg-if", "proc-macro2", - "quote 1.0.21", - "syn 1.0.99", + "quote 1.0.27", + "syn 1.0.109", ] [[package]] @@ -1196,6 +1407,21 @@ dependencies = [ "tempfile", ] +[[package]] +name = "nkeys" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e66a7cd1358277b2a6f77078e70aea7315ff2f20db969cc61153103ec162594" +dependencies = [ + "byteorder", + "data-encoding", + "ed25519-dalek", + "getrandom", + "log", + "rand", + "signatory", +] + [[package]] name = "nom" version = "7.1.1" @@ -1213,12 +1439,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" [[package]] -name = "ntapi" -version = "0.3.6" +name = "nuid" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" +checksum = "20c1bb65186718d348306bf1afdeb20d9ab45b2ab80fb793c0fdcf59ffbb4f38" dependencies = [ - "winapi", + "lazy_static", + "rand", ] [[package]] @@ -1272,15 +1499,6 @@ dependencies = [ "libc", ] -[[package]] -name = "num_threads" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aba1801fb138d8e85e11d0fc70baf4fe1cdfffda7c6cd34a854905df588e5ed0" -dependencies = [ - "libc", -] - [[package]] name = "object" version = "0.25.3" @@ -1292,9 +1510,15 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.9.0" +version = "1.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" + +[[package]] +name = "opaque-debug" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "openssl" @@ -1374,7 +1598,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-sys", + "windows-sys 0.32.0", ] [[package]] @@ -1400,6 +1624,15 @@ dependencies = [ "regex", ] +[[package]] +name = "pem-rfc7468" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84e93a3b1cc0510b03020f33f21e62acdde3dcaef432edc95bea377fbd4c2cd4" +dependencies = [ + "base64ct", +] + [[package]] name = "percent-encoding" version = "1.0.1" @@ -1428,8 +1661,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c950132583b500556b1efd71d45b319029f2b71518d979fcc208e16b42426f" dependencies = [ "proc-macro2", - "quote 1.0.21", - "syn 1.0.99", + "quote 1.0.27", + "syn 1.0.109", ] [[package]] @@ -1444,6 +1677,18 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkcs8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee3ef9b64d26bad0536099c816c6734379e45bbd5f14798def6809e5cc350447" +dependencies = [ + "der", + "pem-rfc7468", + "spki", + "zeroize", +] + [[package]] name = "pkg-config" version = "0.3.19" @@ -1462,6 +1707,15 @@ version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" +[[package]] +name = "pq-sys" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31c0052426df997c0cbd30789eb44ca097e3541717a7b8fa36b1c464ee7edebd" +dependencies = [ + "vcpkg", +] + [[package]] name = "predicates" version = "1.0.8" @@ -1493,9 +1747,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.43" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab" +checksum = "fa1fb82fc0c281dd9671101b66b771ebbe1eaf967b96ac8740dcba4b70005ca8" dependencies = [ "unicode-ident", ] @@ -1523,8 +1777,8 @@ checksum = "f8f30cdb09c39930b8fa5e0f23cbb895ab3f766b187403a0ba0956fc1ef4f0e5" dependencies = [ "lazy_static", "proc-macro2", - "quote 1.0.21", - "syn 1.0.99", + "quote 1.0.27", + "syn 1.0.109", ] [[package]] @@ -1561,9 +1815,9 @@ checksum = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" [[package]] name = "quote" -version = "1.0.21" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500" dependencies = [ "proc-macro2", ] @@ -1597,7 +1851,7 @@ checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" dependencies = [ "libc", "rand_chacha", - "rand_core", + "rand_core 0.6.3", "rand_hc", ] @@ -1608,9 +1862,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.3", ] +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" + [[package]] name = "rand_core" version = "0.6.3" @@ -1626,7 +1886,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" dependencies = [ - "rand_core", + "rand_core 0.6.3", ] [[package]] @@ -1665,13 +1925,13 @@ dependencies = [ [[package]] name = "regex" -version = "1.6.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-syntax 0.7.1", ] [[package]] @@ -1680,7 +1940,7 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" dependencies = [ - "regex-syntax", + "regex-syntax 0.6.27", ] [[package]] @@ -1689,6 +1949,12 @@ version = "0.6.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" +[[package]] +name = "regex-syntax" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" + [[package]] name = "remove_dir_all" version = "0.5.3" @@ -1763,7 +2029,7 @@ dependencies = [ "thiserror", "tokio", "tokio-rustls", - "webpki", + "webpki 0.21.4", ] [[package]] @@ -1801,8 +2067,51 @@ dependencies = [ "base64 0.13.0", "log", "ring", - "sct", - "webpki", + "sct 0.6.1", + "webpki 0.21.4", +] + +[[package]] +name = "rustls" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c911ba11bc8433e811ce56fde130ccf32f5127cab0e0194e9c68c5a5b671791e" +dependencies = [ + "log", + "ring", + "rustls-webpki", + "sct 0.7.0", +] + +[[package]] +name = "rustls-native-certs" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0167bac7a9f490495f3c33013e7722b53cb087ecbe082fb0c6387c96f634ea50" +dependencies = [ + "openssl-probe", + "rustls-pemfile", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" +dependencies = [ + "base64 0.21.0", +] + +[[package]] +name = "rustls-webpki" +version = "0.100.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6207cd5ed3d8dca7816f8f3725513a34609c0c765bf652b8c3cb4cfd87db46b" +dependencies = [ + "ring", + "untrusted", ] [[package]] @@ -1846,6 +2155,16 @@ dependencies = [ "untrusted", ] +[[package]] +name = "sct" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "security-framework" version = "2.3.1" @@ -1881,7 +2200,7 @@ version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "546b9b6f76c26c60ffbcf0b7136e15169fe13d43949b4aadb7c1edc1c3f3a26f" dependencies = [ - "sentry-anyhow", + "sentry-anyhow 0.23.0", "sentry-backtrace 0.23.0", "sentry-core 0.23.0", "tokio", @@ -1903,8 +2222,22 @@ dependencies = [ ] [[package]] -name = "sentry-anyhow" -version = "0.23.0" +name = "sentry" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c3d7f8bf7373e75222452fcdd9347d857452a92d0eec738f941bc4656c5b5df" +dependencies = [ + "httpdate", + "reqwest", + "sentry-anyhow 0.31.0", + "sentry-backtrace 0.31.0", + "sentry-core 0.31.0", + "tokio", +] + +[[package]] +name = "sentry-anyhow" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "338ef04f73ca2fb1130ebab3853dca36041aa219a442ae873627373887660c36" dependencies = [ @@ -1913,6 +2246,17 @@ dependencies = [ "sentry-core 0.23.0", ] +[[package]] +name = "sentry-anyhow" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ef7f47c57a1146d553b4976f20e8bba370195a88858bdf6945a63c529549236" +dependencies = [ + "anyhow", + "sentry-backtrace 0.31.0", + "sentry-core 0.31.0", +] + [[package]] name = "sentry-backtrace" version = "0.23.0" @@ -1937,6 +2281,18 @@ dependencies = [ "sentry-core 0.27.0", ] +[[package]] +name = "sentry-backtrace" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03b7cdefbdca51f1146f0f24a3cb4ecb6428951f030ff5c720cfb5c60bd174c0" +dependencies = [ + "backtrace", + "once_cell", + "regex", + "sentry-core 0.31.0", +] + [[package]] name = "sentry-contexts" version = "0.27.0" @@ -1977,6 +2333,19 @@ dependencies = [ "serde_json", ] +[[package]] +name = "sentry-core" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e781b55761e47a60d1ff326ae8059de22b0e6b0cee68eab1c5912e4fb199a76" +dependencies = [ + "once_cell", + "rand", + "sentry-types 0.31.0", + "serde", + "serde_json", +] + [[package]] name = "sentry-panic" version = "0.27.0" @@ -2014,42 +2383,68 @@ dependencies = [ "serde", "serde_json", "thiserror", - "time 0.3.7", + "time 0.3.21", + "url 2.2.2", + "uuid 1.3.3", +] + +[[package]] +name = "sentry-types" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d642a04657cc77d8de52ae7c6d93a15cb02284eb219344a89c1e2b26bbaf578c" +dependencies = [ + "debugid 0.8.0", + "getrandom", + "hex", + "serde", + "serde_json", + "thiserror", + "time 0.3.21", "url 2.2.2", - "uuid 1.1.2", + "uuid 1.3.3", ] [[package]] name = "serde" -version = "1.0.144" +version = "1.0.163" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860" +checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.144" +version = "1.0.163" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94ed3a816fb1d101812f83e789f888322c34e291f894f19590dc310963e87a00" +checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" dependencies = [ "proc-macro2", - "quote 1.0.21", - "syn 1.0.99", + "quote 1.0.27", + "syn 2.0.16", ] [[package]] name = "serde_json" -version = "1.0.85" +version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44" +checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" dependencies = [ "itoa 1.0.1", "ryu", "serde", ] +[[package]] +name = "serde_nanos" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae801b7733ca8d6a2b580debe99f67f36826a0f5b8a36055dc6bc40f8d6bc71" +dependencies = [ + "serde", +] + [[package]] name = "serde_qs" version = "0.10.1" @@ -2061,6 +2456,17 @@ dependencies = [ "thiserror", ] +[[package]] +name = "serde_repr" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcec881020c684085e55a25f7fd888954d56609ef363479dc5a1305eb0d40cab" +dependencies = [ + "proc-macro2", + "quote 1.0.27", + "syn 2.0.16", +] + [[package]] name = "serde_urlencoded" version = "0.7.0" @@ -2081,7 +2487,7 @@ checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" dependencies = [ "cfg-if", "cpufeatures", - "digest", + "digest 0.10.3", ] [[package]] @@ -2090,6 +2496,19 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + [[package]] name = "sha2" version = "0.10.5" @@ -2098,7 +2517,7 @@ checksum = "cf9db03534dff993187064c4e0c05a5708d2a9728ace9a8959b77bedf415dac5" dependencies = [ "cfg-if", "cpufeatures", - "digest", + "digest 0.10.3", ] [[package]] @@ -2141,6 +2560,24 @@ dependencies = [ "tokio", ] +[[package]] +name = "signatory" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfecc059e81632eef1dd9b79e22fc28b8fe69b30d3357512a77a0ad8ee3c782" +dependencies = [ + "pkcs8", + "rand_core 0.6.3", + "signature", + "zeroize", +] + +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" + [[package]] name = "simple_asn1" version = "0.4.1" @@ -2172,9 +2609,9 @@ checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" [[package]] name = "socket2" -version = "0.4.4" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" dependencies = [ "libc", "winapi", @@ -2186,6 +2623,15 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +[[package]] +name = "spki" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c01a0c15da1b0b0e1494112e7af814a678fec9bd157881b49beac661e9b6f32" +dependencies = [ + "der", +] + [[package]] name = "sqlformat" version = "0.1.8" @@ -2197,14 +2643,35 @@ dependencies = [ "unicode_categories", ] +[[package]] +name = "sqlformat" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c12bc9199d1db8234678b7051747c07f517cdcf019262d1847b94ec8b1aee3e" +dependencies = [ + "itertools", + "nom", + "unicode_categories", +] + [[package]] name = "sqlx" version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7911b0031a0247af40095838002999c7a52fba29d9739e93326e71a5a1bc9d43" dependencies = [ - "sqlx-core", - "sqlx-macros", + "sqlx-core 0.5.13", + "sqlx-macros 0.5.13", +] + +[[package]] +name = "sqlx" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8de3b03a925878ed54a954f621e64bf55a3c1bd29652d0d1a17830405350188" +dependencies = [ + "sqlx-core 0.6.3", + "sqlx-macros 0.6.3", ] [[package]] @@ -2213,15 +2680,15 @@ version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e48c61941ccf5ddcada342cd59e3e5173b007c509e1e8e990dafc830294d9dc5" dependencies = [ - "ahash", - "atoi", + "ahash 0.7.6", + "atoi 0.4.0", "base64 0.13.0", "bigdecimal", "bitflags", "byteorder", "bytes", "chrono", - "crc", + "crc 2.1.0", "crossbeam-queue", "dirs", "either", @@ -2230,7 +2697,7 @@ dependencies = [ "futures-core", "futures-intrusive", "futures-util", - "hashlink", + "hashlink 0.7.0", "hex", "hkdf", "hmac", @@ -2248,10 +2715,10 @@ dependencies = [ "serde", "serde_json", "sha-1", - "sha2", + "sha2 0.10.5", "smallvec", - "sqlformat", - "sqlx-rt", + "sqlformat 0.1.8", + "sqlx-rt 0.5.13", "stringprep", "thiserror", "tokio-stream", @@ -2260,6 +2727,46 @@ dependencies = [ "whoami", ] +[[package]] +name = "sqlx-core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa8241483a83a3f33aa5fff7e7d9def398ff9990b2752b6c6112b83c6d246029" +dependencies = [ + "ahash 0.7.6", + "atoi 1.0.0", + "bitflags", + "byteorder", + "bytes", + "crc 3.0.1", + "crossbeam-queue", + "dotenvy", + "either", + "event-listener", + "futures-channel", + "futures-core", + "futures-intrusive", + "futures-util", + "hashlink 0.8.2", + "hex", + "indexmap", + "itoa 1.0.1", + "libc", + "log", + "memchr", + "once_cell", + "paste", + "percent-encoding 2.1.0", + "sha2 0.10.5", + "smallvec", + "sqlformat 0.2.1", + "sqlx-rt 0.6.3", + "stringprep", + "thiserror", + "tokio-stream", + "url 2.2.2", +] + [[package]] name = "sqlx-macros" version = "0.5.13" @@ -2272,13 +2779,32 @@ dependencies = [ "hex", "once_cell", "proc-macro2", - "quote 1.0.21", + "quote 1.0.27", "serde", "serde_json", - "sha2", - "sqlx-core", - "sqlx-rt", - "syn 1.0.99", + "sha2 0.10.5", + "sqlx-core 0.5.13", + "sqlx-rt 0.5.13", + "syn 1.0.109", + "url 2.2.2", +] + +[[package]] +name = "sqlx-macros" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9966e64ae989e7e575b19d7265cb79d7fc3cbbdf179835cb0d716f294c2049c9" +dependencies = [ + "dotenvy", + "either", + "heck", + "once_cell", + "proc-macro2", + "quote 1.0.27", + "sha2 0.10.5", + "sqlx-core 0.6.3", + "sqlx-rt 0.6.3", + "syn 1.0.109", "url 2.2.2", ] @@ -2294,6 +2820,18 @@ dependencies = [ "tokio-native-tls", ] +[[package]] +name = "sqlx-rt" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "804d3f245f894e61b1e6263c84b23ca675d96753b5abfd5cc8597d86806e8024" +dependencies = [ + "native-tls", + "once_cell", + "tokio", + "tokio-native-tls", +] + [[package]] name = "stringprep" version = "0.1.2" @@ -2324,12 +2862,31 @@ dependencies = [ "rumqttc", "serde", "serde_json", - "sqlx", - "svc-authn", + "sqlx 0.5.9", + "svc-authn 0.7.0", "tokio", "uuid 0.8.2", ] +[[package]] +name = "svc-agent" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c90205083c87c6bc25990d5cdd26f98d8c431441e86dbd4e7421b28c428e04d" +dependencies = [ + "async-channel", + "base64 0.21.0", + "chrono", + "http", + "log", + "rumqttc", + "serde", + "serde_json", + "svc-authn 0.8.0", + "tokio", + "uuid 1.3.3", +] + [[package]] name = "svc-authn" version = "0.7.0" @@ -2341,7 +2898,19 @@ dependencies = [ "jsonwebtoken", "serde", "serde_derive", - "sqlx", + "sqlx 0.5.9", +] + +[[package]] +name = "svc-authn" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb5cf659f78c8fff863c17ac4e674829517919716eeecab602e8d2941e89c111" +dependencies = [ + "diesel", + "serde", + "serde_derive", + "sqlx 0.6.3", ] [[package]] @@ -2361,7 +2930,7 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "svc-authn", + "svc-authn 0.7.0", "tokio", ] @@ -2389,15 +2958,51 @@ dependencies = [ "http", "once_cell", "sentry 0.23.0", - "sentry-anyhow", + "sentry-anyhow 0.23.0", "serde", "serde_derive", - "sqlx", - "svc-agent", - "svc-authn", + "sqlx 0.5.9", + "svc-agent 0.19.4", + "svc-authn 0.7.0", "svc-authz", ] +[[package]] +name = "svc-error" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f841d7fd45d6f179e9f3765491fcb5eea100a5bbe50ea47faf3f262031966d9" +dependencies = [ + "anyhow", + "crossbeam-channel", + "http", + "once_cell", + "sentry 0.31.0", + "sentry-anyhow 0.31.0", + "serde", + "serde_derive", +] + +[[package]] +name = "svc-nats-client" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab57232e87e0c5c4d1ae2b810ffc971cdefd2cdf011d7c2a678cdc40a1a20c51" +dependencies = [ + "anyhow", + "async-nats", + "async-trait", + "futures", + "humantime-serde", + "reqwest", + "serde", + "svc-agent 0.20.0", + "svc-error 0.5.0", + "thiserror", + "tracing", + "uuid 1.3.3", +] + [[package]] name = "svc-utils" version = "0.6.0" @@ -2410,8 +3015,8 @@ dependencies = [ "hyper", "once_cell", "prometheus", - "svc-agent", - "svc-authn", + "svc-agent 0.19.4", + "svc-authn 0.7.0", "svc-error 0.3.0", "tokio", "tower", @@ -2432,12 +3037,23 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.99" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote 1.0.27", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13" +checksum = "a6f671d4b5ffdb8eadec19c0ae67fe2639df8684bd7bc4b83d986b8db549cf01" dependencies = [ "proc-macro2", - "quote 1.0.21", + "quote 1.0.27", "unicode-ident", ] @@ -2486,8 +3102,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8f2591983642de85c921015f3f070c665a197ed69e417af436115e3a1407487" dependencies = [ "proc-macro2", - "quote 1.0.21", - "syn 1.0.99", + "quote 1.0.27", + "syn 1.0.109", ] [[package]] @@ -2511,13 +3127,29 @@ dependencies = [ [[package]] name = "time" -version = "0.3.7" +version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "004cbc98f30fa233c61a38bc77e96a9106e65c88f2d3bef182ae952027e5753d" +checksum = "8f3403384eaacbca9923fa06940178ac13e4edb725486d70e8e15881d0c836cc" dependencies = [ "itoa 1.0.1", - "libc", - "num_threads", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" + +[[package]] +name = "time-macros" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "372950940a5f07bf38dbe211d7283c9e6d7327df53794992d293e534c733d09b" +dependencies = [ + "time-core", ] [[package]] @@ -2537,33 +3169,32 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.17.0" +version = "1.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2af73ac49756f3f7c01172e34a23e5d0216f6c32333757c2c61feb2bbff5a5ee" +checksum = "0aa32867d44e6f2ce3385e89dceb990188b8bb0fb25b0cf576647a6f98ac5105" dependencies = [ + "autocfg", "bytes", "libc", - "memchr", "mio", "num_cpus", - "once_cell", "parking_lot 0.12.0", "pin-project-lite", "signal-hook-registry", "socket2", "tokio-macros", - "winapi", + "windows-sys 0.48.0", ] [[package]] name = "tokio-macros" -version = "1.7.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", - "quote 1.0.21", - "syn 1.0.99", + "quote 1.0.27", + "syn 2.0.16", ] [[package]] @@ -2576,15 +3207,26 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-retry" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f57eb36ecbe0fc510036adff84824dd3c24bb781e21bfa67b69d556aa85214f" +dependencies = [ + "pin-project", + "rand", + "tokio", +] + [[package]] name = "tokio-rustls" version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6" dependencies = [ - "rustls", + "rustls 0.19.1", "tokio", - "webpki", + "webpki 0.21.4", ] [[package]] @@ -2707,7 +3349,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09d48f71a791638519505cefafe162606f706c25592e4bde4d97600c0195312e" dependencies = [ "crossbeam-channel", - "time 0.3.7", + "time 0.3.21", "tracing-subscriber", ] @@ -2718,8 +3360,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4f480b8f81512e825f337ad51e94c1eb5d3bbdf2b363dcd01e2b19a9ffe3f8e" dependencies = [ "proc-macro2", - "quote 1.0.21", - "syn 1.0.99", + "quote 1.0.27", + "syn 1.0.109", ] [[package]] @@ -2904,9 +3546,9 @@ dependencies = [ [[package]] name = "uuid" -version = "1.1.2" +version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd6469f4314d5f1ffec476e05f17cc9a78bc7a27a6a857842170bdf8d6f98d2f" +checksum = "345444e32442451b267fc254ae85a209c64be56d2890e601a0c37ff0c3c5ecd2" dependencies = [ "getrandom", "serde", @@ -2929,9 +3571,9 @@ dependencies = [ [[package]] name = "version_check" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "want" @@ -2977,8 +3619,8 @@ dependencies = [ "lazy_static", "log", "proc-macro2", - "quote 1.0.21", - "syn 1.0.99", + "quote 1.0.27", + "syn 1.0.109", "wasm-bindgen-shared", ] @@ -3000,7 +3642,7 @@ version = "0.2.74" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "088169ca61430fe1e58b8096c24975251700e7b1f6fd91cc9d59b04fb9b18bd4" dependencies = [ - "quote 1.0.21", + "quote 1.0.27", "wasm-bindgen-macro-support", ] @@ -3011,8 +3653,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be2241542ff3d9f241f5e2cb6dd09b37efe786df8851c54957683a49f0987a97" dependencies = [ "proc-macro2", - "quote 1.0.21", - "syn 1.0.99", + "quote 1.0.27", + "syn 1.0.109", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3043,6 +3685,16 @@ dependencies = [ "untrusted", ] +[[package]] +name = "webpki" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "whoami" version = "1.2.3" @@ -3082,43 +3734,175 @@ version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3df6e476185f92a12c072be4a189a0210dcdcf512a1891d6dff9edb874deadc6" dependencies = [ - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_msvc", + "windows_aarch64_msvc 0.32.0", + "windows_i686_gnu 0.32.0", + "windows_i686_msvc 0.32.0", + "windows_x86_64_gnu 0.32.0", + "windows_x86_64_msvc 0.32.0", ] +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.0", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + [[package]] name = "windows_aarch64_msvc" version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8e92753b1c443191654ec532f14c199742964a061be25d77d7a96f09db20bf5" +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + [[package]] name = "windows_i686_gnu" version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a711c68811799e017b6038e0922cb27a5e2f43a2ddb609fe0b6f3eeda9de615" +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + [[package]] name = "windows_i686_msvc" version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "146c11bb1a02615db74680b32a68e2d61f553cc24c4eb5b4ca10311740e44172" +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + [[package]] name = "windows_x86_64_gnu" version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c912b12f7454c6620635bbff3450962753834be2a594819bd5e945af18ec64bc" +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + [[package]] name = "windows_x86_64_msvc" version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "504a2476202769977a040c6364301a3f65d0cc9e3fb08600b2bda150a0488316" +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + [[package]] name = "winreg" version = "0.7.0" @@ -3127,3 +3911,23 @@ checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" dependencies = [ "winapi", ] + +[[package]] +name = "zeroize" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote 1.0.27", + "syn 2.0.16", +] diff --git a/Cargo.toml b/Cargo.toml index f6d9252b..55faa760 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -68,6 +68,7 @@ tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] } url = { version = "2.2.1", features = ["serde"] } uuid = { version = "0.8", features = ["v4", "serde"] } vec1 = { version = "1.8.0", features = ["serde"] } +svc-nats-client = "0.2.0" [dev-dependencies] lazy_static = "1.4" diff --git a/rust-toolchain.toml b/rust-toolchain.toml index cc8f987e..f3322029 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -channel = "1.64.0" +channel = "1.69.0" components = ["rustfmt", "clippy"] diff --git a/src/app/mod.rs b/src/app/mod.rs index df7b98cd..00b1ddf8 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -67,6 +67,19 @@ pub async fn run(db: PgPool, authz_cache: Option>) -> Result agent.clone(), authz, ); + + let state = match &config.nats { + Some(cfg) => { + let nats_client = svc_nats_client::Client::new(cfg.clone()) + .await + .context("nats client")?; + info!("Connected to nats"); + + state.add_nats_client(nats_client) + } + None => state, + }; + let state = Arc::new(state) as Arc; let state_ = state.clone(); diff --git a/src/app/tide_state/mod.rs b/src/app/tide_state/mod.rs index 51ff9bfb..6b590789 100644 --- a/src/app/tide_state/mod.rs +++ b/src/app/tide_state/mod.rs @@ -8,6 +8,7 @@ use svc_agent::error::Error as AgentError; use svc_agent::mqtt::{Agent, IntoPublishableMessage}; use svc_agent::AgentId; use svc_authz::ClientMap as Authz; +use svc_nats_client::NatsClient; use url::Url; use crate::clients::conference::ConferenceClient; @@ -33,6 +34,7 @@ pub trait AppContext: Sync + Send { fn config(&self) -> &Config; fn agent(&self) -> Option<&Agent>; fn turn_host_selector(&self) -> &TurnHostSelector; + fn nats_client(&self) -> Option<&dyn NatsClient>; fn get_preroll_offset(&self, audience: &str) -> i64 { self.config() @@ -64,6 +66,7 @@ pub struct TideState { tq_client: Arc, authz: Authz, turn_host_selector: TurnHostSelector, + nats_client: Option>, } impl TideState { @@ -87,6 +90,14 @@ impl TideState { tq_client, authz, turn_host_selector, + nats_client: None, + } + } + + pub fn add_nats_client(self, nats_client: impl NatsClient + 'static) -> Self { + Self { + nats_client: Some(Arc::new(nats_client)), + ..self } } } @@ -156,6 +167,10 @@ impl AppContext for TideState { fn turn_host_selector(&self) -> &TurnHostSelector { &self.turn_host_selector } + + fn nats_client(&self) -> Option<&dyn NatsClient> { + self.nats_client.as_deref() + } } pub mod message_handler; diff --git a/src/config.rs b/src/config.rs index a11e2b8d..c1c7ed85 100644 --- a/src/config.rs +++ b/src/config.rs @@ -32,6 +32,7 @@ pub struct Config { pub turn_hosts: vec1::Vec1, pub short_namespace: Option, pub frontend: HashMap, + pub nats: Option, } #[derive(Clone, Debug, Deserialize)] diff --git a/src/test_helpers/state.rs b/src/test_helpers/state.rs index 84db1aac..1814c667 100644 --- a/src/test_helpers/state.rs +++ b/src/test_helpers/state.rs @@ -11,6 +11,9 @@ use svc_agent::{ AgentId, }; use svc_authz::ClientMap as Authz; +use svc_nats_client::{ + Event, Message, MessageStream, NatsClient, PublishError, SubscribeError, TermMessageError, +}; use url::Url; use vec1::{vec1, Vec1}; @@ -40,6 +43,7 @@ pub struct TestState { tq_client: Arc, authz: Authz, turn_host_selector: TurnHostSelector, + nats_client: Option>, } fn build_config() -> Config { @@ -98,6 +102,23 @@ fn build_config() -> Config { serde_json::from_value::(config).expect("Failed to parse test config") } +struct TestNatsClient; + +#[async_trait] +impl NatsClient for TestNatsClient { + async fn publish(&self, _event: &Event) -> Result<(), PublishError> { + Ok(()) + } + + async fn subscribe(&self) -> Result { + unimplemented!() + } + + async fn terminate(&self, _message: Message) -> Result<(), TermMessageError> { + unimplemented!() + } +} + impl TestState { pub async fn new(authz: TestAuthz) -> Self { let config = build_config(); @@ -116,6 +137,7 @@ impl TestState { event_client: Arc::new(MockEventClient::new()), tq_client: Arc::new(MockTqClient::new()), authz: authz.into(), + nats_client: Some(Arc::new(TestNatsClient {}) as Arc), } } @@ -136,6 +158,7 @@ impl TestState { tq_client: Arc::new(MockTqClient::new()), authz: authz.into(), turn_host_selector: TurnHostSelector::new(&vec1!["turn.example.org".into()]), + nats_client: Some(Arc::new(TestNatsClient {}) as Arc), } } @@ -234,6 +257,10 @@ impl AppContext for TestState { fn turn_host_selector(&self) -> &TurnHostSelector { &self.turn_host_selector } + + fn nats_client(&self) -> Option<&dyn NatsClient> { + self.nats_client.as_deref() + } } //////////////////////////////////////////////////////////////////////////////// From dcea3060476996f4583d6eebf8e616cfe3a423bc Mon Sep 17 00:00:00 2001 From: Dmitry Shlagoff Date: Fri, 19 May 2023 15:42:44 +0200 Subject: [PATCH 03/35] Impl stage ban_intent --- Cargo.lock | 104 +++--------------- Cargo.toml | 3 +- .../20230519094604_add_ban_sequence.sql | 1 + sqlx-data.json | 18 +++ src/app/api/v1/account/ban.rs | 5 +- src/app/error.rs | 14 +++ src/app/mod.rs | 3 +- src/app/stage/ban_intent.rs | 42 +++++++ src/app/stage/mod.rs | 1 + src/clients/mod.rs | 1 + src/clients/nats.rs | 41 +++++++ src/db/ban_account_op.rs | 13 +++ 12 files changed, 154 insertions(+), 92 deletions(-) create mode 100644 migrations/20230519094604_add_ban_sequence.sql create mode 100644 src/app/stage/ban_intent.rs create mode 100644 src/app/stage/mod.rs create mode 100644 src/clients/nats.rs diff --git a/Cargo.lock b/Cargo.lock index 1cc13a58..f5838670 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -642,6 +642,7 @@ dependencies = [ "svc-authn 0.7.0", "svc-authz", "svc-error 0.4.0", + "svc-events", "svc-nats-client", "svc-utils", "tokio", @@ -2200,7 +2201,7 @@ version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "546b9b6f76c26c60ffbcf0b7136e15169fe13d43949b4aadb7c1edc1c3f3a26f" dependencies = [ - "sentry-anyhow 0.23.0", + "sentry-anyhow", "sentry-backtrace 0.23.0", "sentry-core 0.23.0", "tokio", @@ -2221,20 +2222,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "sentry" -version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c3d7f8bf7373e75222452fcdd9347d857452a92d0eec738f941bc4656c5b5df" -dependencies = [ - "httpdate", - "reqwest", - "sentry-anyhow 0.31.0", - "sentry-backtrace 0.31.0", - "sentry-core 0.31.0", - "tokio", -] - [[package]] name = "sentry-anyhow" version = "0.23.0" @@ -2246,17 +2233,6 @@ dependencies = [ "sentry-core 0.23.0", ] -[[package]] -name = "sentry-anyhow" -version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ef7f47c57a1146d553b4976f20e8bba370195a88858bdf6945a63c529549236" -dependencies = [ - "anyhow", - "sentry-backtrace 0.31.0", - "sentry-core 0.31.0", -] - [[package]] name = "sentry-backtrace" version = "0.23.0" @@ -2281,18 +2257,6 @@ dependencies = [ "sentry-core 0.27.0", ] -[[package]] -name = "sentry-backtrace" -version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03b7cdefbdca51f1146f0f24a3cb4ecb6428951f030ff5c720cfb5c60bd174c0" -dependencies = [ - "backtrace", - "once_cell", - "regex", - "sentry-core 0.31.0", -] - [[package]] name = "sentry-contexts" version = "0.27.0" @@ -2333,19 +2297,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "sentry-core" -version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e781b55761e47a60d1ff326ae8059de22b0e6b0cee68eab1c5912e4fb199a76" -dependencies = [ - "once_cell", - "rand", - "sentry-types 0.31.0", - "serde", - "serde_json", -] - [[package]] name = "sentry-panic" version = "0.27.0" @@ -2388,23 +2339,6 @@ dependencies = [ "uuid 1.3.3", ] -[[package]] -name = "sentry-types" -version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d642a04657cc77d8de52ae7c6d93a15cb02284eb219344a89c1e2b26bbaf578c" -dependencies = [ - "debugid 0.8.0", - "getrandom", - "hex", - "serde", - "serde_json", - "thiserror", - "time 0.3.21", - "url 2.2.2", - "uuid 1.3.3", -] - [[package]] name = "serde" version = "1.0.163" @@ -2958,36 +2892,32 @@ dependencies = [ "http", "once_cell", "sentry 0.23.0", - "sentry-anyhow 0.23.0", + "sentry-anyhow", "serde", "serde_derive", "sqlx 0.5.9", - "svc-agent 0.19.4", - "svc-authn 0.7.0", + "svc-agent 0.20.0", + "svc-authn 0.8.0", "svc-authz", ] [[package]] -name = "svc-error" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f841d7fd45d6f179e9f3765491fcb5eea100a5bbe50ea47faf3f262031966d9" +name = "svc-events" +version = "0.3.0" +source = "git+https://github.com/foxford/svc-conference-events?branch=ULMS-1896/ban-events#8d3e390b0b98d6e07e90f96764e2f6a25374dd2e" dependencies = [ - "anyhow", - "crossbeam-channel", - "http", - "once_cell", - "sentry 0.31.0", - "sentry-anyhow 0.31.0", "serde", - "serde_derive", + "serde_json", + "svc-authn 0.7.0", + "thiserror", + "uuid 0.8.2", ] [[package]] name = "svc-nats-client" -version = "0.2.0" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab57232e87e0c5c4d1ae2b810ffc971cdefd2cdf011d7c2a678cdc40a1a20c51" +checksum = "7deb7c3dff073750c31aed6519c2a272ad35cc69d00425030b9177df711d6942" dependencies = [ "anyhow", "async-nats", @@ -2996,11 +2926,11 @@ dependencies = [ "humantime-serde", "reqwest", "serde", - "svc-agent 0.20.0", - "svc-error 0.5.0", + "svc-agent 0.19.4", + "svc-error 0.4.0", "thiserror", "tracing", - "uuid 1.3.3", + "uuid 0.8.2", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 55faa760..49406de8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,6 +55,8 @@ svc-error = { version = "0.4", features = [ "sentry-extension", "sqlx", ] } +svc-events = { git = "https://github.com/foxford/svc-conference-events", branch = "ULMS-1896/ban-events" } +svc-nats-client = "0.1" svc-utils = { version = "0.6.0", features = ["log-middleware", "metrics-middleware", "cors-middleware", "authn-extractor"] } tokio = { version = "1.17", features = ["full"] } tower = { version = "0.4", features = [ "timeout" ] } @@ -68,7 +70,6 @@ tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] } url = { version = "2.2.1", features = ["serde"] } uuid = { version = "0.8", features = ["v4", "serde"] } vec1 = { version = "1.8.0", features = ["serde"] } -svc-nats-client = "0.2.0" [dev-dependencies] lazy_static = "1.4" diff --git a/migrations/20230519094604_add_ban_sequence.sql b/migrations/20230519094604_add_ban_sequence.sql new file mode 100644 index 00000000..dbb3ae37 --- /dev/null +++ b/migrations/20230519094604_add_ban_sequence.sql @@ -0,0 +1 @@ +CREATE SEQUENCE IF NOT EXISTS ban_entity_seq_id; diff --git a/sqlx-data.json b/sqlx-data.json index f6091d8d..6caf4d5c 100644 --- a/sqlx-data.json +++ b/sqlx-data.json @@ -1574,6 +1574,24 @@ }, "query": "\n SELECT\n user_account AS \"user_account: _\",\n last_op_id AS \"last_op_id: _\",\n last_op_done\n FROM ban_account_op\n WHERE\n user_account = $1\n LIMIT 1;\n " }, + "a4fc28b7fa8a6a5f4051750a2447784a10a221632dbea080fe1d4c980de076fd": { + "describe": { + "columns": [ + { + "name": "value!: i64", + "ordinal": 0, + "type_info": "Int8" + } + ], + "nullable": [ + null + ], + "parameters": { + "Left": [] + } + }, + "query": "SELECT nextval('ban_entity_seq_id') as \"value!: i64\";" + }, "ac4ac9431173165543dbc459bb3b2b9b4461f7016aa6d6967cc9f5f832f208bb": { "describe": { "columns": [ diff --git a/src/app/api/v1/account/ban.rs b/src/app/api/v1/account/ban.rs index 09829137..bdfc790f 100644 --- a/src/app/api/v1/account/ban.rs +++ b/src/app/api/v1/account/ban.rs @@ -13,7 +13,7 @@ use crate::{ api::v1::find_class, error::{ErrorExt, ErrorKind as AppErrorKind}, metrics::AuthorizeMetrics, - AppContext, AuthzObject, + stage, AppContext, AuthzObject, }, db::ban_account_op, }; @@ -68,8 +68,7 @@ pub async fn ban( } } - // schedule task to ourselves - // update ban account op + stage::ban_intent::start(ctx.as_ref(), &mut conn, payload.ban, &class, account_to_ban).await?; Ok(Response::builder().status(200).body(Body::empty()).unwrap()) } diff --git a/src/app/error.rs b/src/app/error.rs index 746abc93..fae51a7f 100644 --- a/src/app/error.rs +++ b/src/app/error.rs @@ -42,6 +42,8 @@ pub enum ErrorKind { ClassAlreadyEstablished, OperationIdObsolete, OperationInProgress, + NatsPublishFailed, + NatsClientNotFound, } impl ErrorKind { @@ -205,6 +207,18 @@ impl From for ErrorKindProperties { title: "Operation is not completed yet, retry later", is_notify_sentry: false, }, + ErrorKind::NatsPublishFailed => ErrorKindProperties { + status: ResponseStatus::UNPROCESSABLE_ENTITY, + kind: "nats_publish_failed", + title: "Nats publish failed", + is_notify_sentry: true, + }, + ErrorKind::NatsClientNotFound => ErrorKindProperties { + status: ResponseStatus::FAILED_DEPENDENCY, + kind: "nats_client_not_found", + title: "Nats client not found", + is_notify_sentry: true, + }, } } } diff --git a/src/app/mod.rs b/src/app/mod.rs index 00b1ddf8..3cc28a20 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -253,7 +253,7 @@ fn build_tq_client(config: &Config, token: &str) -> Arc { mod api; mod authz; -mod error; +pub mod error; mod http; mod info; mod metrics; @@ -261,3 +261,4 @@ mod postprocessing_strategy; pub mod services; mod tide_state; pub mod turn_host; +mod stage; diff --git a/src/app/stage/ban_intent.rs b/src/app/stage/ban_intent.rs new file mode 100644 index 00000000..c33307c6 --- /dev/null +++ b/src/app/stage/ban_intent.rs @@ -0,0 +1,42 @@ +use sqlx::PgConnection; +use svc_authn::AccountId; +use svc_nats_client::EventId; +use uuid::Uuid; + +use crate::{ + app::{ + error::{Error, ErrorExt, ErrorKind as AppErrorKind}, + AppContext, + }, + clients::nats, + db::{self, ban_account_op}, +}; + +const ENTITY_TYPE: &str = "ban-intent"; + +pub async fn start( + ctx: &dyn AppContext, + conn: &mut PgConnection, + ban: bool, + class: &db::class::Object, + user_account: AccountId, +) -> Result<(), Error> { + let event_id = get_next_event_id(conn).await?; + let event = svc_events::ban::BanIntentEventV1 { + ban, + classroom_id: class.id(), + user_account, + op_id: Uuid::new_v4(), + }; + nats::publish_event(ctx, class.id(), &event_id, event.into()).await +} + +async fn get_next_event_id(conn: &mut PgConnection) -> Result { + let next_seq_id = ban_account_op::get_next_seq_id(conn) + .await + .error(AppErrorKind::DbQueryFailed)?; + + let event_id = (ENTITY_TYPE.to_owned(), next_seq_id.value).into(); + + Ok(event_id) +} diff --git a/src/app/stage/mod.rs b/src/app/stage/mod.rs new file mode 100644 index 00000000..aab0c761 --- /dev/null +++ b/src/app/stage/mod.rs @@ -0,0 +1 @@ +pub mod ban_intent; diff --git a/src/clients/mod.rs b/src/clients/mod.rs index b0058a01..59894cae 100644 --- a/src/clients/mod.rs +++ b/src/clients/mod.rs @@ -53,4 +53,5 @@ fn generate_correlation_data() -> String { pub mod conference; pub mod event; +pub mod nats; pub mod tq; diff --git a/src/clients/nats.rs b/src/clients/nats.rs new file mode 100644 index 00000000..11d297a1 --- /dev/null +++ b/src/clients/nats.rs @@ -0,0 +1,41 @@ +use uuid::Uuid; + +use svc_events::Event; +use svc_nats_client::EventId; + +use crate::app::{ + error::{Error, ErrorExt, ErrorKind as AppErrorKind}, + AppContext, +}; + +const SUBJECT_PREFIX: &str = "classroom"; + +pub async fn publish_event( + ctx: &dyn AppContext, + classroom_id: Uuid, + id: &EventId, + event: Event, +) -> Result<(), Error> { + let subject = svc_nats_client::Subject::new( + SUBJECT_PREFIX.to_string(), + classroom_id, + id.entity_type().to_string(), + ); + + let payload = serde_json::to_vec(&event).error(AppErrorKind::InternalFailure)?; + + let event = svc_nats_client::event::Builder::new( + subject, + payload, + id.to_owned(), + ctx.agent_id().to_owned(), + ) + .build(); + + ctx.nats_client() + .ok_or_else(|| anyhow!("nats client not found")) + .error(AppErrorKind::NatsClientNotFound)? + .publish(&event) + .await + .error(AppErrorKind::NatsPublishFailed) +} diff --git a/src/db/ban_account_op.rs b/src/db/ban_account_op.rs index b9dc8556..f3bc4c92 100644 --- a/src/db/ban_account_op.rs +++ b/src/db/ban_account_op.rs @@ -36,3 +36,16 @@ impl<'a> ReadQuery<'a> { .await } } + +pub struct NextSeqId { + pub value: i64, +} + +pub async fn get_next_seq_id(conn: &mut PgConnection) -> sqlx::Result { + sqlx::query_as!( + NextSeqId, + r#"SELECT nextval('ban_entity_seq_id') as "value!: i64";"# + ) + .fetch_one(conn) + .await +} From d2b98080c0e5f860cc7e8d95454c355a982d6fbe Mon Sep 17 00:00:00 2001 From: Dmitry Shlagoff Date: Fri, 19 May 2023 17:53:33 +0200 Subject: [PATCH 04/35] Add upsert query for ban account op --- sqlx-data.json | 67 +++++++++++++++++++++++++++++++++++++ src/app/stage/ban_intent.rs | 7 +++- src/db/ban_account_op.rs | 43 ++++++++++++++++++++++++ 3 files changed, 116 insertions(+), 1 deletion(-) diff --git a/sqlx-data.json b/sqlx-data.json index 6caf4d5c..2f8bc891 100644 --- a/sqlx-data.json +++ b/sqlx-data.json @@ -1195,6 +1195,73 @@ }, "query": "\n UPDATE recording\n SET modified_segments =\n CASE\n WHEN created_by = $3 THEN $2\n ELSE segments\n END,\n adjusted_at = NOW()\n WHERE class_id = $1 AND deleted_at IS NULL\n RETURNING\n id,\n class_id,\n rtc_id,\n stream_uri,\n segments AS \"segments!: Option\",\n started_at,\n modified_segments AS \"modified_segments!: Option\",\n created_at,\n adjusted_at,\n transcoded_at,\n created_by AS \"created_by: AgentId\",\n deleted_at\n " }, + "6d53d0fb1e17234db5c1b2718ae6af81892d8ea0f9695b03c7f96ed39cf66a21": { + "describe": { + "columns": [ + { + "name": "user_account: _", + "ordinal": 0, + "type_info": { + "Custom": { + "kind": { + "Composite": [ + [ + "label", + "Text" + ], + [ + "audience", + "Text" + ] + ] + }, + "name": "account_id" + } + } + }, + { + "name": "last_op_id", + "ordinal": 1, + "type_info": "Uuid" + }, + { + "name": "last_op_done", + "ordinal": 2, + "type_info": "Bool" + } + ], + "nullable": [ + false, + false, + false + ], + "parameters": { + "Left": [ + { + "Custom": { + "kind": { + "Composite": [ + [ + "label", + "Text" + ], + [ + "audience", + "Text" + ] + ] + }, + "name": "account_id" + } + }, + "Uuid", + "Bool", + "Uuid" + ] + } + }, + "query": "\n INSERT INTO ban_account_op (user_account, last_op_id, last_op_done)\n VALUES ($1, $2, $3)\n ON CONFLICT (user_account) DO UPDATE\n SET\n last_op_done = EXCLUDED.last_op_done,\n last_op_id = EXCLUDED.last_op_id\n WHERE\n -- allow change op id iff the previous operation is completed\n (\n ban_account_op.last_op_id = $4 AND\n ban_account_op.last_op_done = true\n ) OR\n -- allow to 'complete' operation iff there's no change in last_op_id\n ban_account_op.last_op_id = EXCLUDED.last_op_id\n RETURNING\n user_account AS \"user_account: _\",\n last_op_id,\n last_op_done\n " + }, "7296ad8d156f41d717c88c4f20c012bc010531d460776813fd70b3141485aabe": { "describe": { "columns": [ diff --git a/src/app/stage/ban_intent.rs b/src/app/stage/ban_intent.rs index c33307c6..52074fcf 100644 --- a/src/app/stage/ban_intent.rs +++ b/src/app/stage/ban_intent.rs @@ -1,5 +1,6 @@ use sqlx::PgConnection; use svc_authn::AccountId; +use svc_events::ban::BanIntentEventV1; use svc_nats_client::EventId; use uuid::Uuid; @@ -22,7 +23,7 @@ pub async fn start( user_account: AccountId, ) -> Result<(), Error> { let event_id = get_next_event_id(conn).await?; - let event = svc_events::ban::BanIntentEventV1 { + let event = BanIntentEventV1 { ban, classroom_id: class.id(), user_account, @@ -31,6 +32,10 @@ pub async fn start( nats::publish_event(ctx, class.id(), &event_id, event.into()).await } +pub async fn handle(ctx: &dyn AppContext, intent: BanIntentEventV1) -> Result<(), Error> { + Ok(()) +} + async fn get_next_event_id(conn: &mut PgConnection) -> Result { let next_seq_id = ban_account_op::get_next_seq_id(conn) .await diff --git a/src/db/ban_account_op.rs b/src/db/ban_account_op.rs index f3bc4c92..14cc5d36 100644 --- a/src/db/ban_account_op.rs +++ b/src/db/ban_account_op.rs @@ -49,3 +49,46 @@ pub async fn get_next_seq_id(conn: &mut PgConnection) -> sqlx::Result .fetch_one(conn) .await } + +/// Upsert works only if provided `last_op_id` equals to the one stored +/// in database. Returns `None` if `last_op_id` in database differs. +pub struct UpsertQuery { + user_account: AccountId, + op_done: bool, + last_op_id: Uuid, + new_op_id: Uuid, +} + +impl UpsertQuery { + pub async fn execute(self, conn: &mut PgConnection) -> sqlx::Result> { + sqlx::query_as!( + Object, + r#" + INSERT INTO ban_account_op (user_account, last_op_id, last_op_done) + VALUES ($1, $2, $3) + ON CONFLICT (user_account) DO UPDATE + SET + last_op_done = EXCLUDED.last_op_done, + last_op_id = EXCLUDED.last_op_id + WHERE + -- allow change op id iff the previous operation is completed + ( + ban_account_op.last_op_id = $4 AND + ban_account_op.last_op_done = true + ) OR + -- allow to 'complete' operation iff there's no change in last_op_id + ban_account_op.last_op_id = EXCLUDED.last_op_id + RETURNING + user_account AS "user_account: _", + last_op_id, + last_op_done + "#, + self.user_account as AccountId, + self.new_op_id, + self.op_done, + self.last_op_id + ) + .fetch_optional(conn) + .await + } +} From 1776faf2f7af790013ef5f91a5a1d7d9dfe7252e Mon Sep 17 00:00:00 2001 From: Dmitry Shlagoff Date: Mon, 22 May 2023 14:22:38 +0200 Subject: [PATCH 05/35] Increase rustc version in Dockerfile --- docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index aabd8b37..5c064fbe 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,7 +1,7 @@ ## ----------------------------------------------------------------------------- ## Build ## ----------------------------------------------------------------------------- -FROM rust:1.64.0-slim-buster as build-stage +FROM rust:1.69.0-slim-buster as build-stage RUN apt update && apt install -y --no-install-recommends \ pkg-config \ From 6159ced0a900cea9cd75ba549a0f3dfe948ac142 Mon Sep 17 00:00:00 2001 From: Dmitry Shlagoff Date: Mon, 22 May 2023 14:24:19 +0200 Subject: [PATCH 06/35] Fix clippy --- src/clients/event/types.rs | 2 +- src/db/class/mod.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/clients/event/types.rs b/src/clients/event/types.rs index b2c96363..c971432e 100644 --- a/src/clients/event/types.rs +++ b/src/clients/event/types.rs @@ -140,7 +140,7 @@ pub trait MqttRequest: Clone + serde::Serialize { fn method(&self) -> &'static str; fn payload(&self) -> JsonValue { - serde_json::to_value(&self).unwrap() + serde_json::to_value(self).unwrap() } } diff --git a/src/db/class/mod.rs b/src/db/class/mod.rs index b9854623..a0a10aef 100644 --- a/src/db/class/mod.rs +++ b/src/db/class/mod.rs @@ -45,9 +45,9 @@ impl std::ops::DerefMut for KeyValueProperties { } impl sqlx::Encode<'_, sqlx::Postgres> for KeyValueProperties { - fn encode_by_ref<'q>( + fn encode_by_ref( &self, - buf: &mut >::ArgumentBuffer, + buf: &mut >::ArgumentBuffer, ) -> sqlx::encode::IsNull { self.clone().into_json().encode_by_ref(buf) } From b1308c2075a6acfccbc4ad7a0c1e4c03648ad19c Mon Sep 17 00:00:00 2001 From: Dmitry Shlagoff Date: Mon, 22 May 2023 16:51:39 +0200 Subject: [PATCH 07/35] Impl ban_intent handler --- Cargo.lock | 2 +- sqlx-data.json | 4 ++-- src/app/api/v1/account/ban.rs | 11 ++++++++++- src/app/stage/ban.rs | 19 +++++++++++++++++++ src/app/stage/ban_intent.rs | 33 +++++++++++++++++++++++++++++++-- src/app/stage/mod.rs | 1 + src/db/ban_account_op.rs | 29 +++++++++++++++++++++++++---- 7 files changed, 89 insertions(+), 10 deletions(-) create mode 100644 src/app/stage/ban.rs diff --git a/Cargo.lock b/Cargo.lock index f5838670..c54b7990 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2904,7 +2904,7 @@ dependencies = [ [[package]] name = "svc-events" version = "0.3.0" -source = "git+https://github.com/foxford/svc-conference-events?branch=ULMS-1896/ban-events#8d3e390b0b98d6e07e90f96764e2f6a25374dd2e" +source = "git+https://github.com/foxford/svc-conference-events?branch=ULMS-1896/ban-events#ae009ee7e86f29f09f14dd6856f303052b7d7158" dependencies = [ "serde", "serde_json", diff --git a/sqlx-data.json b/sqlx-data.json index 2f8bc891..120ff592 100644 --- a/sqlx-data.json +++ b/sqlx-data.json @@ -1195,7 +1195,7 @@ }, "query": "\n UPDATE recording\n SET modified_segments =\n CASE\n WHEN created_by = $3 THEN $2\n ELSE segments\n END,\n adjusted_at = NOW()\n WHERE class_id = $1 AND deleted_at IS NULL\n RETURNING\n id,\n class_id,\n rtc_id,\n stream_uri,\n segments AS \"segments!: Option\",\n started_at,\n modified_segments AS \"modified_segments!: Option\",\n created_at,\n adjusted_at,\n transcoded_at,\n created_by AS \"created_by: AgentId\",\n deleted_at\n " }, - "6d53d0fb1e17234db5c1b2718ae6af81892d8ea0f9695b03c7f96ed39cf66a21": { + "6eeb28486097dd038afd6cfa21a1fa8cd5976caeb32c834290f8d3cad348065f": { "describe": { "columns": [ { @@ -1260,7 +1260,7 @@ ] } }, - "query": "\n INSERT INTO ban_account_op (user_account, last_op_id, last_op_done)\n VALUES ($1, $2, $3)\n ON CONFLICT (user_account) DO UPDATE\n SET\n last_op_done = EXCLUDED.last_op_done,\n last_op_id = EXCLUDED.last_op_id\n WHERE\n -- allow change op id iff the previous operation is completed\n (\n ban_account_op.last_op_id = $4 AND\n ban_account_op.last_op_done = true\n ) OR\n -- allow to 'complete' operation iff there's no change in last_op_id\n ban_account_op.last_op_id = EXCLUDED.last_op_id\n RETURNING\n user_account AS \"user_account: _\",\n last_op_id,\n last_op_done\n " + "query": "\n INSERT INTO ban_account_op (user_account, last_op_id, last_op_done)\n VALUES ($1, $2, $3)\n ON CONFLICT (user_account) DO UPDATE\n SET\n last_op_done = EXCLUDED.last_op_done,\n last_op_id = EXCLUDED.last_op_id\n WHERE\n -- allow to 'complete' operation iff there's no change in last_op_id\n -- or allow to do upsert without real changes so we can process\n -- the same message twice\n ban_account_op.last_op_id = EXCLUDED.last_op_id OR\n -- allow change op id iff the previous operation is completed\n (\n ban_account_op.last_op_id = $4 AND\n ban_account_op.last_op_done = true\n )\n RETURNING\n user_account AS \"user_account: _\",\n last_op_id,\n last_op_done\n " }, "7296ad8d156f41d717c88c4f20c012bc010531d460776813fd70b3141485aabe": { "describe": { diff --git a/src/app/api/v1/account/ban.rs b/src/app/api/v1/account/ban.rs index bdfc790f..3e99e487 100644 --- a/src/app/api/v1/account/ban.rs +++ b/src/app/api/v1/account/ban.rs @@ -22,6 +22,7 @@ use super::AppResult; #[derive(Deserialize)] pub struct BanPayload { + // TODO: maybe Option? last_seen_op_id: Uuid, ban: bool, class_id: Uuid, @@ -68,7 +69,15 @@ pub async fn ban( } } - stage::ban_intent::start(ctx.as_ref(), &mut conn, payload.ban, &class, account_to_ban).await?; + stage::ban_intent::start( + ctx.as_ref(), + &mut conn, + payload.ban, + &class, + account_to_ban, + payload.last_seen_op_id, + ) + .await?; Ok(Response::builder().status(200).body(Body::empty()).unwrap()) } diff --git a/src/app/stage/ban.rs b/src/app/stage/ban.rs new file mode 100644 index 00000000..5250a891 --- /dev/null +++ b/src/app/stage/ban.rs @@ -0,0 +1,19 @@ +use svc_events::ban::{BanEventV1, BanIntentEventV1}; +use svc_nats_client::EventId; + +use crate::{ + app::{error::Error, AppContext}, + clients::nats, +}; + +const ENTITY_TYPE: &str = "ban"; + +pub async fn start( + ctx: &dyn AppContext, + intent: BanIntentEventV1, + intent_id: EventId, +) -> Result<(), Error> { + let event_id = (ENTITY_TYPE.to_owned(), intent_id.sequence_id()).into(); + let event = BanEventV1::from(intent); + nats::publish_event(ctx, event.classroom_id, &event_id, event.into()).await +} diff --git a/src/app/stage/ban_intent.rs b/src/app/stage/ban_intent.rs index 52074fcf..8af22db6 100644 --- a/src/app/stage/ban_intent.rs +++ b/src/app/stage/ban_intent.rs @@ -21,18 +21,47 @@ pub async fn start( ban: bool, class: &db::class::Object, user_account: AccountId, + last_op_id: Uuid, ) -> Result<(), Error> { let event_id = get_next_event_id(conn).await?; let event = BanIntentEventV1 { ban, classroom_id: class.id(), user_account, - op_id: Uuid::new_v4(), + last_op_id, + new_op_id: Uuid::new_v4(), }; nats::publish_event(ctx, class.id(), &event_id, event.into()).await } -pub async fn handle(ctx: &dyn AppContext, intent: BanIntentEventV1) -> Result<(), Error> { +pub async fn handle( + ctx: &dyn AppContext, + intent: BanIntentEventV1, + intent_id: EventId, +) -> Result<(), Error> { + let mut conn = ctx + .get_conn() + .await + .error(AppErrorKind::DbConnAcquisitionFailed)?; + + // We need to update db here first, then schedule next stage, then acknowledge + // current message. In case if we fail somewhere in between, we can continue + // processing as if nothing happened -- subsequent upserts will be successful, + // attempts to schedule the same message will fail (dedup). + + ban_account_op::UpsertQuery::new_operation( + intent.user_account.clone(), + intent.last_op_id, + intent.new_op_id, + ) + .execute(&mut conn) + .await + .error(AppErrorKind::DbQueryFailed)? + // failed to upsert -- we've lost the race + .ok_or(Error::from(AppErrorKind::OperationIdObsolete))?; + + super::ban::start(ctx, intent, intent_id).await?; + Ok(()) } diff --git a/src/app/stage/mod.rs b/src/app/stage/mod.rs index aab0c761..ac052e66 100644 --- a/src/app/stage/mod.rs +++ b/src/app/stage/mod.rs @@ -1 +1,2 @@ pub mod ban_intent; +pub mod ban; diff --git a/src/db/ban_account_op.rs b/src/db/ban_account_op.rs index 14cc5d36..76927762 100644 --- a/src/db/ban_account_op.rs +++ b/src/db/ban_account_op.rs @@ -51,7 +51,8 @@ pub async fn get_next_seq_id(conn: &mut PgConnection) -> sqlx::Result } /// Upsert works only if provided `last_op_id` equals to the one stored -/// in database. Returns `None` if `last_op_id` in database differs. +/// in database or `last_op_id` equals to `new_op_id`. +/// Returns `None` if `last_op_id` in database differs. pub struct UpsertQuery { user_account: AccountId, op_done: bool, @@ -60,6 +61,24 @@ pub struct UpsertQuery { } impl UpsertQuery { + pub fn new_operation(user_account: AccountId, last_op_id: Uuid, new_op_id: Uuid) -> Self { + Self { + user_account, + op_done: false, + last_op_id, + new_op_id, + } + } + + pub fn new_complete(user_account: AccountId, op_id: Uuid) -> Self { + Self { + user_account, + op_done: true, + last_op_id: op_id, + new_op_id: op_id, + } + } + pub async fn execute(self, conn: &mut PgConnection) -> sqlx::Result> { sqlx::query_as!( Object, @@ -71,13 +90,15 @@ impl UpsertQuery { last_op_done = EXCLUDED.last_op_done, last_op_id = EXCLUDED.last_op_id WHERE + -- allow to 'complete' operation iff there's no change in last_op_id + -- or allow to do upsert without real changes so we can process + -- the same message twice + ban_account_op.last_op_id = EXCLUDED.last_op_id OR -- allow change op id iff the previous operation is completed ( ban_account_op.last_op_id = $4 AND ban_account_op.last_op_done = true - ) OR - -- allow to 'complete' operation iff there's no change in last_op_id - ban_account_op.last_op_id = EXCLUDED.last_op_id + ) RETURNING user_account AS "user_account: _", last_op_id, From 6f849e662fbb6e6face651e2639ffbcd3602eb93 Mon Sep 17 00:00:00 2001 From: Dmitry Shlagoff Date: Mon, 22 May 2023 16:56:11 +0200 Subject: [PATCH 08/35] Move migrations into single script --- migrations/20230517104549_add_ban_account_op_table.sql | 2 ++ migrations/20230519094604_add_ban_sequence.sql | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) delete mode 100644 migrations/20230519094604_add_ban_sequence.sql diff --git a/migrations/20230517104549_add_ban_account_op_table.sql b/migrations/20230517104549_add_ban_account_op_table.sql index f903ec3f..ab781292 100644 --- a/migrations/20230517104549_add_ban_account_op_table.sql +++ b/migrations/20230517104549_add_ban_account_op_table.sql @@ -3,3 +3,5 @@ CREATE TABLE IF NOT EXISTS ban_account_op ( last_op_id uuid NOT NULL, last_op_done boolean NOT NULL DEFAULT false ); + +CREATE SEQUENCE IF NOT EXISTS ban_entity_seq_id; diff --git a/migrations/20230519094604_add_ban_sequence.sql b/migrations/20230519094604_add_ban_sequence.sql deleted file mode 100644 index dbb3ae37..00000000 --- a/migrations/20230519094604_add_ban_sequence.sql +++ /dev/null @@ -1 +0,0 @@ -CREATE SEQUENCE IF NOT EXISTS ban_entity_seq_id; From 8b957b1310d84c10c675c9752a8eec01b648ff5e Mon Sep 17 00:00:00 2001 From: Dmitry Shlagoff Date: Tue, 23 May 2023 16:52:44 +0200 Subject: [PATCH 09/35] Update deps --- Cargo.lock | 343 ++++++++++++------------------------ Cargo.toml | 2 +- src/app/stage/ban.rs | 6 +- src/app/stage/ban_intent.rs | 3 +- src/clients/nats.rs | 3 +- 5 files changed, 119 insertions(+), 238 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c54b7990..2265f8e7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -28,17 +28,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "ahash" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" -dependencies = [ - "cfg-if", - "once_cell", - "version_check", -] - [[package]] name = "aho-corasick" version = "1.0.1" @@ -148,15 +137,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "atoi" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7c57d12312ff59c811c0643f4d80830505833c9ffaebd193d819392b265be8e" -dependencies = [ - "num-traits", -] - [[package]] name = "autocfg" version = "1.1.0" @@ -412,16 +392,7 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49fc9a695bca7f35f5f4c15cddc84415f66a74ea78eef08e90c5024f2b540e23" dependencies = [ - "crc-catalog 1.1.1", -] - -[[package]] -name = "crc" -version = "3.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe" -dependencies = [ - "crc-catalog 2.2.0", + "crc-catalog", ] [[package]] @@ -430,12 +401,6 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccaeedb56da03b09f598226e25e80088cb4cd25f316e6e4df7d695f0feeb1403" -[[package]] -name = "crc-catalog" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cace84e55f07e7301bae1c519df89cdad8cc3cd868413d3fdbdeca9ff3db484" - [[package]] name = "crossbeam-channel" version = "0.5.1" @@ -534,29 +499,6 @@ dependencies = [ "const-oid", ] -[[package]] -name = "diesel" -version = "1.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b28135ecf6b7d446b43e27e225622a038cc4e2930a1022f51cdb97ada19b8e4d" -dependencies = [ - "bitflags", - "byteorder", - "diesel_derives", - "pq-sys", -] - -[[package]] -name = "diesel_derives" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45f5098f628d02a7a0f68ddba586fb61e80edec3bdc1be3b921f4ceec60858d3" -dependencies = [ - "proc-macro2", - "quote 1.0.27", - "syn 1.0.109", -] - [[package]] name = "difference" version = "2.0.0" @@ -637,8 +579,8 @@ dependencies = [ "serde_qs", "signal-hook", "signal-hook-tokio", - "sqlx 0.5.9", - "svc-agent 0.19.4", + "sqlx", + "svc-agent", "svc-authn 0.7.0", "svc-authz", "svc-error 0.4.0", @@ -665,12 +607,6 @@ version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" -[[package]] -name = "dotenvy" -version = "0.15.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" - [[package]] name = "downcast" version = "0.10.0" @@ -932,16 +868,7 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" dependencies = [ - "ahash 0.7.6", -] - -[[package]] -name = "hashbrown" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" -dependencies = [ - "ahash 0.8.3", + "ahash", ] [[package]] @@ -953,15 +880,6 @@ dependencies = [ "hashbrown 0.11.2", ] -[[package]] -name = "hashlink" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0761a1b9491c4f2e3d66aa0f62d0fba0af9a0e2852e4d48ea506632a4b56e6aa" -dependencies = [ - "hashbrown 0.13.2", -] - [[package]] name = "hashring" version = "0.3.0" @@ -1708,15 +1626,6 @@ version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" -[[package]] -name = "pq-sys" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c0052426df997c0cbd30789eb44ca097e3541717a7b8fa36b1c464ee7edebd" -dependencies = [ - "vcpkg", -] - [[package]] name = "predicates" version = "1.0.8" @@ -2201,7 +2110,7 @@ version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "546b9b6f76c26c60ffbcf0b7136e15169fe13d43949b4aadb7c1edc1c3f3a26f" dependencies = [ - "sentry-anyhow", + "sentry-anyhow 0.23.0", "sentry-backtrace 0.23.0", "sentry-core 0.23.0", "tokio", @@ -2222,6 +2131,20 @@ dependencies = [ "tokio", ] +[[package]] +name = "sentry" +version = "0.31.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "234f6e133d27140ad5ea3b369a7665f7fbc060fe246f81d8168665b38c08b600" +dependencies = [ + "httpdate", + "reqwest", + "sentry-anyhow 0.31.2", + "sentry-backtrace 0.31.2", + "sentry-core 0.31.2", + "tokio", +] + [[package]] name = "sentry-anyhow" version = "0.23.0" @@ -2233,6 +2156,17 @@ dependencies = [ "sentry-core 0.23.0", ] +[[package]] +name = "sentry-anyhow" +version = "0.31.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e940be4c63b29006b4ac422cacea932c2cb5f8c209647ee86446ed27595a42" +dependencies = [ + "anyhow", + "sentry-backtrace 0.31.2", + "sentry-core 0.31.2", +] + [[package]] name = "sentry-backtrace" version = "0.23.0" @@ -2257,6 +2191,18 @@ dependencies = [ "sentry-core 0.27.0", ] +[[package]] +name = "sentry-backtrace" +version = "0.31.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89b6b53de06308dd5ac08934b597bcd72a9aae0c20bc3ab06da69cb34d468e3" +dependencies = [ + "backtrace", + "once_cell", + "regex", + "sentry-core 0.31.2", +] + [[package]] name = "sentry-contexts" version = "0.27.0" @@ -2297,6 +2243,19 @@ dependencies = [ "serde_json", ] +[[package]] +name = "sentry-core" +version = "0.31.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f954f1b89e8cd82576dc49bfab80304c9a6201343b4fe5c68c819f7a9bbed2" +dependencies = [ + "once_cell", + "rand", + "sentry-types 0.31.2", + "serde", + "serde_json", +] + [[package]] name = "sentry-panic" version = "0.27.0" @@ -2339,6 +2298,23 @@ dependencies = [ "uuid 1.3.3", ] +[[package]] +name = "sentry-types" +version = "0.31.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85c53caf80cb1c6fcdf4d82b7bfff8477f50841e4caad7bf8e5e57a152b564cb" +dependencies = [ + "debugid 0.8.0", + "getrandom", + "hex", + "serde", + "serde_json", + "thiserror", + "time 0.3.21", + "url 2.2.2", + "uuid 1.3.3", +] + [[package]] name = "serde" version = "1.0.163" @@ -2577,35 +2553,14 @@ dependencies = [ "unicode_categories", ] -[[package]] -name = "sqlformat" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c12bc9199d1db8234678b7051747c07f517cdcf019262d1847b94ec8b1aee3e" -dependencies = [ - "itertools", - "nom", - "unicode_categories", -] - [[package]] name = "sqlx" version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7911b0031a0247af40095838002999c7a52fba29d9739e93326e71a5a1bc9d43" dependencies = [ - "sqlx-core 0.5.13", - "sqlx-macros 0.5.13", -] - -[[package]] -name = "sqlx" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8de3b03a925878ed54a954f621e64bf55a3c1bd29652d0d1a17830405350188" -dependencies = [ - "sqlx-core 0.6.3", - "sqlx-macros 0.6.3", + "sqlx-core", + "sqlx-macros", ] [[package]] @@ -2614,15 +2569,15 @@ version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e48c61941ccf5ddcada342cd59e3e5173b007c509e1e8e990dafc830294d9dc5" dependencies = [ - "ahash 0.7.6", - "atoi 0.4.0", + "ahash", + "atoi", "base64 0.13.0", "bigdecimal", "bitflags", "byteorder", "bytes", "chrono", - "crc 2.1.0", + "crc", "crossbeam-queue", "dirs", "either", @@ -2631,7 +2586,7 @@ dependencies = [ "futures-core", "futures-intrusive", "futures-util", - "hashlink 0.7.0", + "hashlink", "hex", "hkdf", "hmac", @@ -2651,8 +2606,8 @@ dependencies = [ "sha-1", "sha2 0.10.5", "smallvec", - "sqlformat 0.1.8", - "sqlx-rt 0.5.13", + "sqlformat", + "sqlx-rt", "stringprep", "thiserror", "tokio-stream", @@ -2661,46 +2616,6 @@ dependencies = [ "whoami", ] -[[package]] -name = "sqlx-core" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa8241483a83a3f33aa5fff7e7d9def398ff9990b2752b6c6112b83c6d246029" -dependencies = [ - "ahash 0.7.6", - "atoi 1.0.0", - "bitflags", - "byteorder", - "bytes", - "crc 3.0.1", - "crossbeam-queue", - "dotenvy", - "either", - "event-listener", - "futures-channel", - "futures-core", - "futures-intrusive", - "futures-util", - "hashlink 0.8.2", - "hex", - "indexmap", - "itoa 1.0.1", - "libc", - "log", - "memchr", - "once_cell", - "paste", - "percent-encoding 2.1.0", - "sha2 0.10.5", - "smallvec", - "sqlformat 0.2.1", - "sqlx-rt 0.6.3", - "stringprep", - "thiserror", - "tokio-stream", - "url 2.2.2", -] - [[package]] name = "sqlx-macros" version = "0.5.13" @@ -2717,27 +2632,8 @@ dependencies = [ "serde", "serde_json", "sha2 0.10.5", - "sqlx-core 0.5.13", - "sqlx-rt 0.5.13", - "syn 1.0.109", - "url 2.2.2", -] - -[[package]] -name = "sqlx-macros" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9966e64ae989e7e575b19d7265cb79d7fc3cbbdf179835cb0d716f294c2049c9" -dependencies = [ - "dotenvy", - "either", - "heck", - "once_cell", - "proc-macro2", - "quote 1.0.27", - "sha2 0.10.5", - "sqlx-core 0.6.3", - "sqlx-rt 0.6.3", + "sqlx-core", + "sqlx-rt", "syn 1.0.109", "url 2.2.2", ] @@ -2754,18 +2650,6 @@ dependencies = [ "tokio-native-tls", ] -[[package]] -name = "sqlx-rt" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "804d3f245f894e61b1e6263c84b23ca675d96753b5abfd5cc8597d86806e8024" -dependencies = [ - "native-tls", - "once_cell", - "tokio", - "tokio-native-tls", -] - [[package]] name = "stringprep" version = "0.1.2" @@ -2796,31 +2680,12 @@ dependencies = [ "rumqttc", "serde", "serde_json", - "sqlx 0.5.9", + "sqlx", "svc-authn 0.7.0", "tokio", "uuid 0.8.2", ] -[[package]] -name = "svc-agent" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c90205083c87c6bc25990d5cdd26f98d8c431441e86dbd4e7421b28c428e04d" -dependencies = [ - "async-channel", - "base64 0.21.0", - "chrono", - "http", - "log", - "rumqttc", - "serde", - "serde_json", - "svc-authn 0.8.0", - "tokio", - "uuid 1.3.3", -] - [[package]] name = "svc-authn" version = "0.7.0" @@ -2832,7 +2697,7 @@ dependencies = [ "jsonwebtoken", "serde", "serde_derive", - "sqlx 0.5.9", + "sqlx", ] [[package]] @@ -2841,10 +2706,8 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb5cf659f78c8fff863c17ac4e674829517919716eeecab602e8d2941e89c111" dependencies = [ - "diesel", "serde", "serde_derive", - "sqlx 0.6.3", ] [[package]] @@ -2892,19 +2755,35 @@ dependencies = [ "http", "once_cell", "sentry 0.23.0", - "sentry-anyhow", + "sentry-anyhow 0.23.0", "serde", "serde_derive", - "sqlx 0.5.9", - "svc-agent 0.20.0", + "sqlx", + "svc-agent", "svc-authn 0.8.0", "svc-authz", ] +[[package]] +name = "svc-error" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f841d7fd45d6f179e9f3765491fcb5eea100a5bbe50ea47faf3f262031966d9" +dependencies = [ + "anyhow", + "crossbeam-channel", + "http", + "once_cell", + "sentry 0.31.2", + "sentry-anyhow 0.31.2", + "serde", + "serde_derive", +] + [[package]] name = "svc-events" version = "0.3.0" -source = "git+https://github.com/foxford/svc-conference-events?branch=ULMS-1896/ban-events#ae009ee7e86f29f09f14dd6856f303052b7d7158" +source = "git+https://github.com/foxford/svc-conference-events?branch=ULMS-1896/ban-events#469baab9fbf245d9e584b8b335b5708361fb4d55" dependencies = [ "serde", "serde_json", @@ -2915,20 +2794,22 @@ dependencies = [ [[package]] name = "svc-nats-client" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7deb7c3dff073750c31aed6519c2a272ad35cc69d00425030b9177df711d6942" +version = "0.2.0" +source = "git+https://github.com/foxford/svc-nats-client/?branch=ULMS-1896/add-consumer-move-event-id#b8867a7e26f2105ab453dc963109f9ba933bbbbe" dependencies = [ "anyhow", "async-nats", "async-trait", "futures", + "futures-util", "humantime-serde", "reqwest", "serde", - "svc-agent 0.19.4", - "svc-error 0.4.0", + "svc-agent", + "svc-error 0.5.0", + "svc-events", "thiserror", + "tokio", "tracing", "uuid 0.8.2", ] @@ -2945,7 +2826,7 @@ dependencies = [ "hyper", "once_cell", "prometheus", - "svc-agent 0.19.4", + "svc-agent", "svc-authn 0.7.0", "svc-error 0.3.0", "tokio", diff --git a/Cargo.toml b/Cargo.toml index 49406de8..35fc459f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,7 +56,7 @@ svc-error = { version = "0.4", features = [ "sqlx", ] } svc-events = { git = "https://github.com/foxford/svc-conference-events", branch = "ULMS-1896/ban-events" } -svc-nats-client = "0.1" +svc-nats-client = { git = "https://github.com/foxford/svc-nats-client/", branch = "ULMS-1896/add-consumer-move-event-id" } svc-utils = { version = "0.6.0", features = ["log-middleware", "metrics-middleware", "cors-middleware", "authn-extractor"] } tokio = { version = "1.17", features = ["full"] } tower = { version = "0.4", features = [ "timeout" ] } diff --git a/src/app/stage/ban.rs b/src/app/stage/ban.rs index 5250a891..cb246620 100644 --- a/src/app/stage/ban.rs +++ b/src/app/stage/ban.rs @@ -1,5 +1,7 @@ -use svc_events::ban::{BanEventV1, BanIntentEventV1}; -use svc_nats_client::EventId; +use svc_events::{ + ban::{BanEventV1, BanIntentEventV1}, + EventId, +}; use crate::{ app::{error::Error, AppContext}, diff --git a/src/app/stage/ban_intent.rs b/src/app/stage/ban_intent.rs index 8af22db6..1406dfe6 100644 --- a/src/app/stage/ban_intent.rs +++ b/src/app/stage/ban_intent.rs @@ -1,7 +1,6 @@ use sqlx::PgConnection; use svc_authn::AccountId; -use svc_events::ban::BanIntentEventV1; -use svc_nats_client::EventId; +use svc_events::{ban::BanIntentEventV1, EventId}; use uuid::Uuid; use crate::{ diff --git a/src/clients/nats.rs b/src/clients/nats.rs index 11d297a1..159862ae 100644 --- a/src/clients/nats.rs +++ b/src/clients/nats.rs @@ -1,7 +1,6 @@ use uuid::Uuid; -use svc_events::Event; -use svc_nats_client::EventId; +use svc_events::{Event, EventId}; use crate::app::{ error::{Error, ErrorExt, ErrorKind as AppErrorKind}, From b5ab3ab264a3481af0f188164d52e18ac29e694a Mon Sep 17 00:00:00 2001 From: Dmitry Shlagoff Date: Tue, 23 May 2023 17:29:45 +0200 Subject: [PATCH 10/35] WIP add nats consumer --- Cargo.lock | 2 +- src/app/mod.rs | 31 +++++++++++++++++++++++++++---- src/app/stage/mod.rs | 13 ++++++++++++- src/config.rs | 1 + 4 files changed, 41 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2265f8e7..b86c830d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2795,7 +2795,7 @@ dependencies = [ [[package]] name = "svc-nats-client" version = "0.2.0" -source = "git+https://github.com/foxford/svc-nats-client/?branch=ULMS-1896/add-consumer-move-event-id#b8867a7e26f2105ab453dc963109f9ba933bbbbe" +source = "git+https://github.com/foxford/svc-nats-client/?branch=ULMS-1896/add-consumer-move-event-id#a6953d7429a5438964e17a6a1c5d373356ae64d8" dependencies = [ "anyhow", "async-nats", diff --git a/src/app/mod.rs b/src/app/mod.rs index 3cc28a20..6eecff5a 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -1,5 +1,5 @@ -use std::sync::Arc; use std::time::Duration; +use std::{future, sync::Arc}; use anyhow::{Context, Result}; use futures::StreamExt; @@ -68,21 +68,39 @@ pub async fn run(db: PgPool, authz_cache: Option>) -> Result authz, ); - let state = match &config.nats { + let (shutdown_tx, shutdown_rx) = tokio::sync::watch::channel(()); + + let nats_client = match &config.nats { Some(cfg) => { let nats_client = svc_nats_client::Client::new(cfg.clone()) .await .context("nats client")?; info!("Connected to nats"); - state.add_nats_client(nats_client) + Some(nats_client) } + None => None, + }; + + let state = match nats_client.clone() { + Some(nats_client) => state.add_nats_client(nats_client), None => state, }; let state = Arc::new(state) as Arc; let state_ = state.clone(); + let nats_consumer = match (nats_client, &config.nats_consumer) { + (Some(nats_client), Some(cfg)) => { + svc_nats_client::consumer::run(nats_client, cfg.clone(), shutdown_rx, move |msg| { + crate::app::stage::route_message(state_.clone(), msg) + }) + } + _ => tokio::spawn(future::ready(Ok(()))), + }; + + let state_ = state.clone(); + let message_handler = Arc::new(MessageHandler::new(state_, dispatcher)); tokio::task::spawn(async move { while let Some(message) = rx.recv().await { @@ -147,6 +165,11 @@ pub async fn run(db: PgPool, authz_cache: Option>) -> Result let signals = signals_stream.next(); futures::future::select(app_future, signals).await; + shutdown_tx.send(()).ok(); + + if let Err(err) = nats_consumer.await { + tracing::error!(%err, "nats consumer failed"); + } metrics_server.shutdown().await; @@ -259,6 +282,6 @@ mod info; mod metrics; mod postprocessing_strategy; pub mod services; +mod stage; mod tide_state; pub mod turn_host; -mod stage; diff --git a/src/app/stage/mod.rs b/src/app/stage/mod.rs index ac052e66..e98687f0 100644 --- a/src/app/stage/mod.rs +++ b/src/app/stage/mod.rs @@ -1,2 +1,13 @@ -pub mod ban_intent; +use std::sync::Arc; + +use super::AppContext; + pub mod ban; +pub mod ban_intent; + +pub async fn route_message<'a>( + ctx: Arc, + msg: &'a svc_nats_client::Message, +) -> svc_nats_client::consumer::HandleMessageOutcome { + todo!() +} diff --git a/src/config.rs b/src/config.rs index c1c7ed85..9c8ca2a5 100644 --- a/src/config.rs +++ b/src/config.rs @@ -33,6 +33,7 @@ pub struct Config { pub short_namespace: Option, pub frontend: HashMap, pub nats: Option, + pub nats_consumer: Option, } #[derive(Clone, Debug, Deserialize)] From 7342ecd87e1bb81bcfa43ed6769cecc28f93e37a Mon Sep 17 00:00:00 2001 From: Dmitry Shlagoff Date: Wed, 24 May 2023 15:43:02 +0200 Subject: [PATCH 11/35] Add nats consumer and ban handlers --- Cargo.lock | 4 +- Cargo.toml | 2 +- ...0230517104549_add_ban_account_op_table.sql | 3 +- sqlx-data.json | 153 ++++++++++-------- src/app/api/v1/account/ban.rs | 2 +- src/app/error.rs | 7 + src/app/stage/ban.rs | 68 +++++++- src/app/stage/ban_intent.rs | 17 +- src/app/stage/mod.rs | 116 ++++++++++++- src/db/ban_account_op.rs | 53 ++++-- src/test_helpers/state.rs | 2 +- 11 files changed, 325 insertions(+), 102 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b86c830d..bd589cdf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2783,7 +2783,7 @@ dependencies = [ [[package]] name = "svc-events" version = "0.3.0" -source = "git+https://github.com/foxford/svc-conference-events?branch=ULMS-1896/ban-events#469baab9fbf245d9e584b8b335b5708361fb4d55" +source = "git+https://github.com/foxford/svc-events?branch=ULMS-1896/ban-events#469baab9fbf245d9e584b8b335b5708361fb4d55" dependencies = [ "serde", "serde_json", @@ -2795,7 +2795,7 @@ dependencies = [ [[package]] name = "svc-nats-client" version = "0.2.0" -source = "git+https://github.com/foxford/svc-nats-client/?branch=ULMS-1896/add-consumer-move-event-id#a6953d7429a5438964e17a6a1c5d373356ae64d8" +source = "git+https://github.com/foxford/svc-nats-client/?branch=ULMS-1896/add-consumer-move-event-id#90aec7aef0379e771cc7ebe8a2bd8d55f656bb6e" dependencies = [ "anyhow", "async-nats", diff --git a/Cargo.toml b/Cargo.toml index 35fc459f..3ab40bd2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,7 +55,7 @@ svc-error = { version = "0.4", features = [ "sentry-extension", "sqlx", ] } -svc-events = { git = "https://github.com/foxford/svc-conference-events", branch = "ULMS-1896/ban-events" } +svc-events = { git = "https://github.com/foxford/svc-events", branch = "ULMS-1896/ban-events" } svc-nats-client = { git = "https://github.com/foxford/svc-nats-client/", branch = "ULMS-1896/add-consumer-move-event-id" } svc-utils = { version = "0.6.0", features = ["log-middleware", "metrics-middleware", "cors-middleware", "authn-extractor"] } tokio = { version = "1.17", features = ["full"] } diff --git a/migrations/20230517104549_add_ban_account_op_table.sql b/migrations/20230517104549_add_ban_account_op_table.sql index ab781292..d44a6151 100644 --- a/migrations/20230517104549_add_ban_account_op_table.sql +++ b/migrations/20230517104549_add_ban_account_op_table.sql @@ -1,7 +1,8 @@ CREATE TABLE IF NOT EXISTS ban_account_op ( user_account account_id PRIMARY KEY, last_op_id uuid NOT NULL, - last_op_done boolean NOT NULL DEFAULT false + video_complete boolean NOT NULL DEFAULT false, + event_access_complete boolean NOT NULL DEFAULT false ); CREATE SEQUENCE IF NOT EXISTS ban_entity_seq_id; diff --git a/sqlx-data.json b/sqlx-data.json index 120ff592..c653c1fc 100644 --- a/sqlx-data.json +++ b/sqlx-data.json @@ -1195,73 +1195,6 @@ }, "query": "\n UPDATE recording\n SET modified_segments =\n CASE\n WHEN created_by = $3 THEN $2\n ELSE segments\n END,\n adjusted_at = NOW()\n WHERE class_id = $1 AND deleted_at IS NULL\n RETURNING\n id,\n class_id,\n rtc_id,\n stream_uri,\n segments AS \"segments!: Option\",\n started_at,\n modified_segments AS \"modified_segments!: Option\",\n created_at,\n adjusted_at,\n transcoded_at,\n created_by AS \"created_by: AgentId\",\n deleted_at\n " }, - "6eeb28486097dd038afd6cfa21a1fa8cd5976caeb32c834290f8d3cad348065f": { - "describe": { - "columns": [ - { - "name": "user_account: _", - "ordinal": 0, - "type_info": { - "Custom": { - "kind": { - "Composite": [ - [ - "label", - "Text" - ], - [ - "audience", - "Text" - ] - ] - }, - "name": "account_id" - } - } - }, - { - "name": "last_op_id", - "ordinal": 1, - "type_info": "Uuid" - }, - { - "name": "last_op_done", - "ordinal": 2, - "type_info": "Bool" - } - ], - "nullable": [ - false, - false, - false - ], - "parameters": { - "Left": [ - { - "Custom": { - "kind": { - "Composite": [ - [ - "label", - "Text" - ], - [ - "audience", - "Text" - ] - ] - }, - "name": "account_id" - } - }, - "Uuid", - "Bool", - "Uuid" - ] - } - }, - "query": "\n INSERT INTO ban_account_op (user_account, last_op_id, last_op_done)\n VALUES ($1, $2, $3)\n ON CONFLICT (user_account) DO UPDATE\n SET\n last_op_done = EXCLUDED.last_op_done,\n last_op_id = EXCLUDED.last_op_id\n WHERE\n -- allow to 'complete' operation iff there's no change in last_op_id\n -- or allow to do upsert without real changes so we can process\n -- the same message twice\n ban_account_op.last_op_id = EXCLUDED.last_op_id OR\n -- allow change op id iff the previous operation is completed\n (\n ban_account_op.last_op_id = $4 AND\n ban_account_op.last_op_done = true\n )\n RETURNING\n user_account AS \"user_account: _\",\n last_op_id,\n last_op_done\n " - }, "7296ad8d156f41d717c88c4f20c012bc010531d460776813fd70b3141485aabe": { "describe": { "columns": [ @@ -1593,7 +1526,7 @@ }, "query": "\n SELECT\n id::text AS \"id!: String\"\n FROM class\n WHERE conference_room_id = $1\n " }, - "822f926f43aa41ec0c5f772fb842755f8283283edf3c2c769506b3ed08a79607": { + "8cdcd79a99763311030cf8cdb85effaf8031104a08a3b118bdaaf33c4269980f": { "describe": { "columns": [ { @@ -1623,12 +1556,18 @@ "type_info": "Uuid" }, { - "name": "last_op_done", + "name": "video_complete", "ordinal": 2, "type_info": "Bool" + }, + { + "name": "event_access_complete", + "ordinal": 3, + "type_info": "Bool" } ], "nullable": [ + false, false, false, false @@ -1639,7 +1578,7 @@ ] } }, - "query": "\n SELECT\n user_account AS \"user_account: _\",\n last_op_id AS \"last_op_id: _\",\n last_op_done\n FROM ban_account_op\n WHERE\n user_account = $1\n LIMIT 1;\n " + "query": "\n SELECT\n user_account AS \"user_account: _\",\n last_op_id AS \"last_op_id: _\",\n video_complete,\n event_access_complete\n FROM ban_account_op\n WHERE\n user_account = $1\n LIMIT 1;\n " }, "a4fc28b7fa8a6a5f4051750a2447784a10a221632dbea080fe1d4c980de076fd": { "describe": { @@ -1814,6 +1753,80 @@ }, "query": "\n INSERT INTO recording (class_id, rtc_id, segments, modified_segments, stream_uri, started_at, adjusted_at, transcoded_at, created_by)\n VALUES ($1, $2, $3, $4, $5, NOW(), NOW(), NOW(), $6)\n RETURNING\n id,\n class_id,\n rtc_id,\n stream_uri,\n segments AS \"segments!: Option\",\n started_at,\n modified_segments AS \"modified_segments!: Option\",\n created_at,\n adjusted_at,\n transcoded_at,\n created_by AS \"created_by: AgentId\",\n deleted_at\n " }, + "ad5a4328c5367ea842463346d51759c429921081fb8c9c8e103970d77209ff10": { + "describe": { + "columns": [ + { + "name": "user_account: _", + "ordinal": 0, + "type_info": { + "Custom": { + "kind": { + "Composite": [ + [ + "label", + "Text" + ], + [ + "audience", + "Text" + ] + ] + }, + "name": "account_id" + } + } + }, + { + "name": "last_op_id", + "ordinal": 1, + "type_info": "Uuid" + }, + { + "name": "video_complete", + "ordinal": 2, + "type_info": "Bool" + }, + { + "name": "event_access_complete", + "ordinal": 3, + "type_info": "Bool" + } + ], + "nullable": [ + false, + false, + false, + false + ], + "parameters": { + "Left": [ + { + "Custom": { + "kind": { + "Composite": [ + [ + "label", + "Text" + ], + [ + "audience", + "Text" + ] + ] + }, + "name": "account_id" + } + }, + "Uuid", + "Bool", + "Bool", + "Uuid" + ] + } + }, + "query": "\n INSERT INTO ban_account_op (user_account, last_op_id, video_complete, event_access_complete)\n VALUES ($1, $2, COALESCE($3, false), COALESCE($4, false))\n ON CONFLICT (user_account) DO UPDATE\n SET\n video_complete = COALESCE(EXCLUDED.video_complete, ban_account_op.video_complete),\n event_access_complete = COALESCE(EXCLUDED.event_access_complete, ban_account_op.event_access_complete),\n last_op_id = EXCLUDED.last_op_id\n WHERE\n -- allow to 'complete' operation iff there's no change in last_op_id\n -- or allow to do upsert without real changes so we can process\n -- the same message twice\n ban_account_op.last_op_id = EXCLUDED.last_op_id OR\n -- allow change op id iff the previous operation is completed\n (\n ban_account_op.last_op_id = $5 AND\n ban_account_op.video_complete = true AND\n ban_account_op.event_access_complete = true\n )\n RETURNING\n user_account AS \"user_account: _\",\n last_op_id,\n video_complete,\n event_access_complete\n " + }, "c67188dde7672c71f7e14a0ef09047934fbf808e5541e1b35c88004f36c16c8b": { "describe": { "columns": [ diff --git a/src/app/api/v1/account/ban.rs b/src/app/api/v1/account/ban.rs index 3e99e487..12e75858 100644 --- a/src/app/api/v1/account/ban.rs +++ b/src/app/api/v1/account/ban.rs @@ -64,7 +64,7 @@ pub async fn ban( return Err(AppErrorKind::OperationIdObsolete.into()); } - if !op.last_op_done { + if !op.complete() { return Err(AppErrorKind::OperationInProgress.into()); } } diff --git a/src/app/error.rs b/src/app/error.rs index fae51a7f..3c549c79 100644 --- a/src/app/error.rs +++ b/src/app/error.rs @@ -42,6 +42,7 @@ pub enum ErrorKind { ClassAlreadyEstablished, OperationIdObsolete, OperationInProgress, + OperationFailure, NatsPublishFailed, NatsClientNotFound, } @@ -207,6 +208,12 @@ impl From for ErrorKindProperties { title: "Operation is not completed yet, retry later", is_notify_sentry: false, }, + ErrorKind::OperationFailure => ErrorKindProperties { + status: ResponseStatus::INTERNAL_SERVER_ERROR, + kind: "operation_failure", + title: "Operation failed really bad", + is_notify_sentry: true, + }, ErrorKind::NatsPublishFailed => ErrorKindProperties { status: ResponseStatus::UNPROCESSABLE_ENTITY, kind: "nats_publish_failed", diff --git a/src/app/stage/ban.rs b/src/app/stage/ban.rs index cb246620..49e81941 100644 --- a/src/app/stage/ban.rs +++ b/src/app/stage/ban.rs @@ -1,13 +1,19 @@ use svc_events::{ - ban::{BanEventV1, BanIntentEventV1}, + ban::{BanEventAccessCompleteV1, BanEventV1, BanIntentEventV1, BanVideoCompleteV1}, EventId, }; use crate::{ - app::{error::Error, AppContext}, + app::{ + error::{Error, ErrorExt, ErrorKind as AppErrorKind}, + AppContext, + }, clients::nats, + db::ban_account_op, }; +use super::{FailureKind, HandleMsgFailure}; + const ENTITY_TYPE: &str = "ban"; pub async fn start( @@ -19,3 +25,61 @@ pub async fn start( let event = BanEventV1::from(intent); nats::publish_event(ctx, event.classroom_id, &event_id, event.into()).await } + +pub async fn handle_video_complete( + ctx: &dyn AppContext, + video_complete: BanVideoCompleteV1, + event_id: EventId, +) -> Result<(), HandleMsgFailure> { + let mut conn = ctx + .get_conn() + .await + .error(AppErrorKind::DbConnAcquisitionFailed) + .transient()?; + + let op = ban_account_op::UpsertQuery::new_video_complete( + video_complete.user_account.clone(), + video_complete.op_id, + ) + .execute(&mut conn) + .await + .error(AppErrorKind::DbQueryFailed) + .transient()? + .ok_or(Error::from(AppErrorKind::OperationFailure)) + .permanent()?; + + if op.complete() { + // TODO: finish flow + } + + Ok(()) +} + +pub async fn handle_event_access_complete( + ctx: &dyn AppContext, + event_access_complete: BanEventAccessCompleteV1, + event_id: EventId, +) -> Result<(), HandleMsgFailure> { + let mut conn = ctx + .get_conn() + .await + .error(AppErrorKind::DbConnAcquisitionFailed) + .transient()?; + + let op = ban_account_op::UpsertQuery::new_event_access_complete( + event_access_complete.user_account.clone(), + event_access_complete.op_id, + ) + .execute(&mut conn) + .await + .error(AppErrorKind::DbQueryFailed) + .transient()? + .ok_or(Error::from(AppErrorKind::OperationFailure)) + .permanent()?; + + if op.complete() { + // TODO: finish flow + } + + Ok(()) +} diff --git a/src/app/stage/ban_intent.rs b/src/app/stage/ban_intent.rs index 1406dfe6..4b7bb7f1 100644 --- a/src/app/stage/ban_intent.rs +++ b/src/app/stage/ban_intent.rs @@ -12,6 +12,8 @@ use crate::{ db::{self, ban_account_op}, }; +use super::{FailureKind, HandleMsgFailure}; + const ENTITY_TYPE: &str = "ban-intent"; pub async fn start( @@ -37,11 +39,12 @@ pub async fn handle( ctx: &dyn AppContext, intent: BanIntentEventV1, intent_id: EventId, -) -> Result<(), Error> { +) -> Result<(), HandleMsgFailure> { let mut conn = ctx .get_conn() .await - .error(AppErrorKind::DbConnAcquisitionFailed)?; + .error(AppErrorKind::DbConnAcquisitionFailed) + .transient()?; // We need to update db here first, then schedule next stage, then acknowledge // current message. In case if we fail somewhere in between, we can continue @@ -55,11 +58,15 @@ pub async fn handle( ) .execute(&mut conn) .await - .error(AppErrorKind::DbQueryFailed)? + .error(AppErrorKind::DbQueryFailed) + .transient()? // failed to upsert -- we've lost the race - .ok_or(Error::from(AppErrorKind::OperationIdObsolete))?; + .ok_or(Error::from(AppErrorKind::OperationIdObsolete)) + .permanent()?; - super::ban::start(ctx, intent, intent_id).await?; + super::ban::start(ctx, intent, intent_id) + .await + .transient()?; Ok(()) } diff --git a/src/app/stage/mod.rs b/src/app/stage/mod.rs index e98687f0..78525556 100644 --- a/src/app/stage/mod.rs +++ b/src/app/stage/mod.rs @@ -1,13 +1,119 @@ -use std::sync::Arc; +use std::{convert::TryFrom, str::FromStr, sync::Arc}; + +use anyhow::Context; +use svc_events::{Event, EventV1}; +use svc_nats_client::{consumer::HandleMessageOutcome, Subject}; + +use crate::db; use super::AppContext; pub mod ban; pub mod ban_intent; -pub async fn route_message<'a>( +pub async fn route_message( + ctx: Arc, + msg: Arc, +) -> HandleMessageOutcome { + match do_route_msg(ctx, msg).await { + Ok(_) => HandleMessageOutcome::Processed, + Err(HandleMsgFailure::Transient(e)) => { + tracing::error!(%e, "transient failure, retrying"); + HandleMessageOutcome::ProcessLater + } + Err(HandleMsgFailure::Permanent(e)) => { + tracing::error!(%e, "permanent failure, won't process"); + HandleMessageOutcome::WontProcess + } + } +} + +pub enum HandleMsgFailure { + Transient(E), + Permanent(E), +} + +trait FailureKind { + /// This error can be fixed by retrying later. + fn transient(self) -> Result>; + /// This error can't be fixed by retrying later (parse failure, unknown id, etc). + fn permanent(self) -> Result>; +} + +impl FailureKind for Result { + fn transient(self) -> Result> { + self.map_err(|e| HandleMsgFailure::Transient(e)) + } + + fn permanent(self) -> Result> { + self.map_err(|e| HandleMsgFailure::Permanent(e)) + } +} + +async fn do_route_msg( ctx: Arc, - msg: &'a svc_nats_client::Message, -) -> svc_nats_client::consumer::HandleMessageOutcome { - todo!() + msg: Arc, +) -> Result<(), HandleMsgFailure> { + let subject = Subject::from_str(&msg.subject) + .context("parse nats subject") + .permanent()?; + + let event = serde_json::from_slice::(msg.payload.as_ref()) + .context("parse nats payload") + .permanent()?; + + let classroom_id = subject.classroom_id(); + let room = { + let mut conn = ctx + .get_conn() + .await + .map_err(|e| anyhow::anyhow!(e)) + .transient()?; + + db::class::ReadQuery::by_id(classroom_id) + .execute(&mut conn) + .await + .context("find room by classroom_id") + .transient()? + .ok_or(anyhow!( + "failed to get room by classroom_id: {}", + classroom_id + )) + .permanent()? + }; + + let headers = svc_nats_client::Headers::try_from(msg.headers.clone().unwrap_or_default()) + .context("parse nats headers") + .permanent()?; + let agent_id = headers.sender_id(); + let event_id = headers.event_id(); + + let r = match event { + Event::V1(e) => match e { + EventV1::BanIntent(intent) => { + ban_intent::handle(ctx.as_ref(), intent, event_id.clone()).await + } + EventV1::BanVideoComplete(video_complete) => { + ban::handle_video_complete(ctx.as_ref(), video_complete, event_id.clone()).await + } + EventV1::BanEventAccessComplete(event_access_complete) => { + ban::handle_event_access_complete( + ctx.as_ref(), + event_access_complete, + event_id.clone(), + ) + .await + } + _ => Ok(()), + }, + }; + + match r { + Ok(_) => Ok(()), + Err(HandleMsgFailure::Transient(e)) => Err(HandleMsgFailure::Transient(anyhow!(e))), + Err(HandleMsgFailure::Permanent(e)) => { + // TODO: send notification about error + Err(HandleMsgFailure::Permanent(anyhow!(e))) + } + } } diff --git a/src/db/ban_account_op.rs b/src/db/ban_account_op.rs index 76927762..32ca83e3 100644 --- a/src/db/ban_account_op.rs +++ b/src/db/ban_account_op.rs @@ -5,7 +5,14 @@ use uuid::Uuid; pub struct Object { pub user_account: AccountId, pub last_op_id: Uuid, - pub last_op_done: bool, + pub video_complete: bool, + pub event_access_complete: bool, +} + +impl Object { + pub fn complete(&self) -> bool { + self.video_complete && self.event_access_complete + } } pub struct ReadQuery<'a> { @@ -24,7 +31,8 @@ impl<'a> ReadQuery<'a> { SELECT user_account AS "user_account: _", last_op_id AS "last_op_id: _", - last_op_done + video_complete, + event_access_complete FROM ban_account_op WHERE user_account = $1 @@ -55,7 +63,8 @@ pub async fn get_next_seq_id(conn: &mut PgConnection) -> sqlx::Result /// Returns `None` if `last_op_id` in database differs. pub struct UpsertQuery { user_account: AccountId, - op_done: bool, + video_complete: Option, + event_access_complete: Option, last_op_id: Uuid, new_op_id: Uuid, } @@ -64,16 +73,28 @@ impl UpsertQuery { pub fn new_operation(user_account: AccountId, last_op_id: Uuid, new_op_id: Uuid) -> Self { Self { user_account, - op_done: false, + video_complete: None, + event_access_complete: None, last_op_id, new_op_id, } } - pub fn new_complete(user_account: AccountId, op_id: Uuid) -> Self { + pub fn new_video_complete(user_account: AccountId, op_id: Uuid) -> Self { + Self { + user_account, + video_complete: Some(true), + event_access_complete: None, + last_op_id: op_id, + new_op_id: op_id, + } + } + + pub fn new_event_access_complete(user_account: AccountId, op_id: Uuid) -> Self { Self { user_account, - op_done: true, + video_complete: None, + event_access_complete: Some(true), last_op_id: op_id, new_op_id: op_id, } @@ -83,12 +104,13 @@ impl UpsertQuery { sqlx::query_as!( Object, r#" - INSERT INTO ban_account_op (user_account, last_op_id, last_op_done) - VALUES ($1, $2, $3) + INSERT INTO ban_account_op (user_account, last_op_id, video_complete, event_access_complete) + VALUES ($1, $2, COALESCE($3, false), COALESCE($4, false)) ON CONFLICT (user_account) DO UPDATE SET - last_op_done = EXCLUDED.last_op_done, - last_op_id = EXCLUDED.last_op_id + video_complete = COALESCE(EXCLUDED.video_complete, ban_account_op.video_complete), + event_access_complete = COALESCE(EXCLUDED.event_access_complete, ban_account_op.event_access_complete), + last_op_id = EXCLUDED.last_op_id WHERE -- allow to 'complete' operation iff there's no change in last_op_id -- or allow to do upsert without real changes so we can process @@ -96,17 +118,20 @@ impl UpsertQuery { ban_account_op.last_op_id = EXCLUDED.last_op_id OR -- allow change op id iff the previous operation is completed ( - ban_account_op.last_op_id = $4 AND - ban_account_op.last_op_done = true + ban_account_op.last_op_id = $5 AND + ban_account_op.video_complete = true AND + ban_account_op.event_access_complete = true ) RETURNING user_account AS "user_account: _", last_op_id, - last_op_done + video_complete, + event_access_complete "#, self.user_account as AccountId, self.new_op_id, - self.op_done, + self.video_complete, + self.event_access_complete, self.last_op_id ) .fetch_optional(conn) diff --git a/src/test_helpers/state.rs b/src/test_helpers/state.rs index 1814c667..5828a751 100644 --- a/src/test_helpers/state.rs +++ b/src/test_helpers/state.rs @@ -114,7 +114,7 @@ impl NatsClient for TestNatsClient { unimplemented!() } - async fn terminate(&self, _message: Message) -> Result<(), TermMessageError> { + async fn terminate(&self, _message: &Message) -> Result<(), TermMessageError> { unimplemented!() } } From f78b9506bdb5682c1e6a6989a644990330642173 Mon Sep 17 00:00:00 2001 From: Dmitry Shlagoff Date: Thu, 25 May 2023 17:07:43 +0200 Subject: [PATCH 12/35] Rename fields + add missing pieces --- Cargo.lock | 2 +- ...0230517104549_add_ban_account_op_table.sql | 4 +- sqlx-data.json | 256 +++++++++--------- src/app/api/v1/account/ban.rs | 3 +- src/app/stage/ban.rs | 53 ++-- src/app/stage/ban_intent.rs | 10 +- src/app/stage/mod.rs | 19 +- src/db/ban_account_op.rs | 58 ++-- 8 files changed, 210 insertions(+), 195 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bd589cdf..b05fcd7a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2783,7 +2783,7 @@ dependencies = [ [[package]] name = "svc-events" version = "0.3.0" -source = "git+https://github.com/foxford/svc-events?branch=ULMS-1896/ban-events#469baab9fbf245d9e584b8b335b5708361fb4d55" +source = "git+https://github.com/foxford/svc-events?branch=ULMS-1896/ban-events#70aad419dd5bcdd9d6d001fe9d6e19a8546c74ba" dependencies = [ "serde", "serde_json", diff --git a/migrations/20230517104549_add_ban_account_op_table.sql b/migrations/20230517104549_add_ban_account_op_table.sql index d44a6151..f8bb2036 100644 --- a/migrations/20230517104549_add_ban_account_op_table.sql +++ b/migrations/20230517104549_add_ban_account_op_table.sql @@ -1,8 +1,8 @@ CREATE TABLE IF NOT EXISTS ban_account_op ( user_account account_id PRIMARY KEY, last_op_id uuid NOT NULL, - video_complete boolean NOT NULL DEFAULT false, - event_access_complete boolean NOT NULL DEFAULT false + is_video_streaming_banned boolean NOT NULL DEFAULT false, + is_collaboration_banned boolean NOT NULL DEFAULT false ); CREATE SEQUENCE IF NOT EXISTS ban_entity_seq_id; diff --git a/sqlx-data.json b/sqlx-data.json index c653c1fc..c693b2dc 100644 --- a/sqlx-data.json +++ b/sqlx-data.json @@ -781,6 +781,134 @@ }, "query": "DELETE FROM recording WHERE class_id = $1 AND rtc_id = $2" }, + "59afd2ec969f3fd188d888784ff22b0f1d810bf75c58f2488cd3b8b263853a27": { + "describe": { + "columns": [ + { + "name": "user_account: _", + "ordinal": 0, + "type_info": { + "Custom": { + "kind": { + "Composite": [ + [ + "label", + "Text" + ], + [ + "audience", + "Text" + ] + ] + }, + "name": "account_id" + } + } + }, + { + "name": "last_op_id: _", + "ordinal": 1, + "type_info": "Uuid" + }, + { + "name": "is_video_streaming_banned", + "ordinal": 2, + "type_info": "Bool" + }, + { + "name": "is_collaboration_banned", + "ordinal": 3, + "type_info": "Bool" + } + ], + "nullable": [ + false, + false, + false, + false + ], + "parameters": { + "Left": [ + "Record" + ] + } + }, + "query": "\n SELECT\n user_account AS \"user_account: _\",\n last_op_id AS \"last_op_id: _\",\n is_video_streaming_banned,\n is_collaboration_banned\n FROM ban_account_op\n WHERE\n user_account = $1\n LIMIT 1;\n " + }, + "5a9a081e5b9f08f8558f4549b51dc1183af3da3e34c7de7e5c2bbac6590c0e17": { + "describe": { + "columns": [ + { + "name": "user_account: _", + "ordinal": 0, + "type_info": { + "Custom": { + "kind": { + "Composite": [ + [ + "label", + "Text" + ], + [ + "audience", + "Text" + ] + ] + }, + "name": "account_id" + } + } + }, + { + "name": "last_op_id", + "ordinal": 1, + "type_info": "Uuid" + }, + { + "name": "is_video_streaming_banned", + "ordinal": 2, + "type_info": "Bool" + }, + { + "name": "is_collaboration_banned", + "ordinal": 3, + "type_info": "Bool" + } + ], + "nullable": [ + false, + false, + false, + false + ], + "parameters": { + "Left": [ + { + "Custom": { + "kind": { + "Composite": [ + [ + "label", + "Text" + ], + [ + "audience", + "Text" + ] + ] + }, + "name": "account_id" + } + }, + "Uuid", + "Bool", + "Bool", + "Uuid" + ] + } + }, + "query": "\n INSERT INTO ban_account_op (user_account, last_op_id, is_video_streaming_banned, is_collaboration_banned)\n VALUES ($1, $2, COALESCE($3, false), COALESCE($4, false))\n ON CONFLICT (user_account) DO UPDATE\n SET\n is_video_streaming_banned = COALESCE(EXCLUDED.is_video_streaming_banned, ban_account_op.is_video_streaming_banned),\n is_collaboration_banned = COALESCE(EXCLUDED.is_collaboration_banned, ban_account_op.is_collaboration_banned),\n last_op_id = EXCLUDED.last_op_id\n WHERE\n -- allow to 'complete' operation if there's no change in last_op_id\n -- or allow to do upsert without real changes so we can process\n -- the same message twice\n ban_account_op.last_op_id = EXCLUDED.last_op_id OR\n -- allow change op id if the previous operation is completed\n (\n ban_account_op.last_op_id = $5 AND\n ban_account_op.is_video_streaming_banned = true AND\n ban_account_op.is_collaboration_banned = true\n )\n RETURNING\n user_account AS \"user_account: _\",\n last_op_id,\n is_video_streaming_banned,\n is_collaboration_banned\n " + }, "6016a361f9fb26d49797872a086033a34a01b4f1038125486a47c0cc8713e209": { "describe": { "columns": [ @@ -1526,60 +1654,6 @@ }, "query": "\n SELECT\n id::text AS \"id!: String\"\n FROM class\n WHERE conference_room_id = $1\n " }, - "8cdcd79a99763311030cf8cdb85effaf8031104a08a3b118bdaaf33c4269980f": { - "describe": { - "columns": [ - { - "name": "user_account: _", - "ordinal": 0, - "type_info": { - "Custom": { - "kind": { - "Composite": [ - [ - "label", - "Text" - ], - [ - "audience", - "Text" - ] - ] - }, - "name": "account_id" - } - } - }, - { - "name": "last_op_id: _", - "ordinal": 1, - "type_info": "Uuid" - }, - { - "name": "video_complete", - "ordinal": 2, - "type_info": "Bool" - }, - { - "name": "event_access_complete", - "ordinal": 3, - "type_info": "Bool" - } - ], - "nullable": [ - false, - false, - false, - false - ], - "parameters": { - "Left": [ - "Record" - ] - } - }, - "query": "\n SELECT\n user_account AS \"user_account: _\",\n last_op_id AS \"last_op_id: _\",\n video_complete,\n event_access_complete\n FROM ban_account_op\n WHERE\n user_account = $1\n LIMIT 1;\n " - }, "a4fc28b7fa8a6a5f4051750a2447784a10a221632dbea080fe1d4c980de076fd": { "describe": { "columns": [ @@ -1753,80 +1827,6 @@ }, "query": "\n INSERT INTO recording (class_id, rtc_id, segments, modified_segments, stream_uri, started_at, adjusted_at, transcoded_at, created_by)\n VALUES ($1, $2, $3, $4, $5, NOW(), NOW(), NOW(), $6)\n RETURNING\n id,\n class_id,\n rtc_id,\n stream_uri,\n segments AS \"segments!: Option\",\n started_at,\n modified_segments AS \"modified_segments!: Option\",\n created_at,\n adjusted_at,\n transcoded_at,\n created_by AS \"created_by: AgentId\",\n deleted_at\n " }, - "ad5a4328c5367ea842463346d51759c429921081fb8c9c8e103970d77209ff10": { - "describe": { - "columns": [ - { - "name": "user_account: _", - "ordinal": 0, - "type_info": { - "Custom": { - "kind": { - "Composite": [ - [ - "label", - "Text" - ], - [ - "audience", - "Text" - ] - ] - }, - "name": "account_id" - } - } - }, - { - "name": "last_op_id", - "ordinal": 1, - "type_info": "Uuid" - }, - { - "name": "video_complete", - "ordinal": 2, - "type_info": "Bool" - }, - { - "name": "event_access_complete", - "ordinal": 3, - "type_info": "Bool" - } - ], - "nullable": [ - false, - false, - false, - false - ], - "parameters": { - "Left": [ - { - "Custom": { - "kind": { - "Composite": [ - [ - "label", - "Text" - ], - [ - "audience", - "Text" - ] - ] - }, - "name": "account_id" - } - }, - "Uuid", - "Bool", - "Bool", - "Uuid" - ] - } - }, - "query": "\n INSERT INTO ban_account_op (user_account, last_op_id, video_complete, event_access_complete)\n VALUES ($1, $2, COALESCE($3, false), COALESCE($4, false))\n ON CONFLICT (user_account) DO UPDATE\n SET\n video_complete = COALESCE(EXCLUDED.video_complete, ban_account_op.video_complete),\n event_access_complete = COALESCE(EXCLUDED.event_access_complete, ban_account_op.event_access_complete),\n last_op_id = EXCLUDED.last_op_id\n WHERE\n -- allow to 'complete' operation iff there's no change in last_op_id\n -- or allow to do upsert without real changes so we can process\n -- the same message twice\n ban_account_op.last_op_id = EXCLUDED.last_op_id OR\n -- allow change op id iff the previous operation is completed\n (\n ban_account_op.last_op_id = $5 AND\n ban_account_op.video_complete = true AND\n ban_account_op.event_access_complete = true\n )\n RETURNING\n user_account AS \"user_account: _\",\n last_op_id,\n video_complete,\n event_access_complete\n " - }, "c67188dde7672c71f7e14a0ef09047934fbf808e5541e1b35c88004f36c16c8b": { "describe": { "columns": [ diff --git a/src/app/api/v1/account/ban.rs b/src/app/api/v1/account/ban.rs index 12e75858..0aa852b4 100644 --- a/src/app/api/v1/account/ban.rs +++ b/src/app/api/v1/account/ban.rs @@ -64,7 +64,7 @@ pub async fn ban( return Err(AppErrorKind::OperationIdObsolete.into()); } - if !op.complete() { + if !op.is_completed() { return Err(AppErrorKind::OperationInProgress.into()); } } @@ -74,6 +74,7 @@ pub async fn ban( &mut conn, payload.ban, &class, + account_id, account_to_ban, payload.last_seen_op_id, ) diff --git a/src/app/stage/ban.rs b/src/app/stage/ban.rs index 49e81941..f4b33c44 100644 --- a/src/app/stage/ban.rs +++ b/src/app/stage/ban.rs @@ -1,5 +1,8 @@ use svc_events::{ - ban::{BanEventAccessCompleteV1, BanEventV1, BanIntentEventV1, BanVideoCompleteV1}, + ban::{ + BanAcceptedV1, BanCollaborationCompletedV1, BanCompletedV1, BanIntentV1, + BanVideoStreamingCompletedV1, + }, EventId, }; @@ -18,18 +21,17 @@ const ENTITY_TYPE: &str = "ban"; pub async fn start( ctx: &dyn AppContext, - intent: BanIntentEventV1, + intent: BanIntentV1, intent_id: EventId, ) -> Result<(), Error> { let event_id = (ENTITY_TYPE.to_owned(), intent_id.sequence_id()).into(); - let event = BanEventV1::from(intent); + let event = BanAcceptedV1::from(intent); nats::publish_event(ctx, event.classroom_id, &event_id, event.into()).await } pub async fn handle_video_complete( ctx: &dyn AppContext, - video_complete: BanVideoCompleteV1, - event_id: EventId, + video_streaming_banned: BanVideoStreamingCompletedV1, ) -> Result<(), HandleMsgFailure> { let mut conn = ctx .get_conn() @@ -37,9 +39,9 @@ pub async fn handle_video_complete( .error(AppErrorKind::DbConnAcquisitionFailed) .transient()?; - let op = ban_account_op::UpsertQuery::new_video_complete( - video_complete.user_account.clone(), - video_complete.op_id, + let op = ban_account_op::UpsertQuery::new_video_streaming_banned( + video_streaming_banned.user_account.clone(), + video_streaming_banned.op_id, ) .execute(&mut conn) .await @@ -48,17 +50,19 @@ pub async fn handle_video_complete( .ok_or(Error::from(AppErrorKind::OperationFailure)) .permanent()?; - if op.complete() { - // TODO: finish flow + if op.is_completed() { + let original_event_id = video_streaming_banned.parent.clone(); + finish(ctx, video_streaming_banned, original_event_id) + .await + .transient()?; } Ok(()) } -pub async fn handle_event_access_complete( +pub async fn handle_collaboration_banned( ctx: &dyn AppContext, - event_access_complete: BanEventAccessCompleteV1, - event_id: EventId, + collaboration_banned: BanCollaborationCompletedV1, ) -> Result<(), HandleMsgFailure> { let mut conn = ctx .get_conn() @@ -66,9 +70,9 @@ pub async fn handle_event_access_complete( .error(AppErrorKind::DbConnAcquisitionFailed) .transient()?; - let op = ban_account_op::UpsertQuery::new_event_access_complete( - event_access_complete.user_account.clone(), - event_access_complete.op_id, + let op = ban_account_op::UpsertQuery::new_collaboration_banned( + collaboration_banned.user_account.clone(), + collaboration_banned.op_id, ) .execute(&mut conn) .await @@ -77,9 +81,22 @@ pub async fn handle_event_access_complete( .ok_or(Error::from(AppErrorKind::OperationFailure)) .permanent()?; - if op.complete() { - // TODO: finish flow + if op.is_completed() { + let original_event_id = collaboration_banned.parent.clone(); + finish(ctx, collaboration_banned, original_event_id) + .await + .transient()?; } Ok(()) } + +async fn finish( + ctx: &dyn AppContext, + event: impl Into, + original_event_id: EventId, +) -> Result<(), Error> { + let event_id = (ENTITY_TYPE.to_owned(), original_event_id.sequence_id()).into(); + let event: BanCompletedV1 = event.into(); + nats::publish_event(ctx, event.classroom_id, &event_id, event.into()).await +} diff --git a/src/app/stage/ban_intent.rs b/src/app/stage/ban_intent.rs index 4b7bb7f1..6c79fa2c 100644 --- a/src/app/stage/ban_intent.rs +++ b/src/app/stage/ban_intent.rs @@ -1,6 +1,6 @@ use sqlx::PgConnection; use svc_authn::AccountId; -use svc_events::{ban::BanIntentEventV1, EventId}; +use svc_events::{ban::BanIntentV1, EventId}; use uuid::Uuid; use crate::{ @@ -21,23 +21,25 @@ pub async fn start( conn: &mut PgConnection, ban: bool, class: &db::class::Object, + sender: AccountId, user_account: AccountId, last_op_id: Uuid, ) -> Result<(), Error> { let event_id = get_next_event_id(conn).await?; - let event = BanIntentEventV1 { + let event = BanIntentV1 { ban, classroom_id: class.id(), user_account, last_op_id, new_op_id: Uuid::new_v4(), + sender, }; nats::publish_event(ctx, class.id(), &event_id, event.into()).await } pub async fn handle( ctx: &dyn AppContext, - intent: BanIntentEventV1, + intent: BanIntentV1, intent_id: EventId, ) -> Result<(), HandleMsgFailure> { let mut conn = ctx @@ -60,7 +62,7 @@ pub async fn handle( .await .error(AppErrorKind::DbQueryFailed) .transient()? - // failed to upsert -- we've lost the race + // failed to upsert -- we've lost the race -- need to send event here .ok_or(Error::from(AppErrorKind::OperationIdObsolete)) .permanent()?; diff --git a/src/app/stage/mod.rs b/src/app/stage/mod.rs index 78525556..76879a43 100644 --- a/src/app/stage/mod.rs +++ b/src/app/stage/mod.rs @@ -63,7 +63,7 @@ async fn do_route_msg( .permanent()?; let classroom_id = subject.classroom_id(); - let room = { + let _room = { let mut conn = ctx .get_conn() .await @@ -85,7 +85,7 @@ async fn do_route_msg( let headers = svc_nats_client::Headers::try_from(msg.headers.clone().unwrap_or_default()) .context("parse nats headers") .permanent()?; - let agent_id = headers.sender_id(); + let _agent_id = headers.sender_id(); let event_id = headers.event_id(); let r = match event { @@ -93,16 +93,11 @@ async fn do_route_msg( EventV1::BanIntent(intent) => { ban_intent::handle(ctx.as_ref(), intent, event_id.clone()).await } - EventV1::BanVideoComplete(video_complete) => { - ban::handle_video_complete(ctx.as_ref(), video_complete, event_id.clone()).await + EventV1::BanVideoStreamingCompleted(event) => { + ban::handle_video_complete(ctx.as_ref(), event).await } - EventV1::BanEventAccessComplete(event_access_complete) => { - ban::handle_event_access_complete( - ctx.as_ref(), - event_access_complete, - event_id.clone(), - ) - .await + EventV1::BanCollaborationCompleted(event) => { + ban::handle_collaboration_banned(ctx.as_ref(), event).await } _ => Ok(()), }, @@ -112,7 +107,7 @@ async fn do_route_msg( Ok(_) => Ok(()), Err(HandleMsgFailure::Transient(e)) => Err(HandleMsgFailure::Transient(anyhow!(e))), Err(HandleMsgFailure::Permanent(e)) => { - // TODO: send notification about error + e.notify_sentry(); Err(HandleMsgFailure::Permanent(anyhow!(e))) } } diff --git a/src/db/ban_account_op.rs b/src/db/ban_account_op.rs index 32ca83e3..5cfa80ac 100644 --- a/src/db/ban_account_op.rs +++ b/src/db/ban_account_op.rs @@ -5,13 +5,13 @@ use uuid::Uuid; pub struct Object { pub user_account: AccountId, pub last_op_id: Uuid, - pub video_complete: bool, - pub event_access_complete: bool, + pub is_video_streaming_banned: bool, + pub is_collaboration_banned: bool, } impl Object { - pub fn complete(&self) -> bool { - self.video_complete && self.event_access_complete + pub fn is_completed(&self) -> bool { + self.is_video_streaming_banned && self.is_collaboration_banned } } @@ -31,8 +31,8 @@ impl<'a> ReadQuery<'a> { SELECT user_account AS "user_account: _", last_op_id AS "last_op_id: _", - video_complete, - event_access_complete + is_video_streaming_banned, + is_collaboration_banned FROM ban_account_op WHERE user_account = $1 @@ -63,8 +63,8 @@ pub async fn get_next_seq_id(conn: &mut PgConnection) -> sqlx::Result /// Returns `None` if `last_op_id` in database differs. pub struct UpsertQuery { user_account: AccountId, - video_complete: Option, - event_access_complete: Option, + is_video_streaming_banned: Option, + is_collaboration_banned: Option, last_op_id: Uuid, new_op_id: Uuid, } @@ -73,28 +73,28 @@ impl UpsertQuery { pub fn new_operation(user_account: AccountId, last_op_id: Uuid, new_op_id: Uuid) -> Self { Self { user_account, - video_complete: None, - event_access_complete: None, + is_video_streaming_banned: None, + is_collaboration_banned: None, last_op_id, new_op_id, } } - pub fn new_video_complete(user_account: AccountId, op_id: Uuid) -> Self { + pub fn new_video_streaming_banned(user_account: AccountId, op_id: Uuid) -> Self { Self { user_account, - video_complete: Some(true), - event_access_complete: None, + is_video_streaming_banned: Some(true), + is_collaboration_banned: None, last_op_id: op_id, new_op_id: op_id, } } - pub fn new_event_access_complete(user_account: AccountId, op_id: Uuid) -> Self { + pub fn new_collaboration_banned(user_account: AccountId, op_id: Uuid) -> Self { Self { user_account, - video_complete: None, - event_access_complete: Some(true), + is_video_streaming_banned: None, + is_collaboration_banned: Some(true), last_op_id: op_id, new_op_id: op_id, } @@ -104,34 +104,34 @@ impl UpsertQuery { sqlx::query_as!( Object, r#" - INSERT INTO ban_account_op (user_account, last_op_id, video_complete, event_access_complete) + INSERT INTO ban_account_op (user_account, last_op_id, is_video_streaming_banned, is_collaboration_banned) VALUES ($1, $2, COALESCE($3, false), COALESCE($4, false)) ON CONFLICT (user_account) DO UPDATE SET - video_complete = COALESCE(EXCLUDED.video_complete, ban_account_op.video_complete), - event_access_complete = COALESCE(EXCLUDED.event_access_complete, ban_account_op.event_access_complete), - last_op_id = EXCLUDED.last_op_id + is_video_streaming_banned = COALESCE(EXCLUDED.is_video_streaming_banned, ban_account_op.is_video_streaming_banned), + is_collaboration_banned = COALESCE(EXCLUDED.is_collaboration_banned, ban_account_op.is_collaboration_banned), + last_op_id = EXCLUDED.last_op_id WHERE - -- allow to 'complete' operation iff there's no change in last_op_id + -- allow to 'complete' operation if there's no change in last_op_id -- or allow to do upsert without real changes so we can process -- the same message twice ban_account_op.last_op_id = EXCLUDED.last_op_id OR - -- allow change op id iff the previous operation is completed + -- allow change op id if the previous operation is completed ( - ban_account_op.last_op_id = $5 AND - ban_account_op.video_complete = true AND - ban_account_op.event_access_complete = true + ban_account_op.last_op_id = $5 AND + ban_account_op.is_video_streaming_banned = true AND + ban_account_op.is_collaboration_banned = true ) RETURNING user_account AS "user_account: _", last_op_id, - video_complete, - event_access_complete + is_video_streaming_banned, + is_collaboration_banned "#, self.user_account as AccountId, self.new_op_id, - self.video_complete, - self.event_access_complete, + self.is_video_streaming_banned, + self.is_collaboration_banned, self.last_op_id ) .fetch_optional(conn) From b65b7dc81c051b392712dd8c2e4ea5ebe1521498 Mon Sep 17 00:00:00 2001 From: Dmitry Shlagoff Date: Thu, 25 May 2023 18:22:08 +0200 Subject: [PATCH 13/35] Publish BanRejected (almost) --- src/app/stage/ban_intent.rs | 41 +++++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/src/app/stage/ban_intent.rs b/src/app/stage/ban_intent.rs index 6c79fa2c..c57a09b1 100644 --- a/src/app/stage/ban_intent.rs +++ b/src/app/stage/ban_intent.rs @@ -1,6 +1,9 @@ use sqlx::PgConnection; use svc_authn::AccountId; -use svc_events::{ban::BanIntentV1, EventId}; +use svc_events::{ + ban::{BanIntentV1, BanRejectedV1}, + EventId, +}; use uuid::Uuid; use crate::{ @@ -53,7 +56,7 @@ pub async fn handle( // processing as if nothing happened -- subsequent upserts will be successful, // attempts to schedule the same message will fail (dedup). - ban_account_op::UpsertQuery::new_operation( + let op = ban_account_op::UpsertQuery::new_operation( intent.user_account.clone(), intent.last_op_id, intent.new_op_id, @@ -61,18 +64,38 @@ pub async fn handle( .execute(&mut conn) .await .error(AppErrorKind::DbQueryFailed) - .transient()? - // failed to upsert -- we've lost the race -- need to send event here - .ok_or(Error::from(AppErrorKind::OperationIdObsolete)) - .permanent()?; + .transient()?; - super::ban::start(ctx, intent, intent_id) - .await - .transient()?; + match op { + Some(_) => { + super::ban::start(ctx, intent, intent_id) + .await + .transient()?; + } + // failed to upsert -- we've lost the race + None => { + reject(ctx, intent, &intent_id).await.transient()?; + } + } Ok(()) } +async fn reject( + ctx: &dyn AppContext, + event: BanIntentV1, + original_event_id: &EventId, +) -> Result<(), Error> { + let event = BanRejectedV1::from(event); + let event_id = ( + "ban-intent-rejected".to_owned(), + original_event_id.sequence_id(), + ) + .into(); + // TODO: publish as personal notification + nats::publish_event(ctx, event.classroom_id, &event_id, event.into()).await +} + async fn get_next_event_id(conn: &mut PgConnection) -> Result { let next_seq_id = ban_account_op::get_next_seq_id(conn) .await From b7ccd6630c64c36830e1ab61a72657af72f7d33d Mon Sep 17 00:00:00 2001 From: Dmitry Shlagoff Date: Thu, 25 May 2023 18:44:01 +0200 Subject: [PATCH 14/35] Use new event id format + re-arrange stage code a bit --- Cargo.lock | 4 +- src/app/api/v1/account/ban.rs | 2 +- src/app/stage/ban.rs | 123 ++++++++++++++++++++++++++++++++-- src/app/stage/ban_intent.rs | 107 ----------------------------- src/app/stage/mod.rs | 5 +- 5 files changed, 122 insertions(+), 119 deletions(-) delete mode 100644 src/app/stage/ban_intent.rs diff --git a/Cargo.lock b/Cargo.lock index b05fcd7a..43c2a990 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2783,7 +2783,7 @@ dependencies = [ [[package]] name = "svc-events" version = "0.3.0" -source = "git+https://github.com/foxford/svc-events?branch=ULMS-1896/ban-events#70aad419dd5bcdd9d6d001fe9d6e19a8546c74ba" +source = "git+https://github.com/foxford/svc-events?branch=ULMS-1896/ban-events#0beb9d5d128cd66481615d637188e5c7922f822b" dependencies = [ "serde", "serde_json", @@ -2795,7 +2795,7 @@ dependencies = [ [[package]] name = "svc-nats-client" version = "0.2.0" -source = "git+https://github.com/foxford/svc-nats-client/?branch=ULMS-1896/add-consumer-move-event-id#90aec7aef0379e771cc7ebe8a2bd8d55f656bb6e" +source = "git+https://github.com/foxford/svc-nats-client/?branch=ULMS-1896/add-consumer-move-event-id#3ac5ec6f41e0635a63a89e7070741a1b5bff3421" dependencies = [ "anyhow", "async-nats", diff --git a/src/app/api/v1/account/ban.rs b/src/app/api/v1/account/ban.rs index 0aa852b4..2f2f11af 100644 --- a/src/app/api/v1/account/ban.rs +++ b/src/app/api/v1/account/ban.rs @@ -69,7 +69,7 @@ pub async fn ban( } } - stage::ban_intent::start( + stage::ban::save_intent( ctx.as_ref(), &mut conn, payload.ban, diff --git a/src/app/stage/ban.rs b/src/app/stage/ban.rs index f4b33c44..8e1b2af2 100644 --- a/src/app/stage/ban.rs +++ b/src/app/stage/ban.rs @@ -1,10 +1,13 @@ +use sqlx::PgConnection; +use svc_authn::AccountId; use svc_events::{ ban::{ - BanAcceptedV1, BanCollaborationCompletedV1, BanCompletedV1, BanIntentV1, + BanAcceptedV1, BanCollaborationCompletedV1, BanCompletedV1, BanIntentV1, BanRejectedV1, BanVideoStreamingCompletedV1, }, EventId, }; +use uuid::Uuid; use crate::{ app::{ @@ -12,24 +15,127 @@ use crate::{ AppContext, }, clients::nats, - db::ban_account_op, + db::{self, ban_account_op}, }; use super::{FailureKind, HandleMsgFailure}; const ENTITY_TYPE: &str = "ban"; -pub async fn start( +const INTENT_OP: &str = "intent"; +const ACCEPTED_OP: &str = "accepted"; +const REJECTED_OP: &str = "rejected"; +const COMPLETED_OP: &str = "completed"; + +pub async fn save_intent( + ctx: &dyn AppContext, + conn: &mut PgConnection, + ban: bool, + class: &db::class::Object, + sender: AccountId, + user_account: AccountId, + last_op_id: Uuid, +) -> Result<(), Error> { + let event_id = get_next_event_id(conn).await?; + let event = BanIntentV1 { + ban, + classroom_id: class.id(), + user_account, + last_op_id, + new_op_id: Uuid::new_v4(), + sender, + }; + nats::publish_event(ctx, class.id(), &event_id, event.into()).await +} + +async fn get_next_event_id(conn: &mut PgConnection) -> Result { + let next_seq_id = ban_account_op::get_next_seq_id(conn) + .await + .error(AppErrorKind::DbQueryFailed)?; + + let event_id = ( + ENTITY_TYPE.to_owned(), + INTENT_OP.to_owned(), + next_seq_id.value, + ) + .into(); + + Ok(event_id) +} + +pub async fn handle_intent( + ctx: &dyn AppContext, + intent: BanIntentV1, + intent_id: EventId, +) -> Result<(), HandleMsgFailure> { + let mut conn = ctx + .get_conn() + .await + .error(AppErrorKind::DbConnAcquisitionFailed) + .transient()?; + + // We need to update db here first, then schedule next stage, then acknowledge + // current message. In case if we fail somewhere in between, we can continue + // processing as if nothing happened -- subsequent upserts will be successful, + // attempts to schedule the same message will fail (dedup). + + let op = ban_account_op::UpsertQuery::new_operation( + intent.user_account.clone(), + intent.last_op_id, + intent.new_op_id, + ) + .execute(&mut conn) + .await + .error(AppErrorKind::DbQueryFailed) + .transient()?; + + match op { + Some(_) => { + super::ban::accept(ctx, intent, intent_id) + .await + .transient()?; + } + // failed to upsert -- we've lost the race + None => { + reject(ctx, intent, &intent_id).await.transient()?; + } + } + + Ok(()) +} + +pub async fn accept( ctx: &dyn AppContext, intent: BanIntentV1, intent_id: EventId, ) -> Result<(), Error> { - let event_id = (ENTITY_TYPE.to_owned(), intent_id.sequence_id()).into(); + let event_id = ( + ENTITY_TYPE.to_owned(), + ACCEPTED_OP.to_owned(), + intent_id.sequence_id(), + ) + .into(); let event = BanAcceptedV1::from(intent); nats::publish_event(ctx, event.classroom_id, &event_id, event.into()).await } -pub async fn handle_video_complete( +async fn reject( + ctx: &dyn AppContext, + event: BanIntentV1, + original_event_id: &EventId, +) -> Result<(), Error> { + let event = BanRejectedV1::from(event); + let event_id = ( + ENTITY_TYPE.to_owned(), + REJECTED_OP.to_owned(), + original_event_id.sequence_id(), + ) + .into(); + // TODO: publish as personal notification + nats::publish_event(ctx, event.classroom_id, &event_id, event.into()).await +} + +pub async fn handle_video_streaming_banned( ctx: &dyn AppContext, video_streaming_banned: BanVideoStreamingCompletedV1, ) -> Result<(), HandleMsgFailure> { @@ -96,7 +202,12 @@ async fn finish( event: impl Into, original_event_id: EventId, ) -> Result<(), Error> { - let event_id = (ENTITY_TYPE.to_owned(), original_event_id.sequence_id()).into(); + let event_id = ( + ENTITY_TYPE.to_owned(), + COMPLETED_OP.to_owned(), + original_event_id.sequence_id(), + ) + .into(); let event: BanCompletedV1 = event.into(); nats::publish_event(ctx, event.classroom_id, &event_id, event.into()).await } diff --git a/src/app/stage/ban_intent.rs b/src/app/stage/ban_intent.rs deleted file mode 100644 index c57a09b1..00000000 --- a/src/app/stage/ban_intent.rs +++ /dev/null @@ -1,107 +0,0 @@ -use sqlx::PgConnection; -use svc_authn::AccountId; -use svc_events::{ - ban::{BanIntentV1, BanRejectedV1}, - EventId, -}; -use uuid::Uuid; - -use crate::{ - app::{ - error::{Error, ErrorExt, ErrorKind as AppErrorKind}, - AppContext, - }, - clients::nats, - db::{self, ban_account_op}, -}; - -use super::{FailureKind, HandleMsgFailure}; - -const ENTITY_TYPE: &str = "ban-intent"; - -pub async fn start( - ctx: &dyn AppContext, - conn: &mut PgConnection, - ban: bool, - class: &db::class::Object, - sender: AccountId, - user_account: AccountId, - last_op_id: Uuid, -) -> Result<(), Error> { - let event_id = get_next_event_id(conn).await?; - let event = BanIntentV1 { - ban, - classroom_id: class.id(), - user_account, - last_op_id, - new_op_id: Uuid::new_v4(), - sender, - }; - nats::publish_event(ctx, class.id(), &event_id, event.into()).await -} - -pub async fn handle( - ctx: &dyn AppContext, - intent: BanIntentV1, - intent_id: EventId, -) -> Result<(), HandleMsgFailure> { - let mut conn = ctx - .get_conn() - .await - .error(AppErrorKind::DbConnAcquisitionFailed) - .transient()?; - - // We need to update db here first, then schedule next stage, then acknowledge - // current message. In case if we fail somewhere in between, we can continue - // processing as if nothing happened -- subsequent upserts will be successful, - // attempts to schedule the same message will fail (dedup). - - let op = ban_account_op::UpsertQuery::new_operation( - intent.user_account.clone(), - intent.last_op_id, - intent.new_op_id, - ) - .execute(&mut conn) - .await - .error(AppErrorKind::DbQueryFailed) - .transient()?; - - match op { - Some(_) => { - super::ban::start(ctx, intent, intent_id) - .await - .transient()?; - } - // failed to upsert -- we've lost the race - None => { - reject(ctx, intent, &intent_id).await.transient()?; - } - } - - Ok(()) -} - -async fn reject( - ctx: &dyn AppContext, - event: BanIntentV1, - original_event_id: &EventId, -) -> Result<(), Error> { - let event = BanRejectedV1::from(event); - let event_id = ( - "ban-intent-rejected".to_owned(), - original_event_id.sequence_id(), - ) - .into(); - // TODO: publish as personal notification - nats::publish_event(ctx, event.classroom_id, &event_id, event.into()).await -} - -async fn get_next_event_id(conn: &mut PgConnection) -> Result { - let next_seq_id = ban_account_op::get_next_seq_id(conn) - .await - .error(AppErrorKind::DbQueryFailed)?; - - let event_id = (ENTITY_TYPE.to_owned(), next_seq_id.value).into(); - - Ok(event_id) -} diff --git a/src/app/stage/mod.rs b/src/app/stage/mod.rs index 76879a43..672c6694 100644 --- a/src/app/stage/mod.rs +++ b/src/app/stage/mod.rs @@ -9,7 +9,6 @@ use crate::db; use super::AppContext; pub mod ban; -pub mod ban_intent; pub async fn route_message( ctx: Arc, @@ -91,10 +90,10 @@ async fn do_route_msg( let r = match event { Event::V1(e) => match e { EventV1::BanIntent(intent) => { - ban_intent::handle(ctx.as_ref(), intent, event_id.clone()).await + ban::handle_intent(ctx.as_ref(), intent, event_id.clone()).await } EventV1::BanVideoStreamingCompleted(event) => { - ban::handle_video_complete(ctx.as_ref(), event).await + ban::handle_video_streaming_banned(ctx.as_ref(), event).await } EventV1::BanCollaborationCompleted(event) => { ban::handle_collaboration_banned(ctx.as_ref(), event).await From 92c57f9fc07a6d1c87c2a4e969bb8d487a80da9d Mon Sep 17 00:00:00 2001 From: Dmitry Shlagoff Date: Thu, 25 May 2023 19:06:37 +0200 Subject: [PATCH 15/35] Change op_id: Uuid -> i64 --- Cargo.lock | 2 +- ...0230517104549_add_ban_account_op_table.sql | 2 +- sqlx-data.json | 8 +++--- src/app/api/v1/account/ban.rs | 3 +-- src/app/stage/ban.rs | 27 ++++++++++++------- src/db/ban_account_op.rs | 13 +++++---- 6 files changed, 31 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 43c2a990..11a231c1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2783,7 +2783,7 @@ dependencies = [ [[package]] name = "svc-events" version = "0.3.0" -source = "git+https://github.com/foxford/svc-events?branch=ULMS-1896/ban-events#0beb9d5d128cd66481615d637188e5c7922f822b" +source = "git+https://github.com/foxford/svc-events?branch=ULMS-1896/ban-events#e3150f41e09aad12494f7e8016482546681f54d4" dependencies = [ "serde", "serde_json", diff --git a/migrations/20230517104549_add_ban_account_op_table.sql b/migrations/20230517104549_add_ban_account_op_table.sql index f8bb2036..07f11996 100644 --- a/migrations/20230517104549_add_ban_account_op_table.sql +++ b/migrations/20230517104549_add_ban_account_op_table.sql @@ -1,6 +1,6 @@ CREATE TABLE IF NOT EXISTS ban_account_op ( user_account account_id PRIMARY KEY, - last_op_id uuid NOT NULL, + last_op_id bigint NOT NULL, is_video_streaming_banned boolean NOT NULL DEFAULT false, is_collaboration_banned boolean NOT NULL DEFAULT false ); diff --git a/sqlx-data.json b/sqlx-data.json index c693b2dc..94011d06 100644 --- a/sqlx-data.json +++ b/sqlx-data.json @@ -808,7 +808,7 @@ { "name": "last_op_id: _", "ordinal": 1, - "type_info": "Uuid" + "type_info": "Int8" }, { "name": "is_video_streaming_banned", @@ -862,7 +862,7 @@ { "name": "last_op_id", "ordinal": 1, - "type_info": "Uuid" + "type_info": "Int8" }, { "name": "is_video_streaming_banned", @@ -900,10 +900,10 @@ "name": "account_id" } }, - "Uuid", + "Int8", "Bool", "Bool", - "Uuid" + "Int8" ] } }, diff --git a/src/app/api/v1/account/ban.rs b/src/app/api/v1/account/ban.rs index 2f2f11af..d1f734b0 100644 --- a/src/app/api/v1/account/ban.rs +++ b/src/app/api/v1/account/ban.rs @@ -22,8 +22,7 @@ use super::AppResult; #[derive(Deserialize)] pub struct BanPayload { - // TODO: maybe Option? - last_seen_op_id: Uuid, + last_seen_op_id: i64, ban: bool, class_id: Uuid, } diff --git a/src/app/stage/ban.rs b/src/app/stage/ban.rs index 8e1b2af2..205debb8 100644 --- a/src/app/stage/ban.rs +++ b/src/app/stage/ban.rs @@ -7,7 +7,6 @@ use svc_events::{ }, EventId, }; -use uuid::Uuid; use crate::{ app::{ @@ -34,7 +33,7 @@ pub async fn save_intent( class: &db::class::Object, sender: AccountId, user_account: AccountId, - last_op_id: Uuid, + last_op_id: i64, ) -> Result<(), Error> { let event_id = get_next_event_id(conn).await?; let event = BanIntentV1 { @@ -42,7 +41,6 @@ pub async fn save_intent( classroom_id: class.id(), user_account, last_op_id, - new_op_id: Uuid::new_v4(), sender, }; nats::publish_event(ctx, class.id(), &event_id, event.into()).await @@ -82,7 +80,7 @@ pub async fn handle_intent( let op = ban_account_op::UpsertQuery::new_operation( intent.user_account.clone(), intent.last_op_id, - intent.new_op_id, + intent_id.sequence_id(), ) .execute(&mut conn) .await @@ -115,20 +113,31 @@ pub async fn accept( intent_id.sequence_id(), ) .into(); - let event = BanAcceptedV1::from(intent); + let event = BanAcceptedV1 { + ban: intent.ban, + classroom_id: intent.classroom_id, + user_account: intent.user_account, + op_id: intent_id.sequence_id(), + }; + nats::publish_event(ctx, event.classroom_id, &event_id, event.into()).await } async fn reject( ctx: &dyn AppContext, - event: BanIntentV1, - original_event_id: &EventId, + intent: BanIntentV1, + intent_id: &EventId, ) -> Result<(), Error> { - let event = BanRejectedV1::from(event); + let event = BanRejectedV1 { + ban: intent.ban, + classroom_id: intent.classroom_id, + user_account: intent.user_account, + op_id: intent_id.sequence_id(), + }; let event_id = ( ENTITY_TYPE.to_owned(), REJECTED_OP.to_owned(), - original_event_id.sequence_id(), + intent_id.sequence_id(), ) .into(); // TODO: publish as personal notification diff --git a/src/db/ban_account_op.rs b/src/db/ban_account_op.rs index 5cfa80ac..01b08896 100644 --- a/src/db/ban_account_op.rs +++ b/src/db/ban_account_op.rs @@ -1,10 +1,9 @@ use sqlx::PgConnection; use svc_authn::AccountId; -use uuid::Uuid; pub struct Object { pub user_account: AccountId, - pub last_op_id: Uuid, + pub last_op_id: i64, pub is_video_streaming_banned: bool, pub is_collaboration_banned: bool, } @@ -65,12 +64,12 @@ pub struct UpsertQuery { user_account: AccountId, is_video_streaming_banned: Option, is_collaboration_banned: Option, - last_op_id: Uuid, - new_op_id: Uuid, + last_op_id: i64, + new_op_id: i64, } impl UpsertQuery { - pub fn new_operation(user_account: AccountId, last_op_id: Uuid, new_op_id: Uuid) -> Self { + pub fn new_operation(user_account: AccountId, last_op_id: i64, new_op_id: i64) -> Self { Self { user_account, is_video_streaming_banned: None, @@ -80,7 +79,7 @@ impl UpsertQuery { } } - pub fn new_video_streaming_banned(user_account: AccountId, op_id: Uuid) -> Self { + pub fn new_video_streaming_banned(user_account: AccountId, op_id: i64) -> Self { Self { user_account, is_video_streaming_banned: Some(true), @@ -90,7 +89,7 @@ impl UpsertQuery { } } - pub fn new_collaboration_banned(user_account: AccountId, op_id: Uuid) -> Self { + pub fn new_collaboration_banned(user_account: AccountId, op_id: i64) -> Self { Self { user_account, is_video_streaming_banned: None, From 98d1882e930ff7035d8e3f9db2e81f1578c3d53e Mon Sep 17 00:00:00 2001 From: Dmitry <0nkery@users.noreply.github.com> Date: Fri, 2 Jun 2023 12:18:41 +0200 Subject: [PATCH 16/35] Update src/app/error.rs Co-authored-by: Grachev Mikhail --- src/app/error.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/error.rs b/src/app/error.rs index 3c549c79..53786828 100644 --- a/src/app/error.rs +++ b/src/app/error.rs @@ -42,7 +42,7 @@ pub enum ErrorKind { ClassAlreadyEstablished, OperationIdObsolete, OperationInProgress, - OperationFailure, + OperationFailed, NatsPublishFailed, NatsClientNotFound, } From aa6bed07192b24b726cb94961911a832ae54cc3b Mon Sep 17 00:00:00 2001 From: Dmitry Shlagoff Date: Fri, 2 Jun 2023 12:20:07 +0200 Subject: [PATCH 17/35] Fix OperationFailed occurrences --- src/app/error.rs | 4 ++-- src/app/stage/ban.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/app/error.rs b/src/app/error.rs index 53786828..1c589c95 100644 --- a/src/app/error.rs +++ b/src/app/error.rs @@ -208,9 +208,9 @@ impl From for ErrorKindProperties { title: "Operation is not completed yet, retry later", is_notify_sentry: false, }, - ErrorKind::OperationFailure => ErrorKindProperties { + ErrorKind::OperationFailed => ErrorKindProperties { status: ResponseStatus::INTERNAL_SERVER_ERROR, - kind: "operation_failure", + kind: "operation_failed", title: "Operation failed really bad", is_notify_sentry: true, }, diff --git a/src/app/stage/ban.rs b/src/app/stage/ban.rs index 205debb8..6997eff5 100644 --- a/src/app/stage/ban.rs +++ b/src/app/stage/ban.rs @@ -162,7 +162,7 @@ pub async fn handle_video_streaming_banned( .await .error(AppErrorKind::DbQueryFailed) .transient()? - .ok_or(Error::from(AppErrorKind::OperationFailure)) + .ok_or(Error::from(AppErrorKind::OperationFailed)) .permanent()?; if op.is_completed() { @@ -193,7 +193,7 @@ pub async fn handle_collaboration_banned( .await .error(AppErrorKind::DbQueryFailed) .transient()? - .ok_or(Error::from(AppErrorKind::OperationFailure)) + .ok_or(Error::from(AppErrorKind::OperationFailed)) .permanent()?; if op.is_completed() { From 33f3e5a1586c3b066c4ff6334d70e763242f4b83 Mon Sep 17 00:00:00 2001 From: Dmitry Shlagoff Date: Fri, 2 Jun 2023 12:20:55 +0200 Subject: [PATCH 18/35] Apply suggestion from review --- src/app/api/v1/account/ban.rs | 2 +- src/db/ban_account_op.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/api/v1/account/ban.rs b/src/app/api/v1/account/ban.rs index d1f734b0..3ee0718e 100644 --- a/src/app/api/v1/account/ban.rs +++ b/src/app/api/v1/account/ban.rs @@ -53,7 +53,7 @@ pub async fn ban( .await .error(AppErrorKind::DbConnAcquisitionFailed)?; - let last_ban_account_op = ban_account_op::ReadQuery::by_id(&account_to_ban) + let last_ban_account_op = ban_account_op::ReadQuery::by_account_id(&account_to_ban) .execute(&mut conn) .await .error(AppErrorKind::DbQueryFailed)?; diff --git a/src/db/ban_account_op.rs b/src/db/ban_account_op.rs index 01b08896..b4f65dae 100644 --- a/src/db/ban_account_op.rs +++ b/src/db/ban_account_op.rs @@ -19,7 +19,7 @@ pub struct ReadQuery<'a> { } impl<'a> ReadQuery<'a> { - pub fn by_id(user_account: &'a AccountId) -> Self { + pub fn by_account_id(user_account: &'a AccountId) -> Self { Self { user_account } } From ca107f7a8498e389176f4fd7276b3eda39b1cf4b Mon Sep 17 00:00:00 2001 From: Mikhail Grachev Date: Mon, 5 Jun 2023 18:48:27 +0300 Subject: [PATCH 19/35] Update dependencies Signed-off-by: Mikhail Grachev --- .github/workflows/check.yml | 2 +- Cargo.lock | 1009 ++++++++++------- Cargo.toml | 42 +- docker/Dockerfile | 2 +- docker/migration.dockerfile | 2 +- rust-toolchain.toml | 2 +- src/app/api/v1/class/read.rs | 6 +- src/app/error.rs | 4 +- src/app/http.rs | 12 +- .../postprocessing_strategy/shared_helpers.rs | 5 +- src/clients/event/types.rs | 2 +- src/db/class/mod.rs | 4 +- src/db/mod.rs | 2 +- src/serde.rs | 11 +- 14 files changed, 628 insertions(+), 477 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 20502738..98ad856c 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -27,7 +27,7 @@ jobs: steps: - uses: actions/checkout@v3 - - uses: dtolnay/rust-toolchain@1.64.0 + - uses: dtolnay/rust-toolchain@1.70.0 with: components: clippy, rustfmt - uses: Swatinem/rust-cache@v1 diff --git a/Cargo.lock b/Cargo.lock index a348eaa4..6049fca4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -28,6 +28,17 @@ dependencies = [ "version_check", ] +[[package]] +name = "ahash" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", +] + [[package]] name = "aho-corasick" version = "0.7.18" @@ -38,19 +49,25 @@ dependencies = [ ] [[package]] -name = "ansi_term" -version = "0.12.1" +name = "android-tzdata" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" dependencies = [ - "winapi", + "libc", ] [[package]] name = "anyhow" -version = "1.0.41" +version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15af2628f6890fe2609a3b91bef4c83450512802e59489f9c1cb1fa5df064a61" +checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" dependencies = [ "backtrace", ] @@ -74,35 +91,35 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.50" +version = "0.1.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b98e84bbb4cbcdd97da190ba0c58a1bb0de2c1fdf67d159e192ed766aeca722" +checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" dependencies = [ "proc-macro2", - "quote 1.0.21", - "syn 1.0.99", + "quote 1.0.28", + "syn 2.0.18", ] [[package]] name = "atoi" -version = "0.4.0" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "616896e05fc0e2649463a93a15183c6a16bf03413a7af88ef1285ddedfa9cda5" +checksum = "d7c57d12312ff59c811c0643f4d80830505833c9ffaebd193d819392b265be8e" dependencies = [ "num-traits", ] [[package]] name = "autocfg" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "axum" -version = "0.5.14" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c943a505c17b494638a38a9af129067f760c9c06794b9f57d499266909be8e72" +checksum = "f8175979259124331c1d7bf6586ee7e0da434155e4b2d48ec2c8386281d8df39" dependencies = [ "async-trait", "axum-core", @@ -113,28 +130,29 @@ dependencies = [ "http", "http-body", "hyper", - "itoa 1.0.1", + "itoa 1.0.6", "matchit", "memchr", "mime", - "percent-encoding 2.1.0", + "percent-encoding 2.2.0", "pin-project-lite", + "rustversion", "serde", "serde_json", + "serde_path_to_error", "serde_urlencoded", "sync_wrapper", "tokio", "tower", - "tower-http 0.3.4", "tower-layer", "tower-service", ] [[package]] name = "axum-core" -version = "0.2.7" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4f44a0e6200e9d11a1cdc989e4b358f6e3d354fbf48478f345a17f4e43f8635" +checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" dependencies = [ "async-trait", "bytes", @@ -142,6 +160,9 @@ dependencies = [ "http", "http-body", "mime", + "rustversion", + "tower-layer", + "tower-service", ] [[package]] @@ -171,13 +192,19 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +[[package]] +name = "base64" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" + [[package]] name = "bigdecimal" -version = "0.2.2" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1e50562e37200edf7c6c43e54a08e64a5553bfb59d9c297d5572512aa517256" +checksum = "a6773ddc0eafc0e509fb60e48dff7f450f8e674a0686ae8605e8d9901bd5eefa" dependencies = [ - "num-bigint 0.3.3", + "num-bigint 0.4.3", "num-integer", "num-traits", ] @@ -223,9 +250,9 @@ checksum = "631ae5198c9be5e753e5cc215e1bd73c2b466a3565173db433f52bb9d3e66dba" [[package]] name = "cc" -version = "1.0.68" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a72c244c1ff497a746a7e1fb3d14bd08420ecda70c8f25c7112f2781652d787" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" [[package]] name = "cfg-if" @@ -235,15 +262,17 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.19" +version = "0.4.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" dependencies = [ - "libc", - "num-integer", + "android-tzdata", + "iana-time-zone", + "js-sys", "num-traits", "serde", "time 0.1.43", + "wasm-bindgen", "winapi", ] @@ -268,9 +297,9 @@ dependencies = [ [[package]] name = "config" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11f1667b8320afa80d69d8bbe40830df2c8a06003d86f73d8e003b2c48df416d" +checksum = "d379af7f68bfc21714c6c7dea883544201741d2ce8274bb12fa54f89507f52a7" dependencies = [ "async-trait", "lazy_static", @@ -292,9 +321,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.2" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "cpufeatures" @@ -307,18 +336,18 @@ dependencies = [ [[package]] name = "crc" -version = "2.1.0" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49fc9a695bca7f35f5f4c15cddc84415f66a74ea78eef08e90c5024f2b540e23" +checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe" dependencies = [ "crc-catalog", ] [[package]] name = "crc-catalog" -version = "1.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccaeedb56da03b09f598226e25e80088cb4cd25f316e6e4df7d695f0feeb1403" +checksum = "9cace84e55f07e7301bae1c519df89cdad8cc3cd868413d3fdbdeca9ff3db484" [[package]] name = "crossbeam-channel" @@ -372,35 +401,48 @@ dependencies = [ [[package]] name = "debugid" -version = "0.7.2" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91cf5a8c2f2097e2a32627123508635d47ce10563d999ec1a95addf08b502ba" +checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" dependencies = [ "serde", - "uuid 0.8.2", + "uuid 1.3.3", ] [[package]] -name = "debugid" -version = "0.8.0" +name = "diesel" +version = "1.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" +checksum = "b28135ecf6b7d446b43e27e225622a038cc4e2930a1022f51cdb97ada19b8e4d" dependencies = [ - "serde", - "uuid 1.1.2", + "bitflags", + "byteorder", + "diesel_derives", + "pq-sys", ] [[package]] -name = "difference" -version = "2.0.0" +name = "diesel_derives" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45f5098f628d02a7a0f68ddba586fb61e80edec3bdc1be3b921f4ceec60858d3" +dependencies = [ + "proc-macro2", + "quote 1.0.28", + "syn 1.0.109", +] + +[[package]] +name = "difflib" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" +checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" [[package]] name = "digest" -version = "0.10.3" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", @@ -429,7 +471,7 @@ dependencies = [ [[package]] name = "dispatcher" -version = "0.4.21" +version = "0.4.22" dependencies = [ "anyhow", "async-trait", @@ -447,15 +489,15 @@ dependencies = [ "lazy_static", "mockall", "once_cell", - "parking_lot 0.12.0", - "percent-encoding 2.1.0", + "parking_lot 0.12.1", + "percent-encoding 2.2.0", "pin-utils", "prometheus", "prometheus-static-metric", "quaint", "rand", "reqwest", - "sentry 0.27.0", + "sentry", "serde", "serde_derive", "serde_json", @@ -466,7 +508,7 @@ dependencies = [ "svc-agent", "svc-authn", "svc-authz", - "svc-error 0.4.0", + "svc-error", "svc-utils", "tokio", "tower", @@ -477,8 +519,8 @@ dependencies = [ "tracing-futures", "tracing-log", "tracing-subscriber", - "url 2.2.2", - "uuid 0.8.2", + "url 2.3.1", + "uuid 1.3.3", "vec1", ] @@ -488,11 +530,17 @@ version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" +[[package]] +name = "dotenvy" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" + [[package]] name = "downcast" -version = "0.10.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bb454f0228b18c7f4c3b0ebbee346ed9c52e7443b0999cd543ff3571205701d" +checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" [[package]] name = "dtoa" @@ -524,11 +572,23 @@ version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" +[[package]] +name = "findshlibs" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40b9e59cd0f7e0806cca4be089683ecb6434e602038df21fe6bf6711b2f07f64" +dependencies = [ + "cc", + "lazy_static", + "libc", + "winapi", +] + [[package]] name = "float-cmp" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1267f4ac4f343772758f7b1bdcbe767c218bbab93bb432acbf5162bbf85a6c4" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" dependencies = [ "num-traits", ] @@ -556,25 +616,24 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" dependencies = [ - "matches", - "percent-encoding 2.1.0", + "percent-encoding 2.2.0", ] [[package]] name = "fragile" -version = "1.0.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69a039c3498dc930fe810151a34ba0c1c70b02b8625035592e74432f678591f2" +checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" [[package]] name = "futures" -version = "0.3.24" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f21eda599937fba36daeb58a22e8f5cee2d14c4a17b5b7739c7c8e5e3b8230c" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" dependencies = [ "futures-channel", "futures-core", @@ -587,9 +646,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.24" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" dependencies = [ "futures-core", "futures-sink", @@ -597,15 +656,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.24" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" [[package]] name = "futures-executor" -version = "0.3.24" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ff63c23854bee61b6e9cd331d523909f238fc7636290b96826e9cfa5faa00ab" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" dependencies = [ "futures-core", "futures-task", @@ -625,38 +684,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.24" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbf4d2a7a308fd4578637c0b17c7e1c7ba127b8f6ba00b29f717e9655d85eb68" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" [[package]] name = "futures-macro" -version = "0.3.24" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42cd15d1c7456c04dbdf7e88bcd69760d74f3a798d6444e16974b505b0e62f17" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", - "quote 1.0.21", - "syn 1.0.99", + "quote 1.0.28", + "syn 2.0.18", ] [[package]] name = "futures-sink" -version = "0.3.24" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b20ba5a92e727ba30e72834706623d94ac93a725410b6a6b6fbc1b07f7ba56" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" [[package]] name = "futures-task" -version = "0.3.24" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" [[package]] name = "futures-util" -version = "0.3.24" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ "futures-channel", "futures-core", @@ -699,9 +758,9 @@ checksum = "0e4075386626662786ddb0ec9081e7c7eeb1ba31951f447ca780ef9f5d568189" [[package]] name = "h2" -version = "0.3.12" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62eeb471aa3e3c9197aa4bfeabfe02982f6dc96f750486c0bb0009ac58b26d2b" +checksum = "d357c7ae988e7d2182f7d7871d0b963962420b0678b0997ce7de72001aeab782" dependencies = [ "bytes", "fnv", @@ -724,20 +783,20 @@ checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" [[package]] name = "hashbrown" -version = "0.11.2" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" dependencies = [ - "ahash", + "ahash 0.8.3", ] [[package]] name = "hashlink" -version = "0.7.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7249a3129cbc1ffccd74857f81464a323a152173cdb134e0fd81bc803b29facf" +checksum = "0761a1b9491c4f2e3d66aa0f62d0fba0af9a0e2852e4d48ea506632a4b56e6aa" dependencies = [ - "hashbrown 0.11.2", + "hashbrown 0.13.2", ] [[package]] @@ -751,9 +810,9 @@ dependencies = [ [[package]] name = "headers" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cff78e5788be1e0ab65b04d306b2ed5092c815ec97ec70f4ebd5aee158aa55d" +checksum = "f3e372db8e5c0d213e0cd0b9be18be2aca3d44cf2fe30a9d46a65581cd454584" dependencies = [ "base64 0.13.0", "bitflags", @@ -762,7 +821,7 @@ dependencies = [ "http", "httpdate", "mime", - "sha-1", + "sha1 0.10.5", ] [[package]] @@ -829,13 +888,13 @@ dependencies = [ [[package]] name = "http" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" dependencies = [ "bytes", "fnv", - "itoa 1.0.1", + "itoa 1.0.6", ] [[package]] @@ -857,9 +916,9 @@ checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29" [[package]] name = "httparse" -version = "1.6.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9100414882e15fb7feccb4897e5f0ff0ff1ca7d1a86a23208ada4d7a18e6c6c4" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" [[package]] name = "httpdate" @@ -885,9 +944,9 @@ dependencies = [ [[package]] name = "hyper" -version = "0.14.20" +version = "0.14.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac" +checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" dependencies = [ "bytes", "futures-channel", @@ -898,7 +957,7 @@ dependencies = [ "http-body", "httparse", "httpdate", - "itoa 1.0.1", + "itoa 1.0.6", "pin-project-lite", "socket2", "tokio", @@ -920,6 +979,29 @@ dependencies = [ "tokio-native-tls", ] +[[package]] +name = "iana-time-zone" +version = "0.1.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "idna" version = "0.1.5" @@ -933,11 +1015,10 @@ dependencies = [ [[package]] name = "idna" -version = "0.2.3" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" dependencies = [ - "matches", "unicode-bidi", "unicode-normalization", ] @@ -984,9 +1065,9 @@ checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" [[package]] name = "itoa" -version = "1.0.1" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" [[package]] name = "js-sys" @@ -1019,9 +1100,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.121" +version = "0.2.144" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efaa7b300f3b5fe8eb6bf21ce3895e1751d9665086af2d64b42f19701015ff4f" +checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" [[package]] name = "lock_api" @@ -1034,12 +1115,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.14" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" -dependencies = [ - "cfg-if", -] +checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de" [[package]] name = "match_cfg" @@ -1064,9 +1142,9 @@ checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" [[package]] name = "matchit" -version = "0.5.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73cbba799671b762df5a175adf59ce145165747bb891505c43d09aefbbf38beb" +checksum = "b87248edafb776e59e6ee64a79086f65890d3510f2c656c000bf2a7e8a0aea40" [[package]] name = "md-5" @@ -1122,32 +1200,20 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.2" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52da4364ffb0e4fe33a9841a98a3f3014fb964045ce4f7a45a398243c8d6b0c9" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", - "log", - "miow", - "ntapi", "wasi 0.11.0+wasi-snapshot-preview1", - "winapi", -] - -[[package]] -name = "miow" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" -dependencies = [ - "winapi", + "windows-sys 0.48.0", ] [[package]] name = "mockall" -version = "0.9.1" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18d614ad23f9bb59119b8b5670a85c7ba92c5e9adf4385c81ea00c51c8be33d5" +checksum = "4c84490118f2ee2d74570d114f3d0493cbf02790df303d2707606c3e14e07c96" dependencies = [ "cfg-if", "downcast", @@ -1160,14 +1226,14 @@ dependencies = [ [[package]] name = "mockall_derive" -version = "0.9.1" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dd4234635bca06fc96c7368d038061e0aae1b00a764dc817e900dc974e3deea" +checksum = "22ce75669015c4f47b289fd4d4f56e894e4c96003ffdf3ac51313126f94c6cbb" dependencies = [ "cfg-if", "proc-macro2", - "quote 1.0.21", - "syn 1.0.99", + "quote 1.0.28", + "syn 1.0.109", ] [[package]] @@ -1181,9 +1247,9 @@ dependencies = [ [[package]] name = "native-tls" -version = "0.2.7" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8d96b2e1c8da3957d58100b09f102c6d9cfdfced01b7ec5a8974044bb09dbd4" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" dependencies = [ "lazy_static", "libc", @@ -1214,11 +1280,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" [[package]] -name = "ntapi" -version = "0.3.6" +name = "nu-ansi-term" +version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" dependencies = [ + "overload", "winapi", ] @@ -1235,9 +1302,9 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.3.3" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6f7833f2cbf2360a6cfd58cd41a53aa7a90bd4c202f5b1c7dd2ed73c57b2c3" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" dependencies = [ "autocfg", "num-integer", @@ -1293,9 +1360,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.9.0" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5" +checksum = "9670a07f94779e00908f3e686eab508878ebb390ba6e604d3a284c00e8d0487b" [[package]] name = "openssl" @@ -1330,6 +1397,23 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "os_info" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "006e42d5b888366f1880eda20371fedde764ed2213dc8496f49622fa0c99cd5e" +dependencies = [ + "log", + "serde", + "winapi", +] + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "parking_lot" version = "0.11.1" @@ -1343,9 +1427,9 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", "parking_lot_core 0.9.1", @@ -1375,7 +1459,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-sys", + "windows-sys 0.32.0", ] [[package]] @@ -1409,9 +1493,9 @@ checksum = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" [[package]] name = "percent-encoding" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] name = "pin-project" @@ -1429,15 +1513,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c950132583b500556b1efd71d45b319029f2b71518d979fcc208e16b42426f" dependencies = [ "proc-macro2", - "quote 1.0.21", - "syn 1.0.99", + "quote 1.0.28", + "syn 1.0.109", ] [[package]] name = "pin-project-lite" -version = "0.2.7" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" [[package]] name = "pin-utils" @@ -1463,14 +1547,24 @@ version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" +[[package]] +name = "pq-sys" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31c0052426df997c0cbd30789eb44ca097e3541717a7b8fa36b1c464ee7edebd" +dependencies = [ + "vcpkg", +] + [[package]] name = "predicates" -version = "1.0.8" +version = "2.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f49cfaf7fdaa3bfacc6fa3e7054e65148878354a5cfddcf661df4c851f8021df" +checksum = "59230a63c37f3e18569bdb90e4a89cbf5bf8b06fea0b84e65ea10cc4df47addd" dependencies = [ - "difference", + "difflib", "float-cmp", + "itertools", "normalize-line-endings", "predicates-core", "regex", @@ -1494,24 +1588,24 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.43" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab" +checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b" dependencies = [ "unicode-ident", ] [[package]] name = "prometheus" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45c8babc29389186697fe5a2a4859d697825496b83db5d0b65271cdc0488e88c" +checksum = "449811d15fbdf5ceb5c1144416066429cf82316e2ec8ce0c1f6f8a02e7bbcf8c" dependencies = [ "cfg-if", "fnv", "lazy_static", "memchr", - "parking_lot 0.12.0", + "parking_lot 0.12.1", "protobuf", "thiserror", ] @@ -1524,8 +1618,8 @@ checksum = "f8f30cdb09c39930b8fa5e0f23cbb895ab3f766b187403a0ba0956fc1ef4f0e5" dependencies = [ "lazy_static", "proc-macro2", - "quote 1.0.21", - "syn 1.0.99", + "quote 1.0.28", + "syn 1.0.109", ] [[package]] @@ -1547,7 +1641,7 @@ dependencies = [ "log", "metrics", "num_cpus", - "percent-encoding 2.1.0", + "percent-encoding 2.2.0", "rust_decimal", "thiserror", "url 1.7.2", @@ -1562,9 +1656,9 @@ checksum = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" [[package]] name = "quote" -version = "1.0.21" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" dependencies = [ "proc-macro2", ] @@ -1592,14 +1686,13 @@ dependencies = [ [[package]] name = "rand" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha", "rand_core", - "rand_hc", ] [[package]] @@ -1621,15 +1714,6 @@ dependencies = [ "getrandom", ] -[[package]] -name = "rand_hc" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" -dependencies = [ - "rand_core", -] - [[package]] name = "redis" version = "0.20.2" @@ -1640,9 +1724,9 @@ dependencies = [ "combine", "dtoa", "itoa 0.4.7", - "percent-encoding 2.1.0", - "sha1", - "url 2.2.2", + "percent-encoding 2.2.0", + "sha1 0.6.0", + "url 2.3.1", ] [[package]] @@ -1701,33 +1785,35 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.11.4" +version = "0.11.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "246e9f61b9bb77df069a947682be06e31ac43ea37862e244a69f177694ea6d22" +checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" dependencies = [ - "base64 0.13.0", + "base64 0.21.2", "bytes", "encoding_rs", "futures-core", "futures-util", + "h2", "http", "http-body", "hyper", "hyper-tls", "ipnet", "js-sys", - "lazy_static", "log", "mime", "native-tls", - "percent-encoding 2.1.0", + "once_cell", + "percent-encoding 2.2.0", "pin-project-lite", "serde", "serde_json", "serde_urlencoded", "tokio", "tokio-native-tls", - "url 2.2.2", + "tower-service", + "url 2.3.1", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", @@ -1806,6 +1892,12 @@ dependencies = [ "webpki", ] +[[package]] +name = "rustversion" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" + [[package]] name = "ryu" version = "1.0.5" @@ -1878,219 +1970,204 @@ checksum = "a4a3381e03edd24287172047536f20cabde766e2cd3e65e6b00fb3af51c4f38d" [[package]] name = "sentry" -version = "0.23.0" +version = "0.31.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "546b9b6f76c26c60ffbcf0b7136e15169fe13d43949b4aadb7c1edc1c3f3a26f" -dependencies = [ - "sentry-anyhow", - "sentry-backtrace 0.23.0", - "sentry-core 0.23.0", - "tokio", -] - -[[package]] -name = "sentry" -version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73642819e7fa63eb264abc818a2f65ac8764afbe4870b5ee25bcecc491be0d4c" +checksum = "de31c6e03322af2175d3c850c5b5e11efcadc01948cd1fb7b5ad0a7c7b6c7ff2" dependencies = [ "httpdate", + "native-tls", "reqwest", - "sentry-backtrace 0.27.0", + "sentry-anyhow", + "sentry-backtrace", "sentry-contexts", - "sentry-core 0.27.0", + "sentry-core", + "sentry-debug-images", "sentry-panic", + "sentry-tracing", "tokio", + "ureq", ] [[package]] name = "sentry-anyhow" -version = "0.23.0" +version = "0.31.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "338ef04f73ca2fb1130ebab3853dca36041aa219a442ae873627373887660c36" +checksum = "4df1501f58a7821af9d3bd11435d1c0c74121a6e6931f7615592fdfa85a5fb84" dependencies = [ "anyhow", - "sentry-backtrace 0.23.0", - "sentry-core 0.23.0", -] - -[[package]] -name = "sentry-backtrace" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cd0cba2aff36ac98708f7a6e7abbdde82dbaf180d5870c41084dc1b473648b9" -dependencies = [ - "backtrace", - "lazy_static", - "regex", - "sentry-core 0.23.0", + "sentry-backtrace", + "sentry-core", ] [[package]] name = "sentry-backtrace" -version = "0.27.0" +version = "0.31.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49bafa55eefc6dbc04c7dac91e8c8ab9e89e9414f3193c105cabd991bbc75134" +checksum = "264e3ad27da3d1ad81b499dbcceae0a50e0e6ffc4b65b93f47d5180d46827644" dependencies = [ "backtrace", "once_cell", "regex", - "sentry-core 0.27.0", + "sentry-core", ] [[package]] name = "sentry-contexts" -version = "0.27.0" +version = "0.31.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c63317c4051889e73f0b00ce4024cae3e6a225f2e18a27d2c1522eb9ce2743da" +checksum = "7144590f7950647e4df5bd95f234c3aa29124729c54bd2457e1224d701d1a91c" dependencies = [ "hostname", "libc", + "os_info", "rustc_version", - "sentry-core 0.27.0", + "sentry-core", "uname", ] [[package]] name = "sentry-core" -version = "0.23.0" +version = "0.31.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a957270c9a430218f8031c866493061a27e35a70250e9527f093563a33ce6b" +checksum = "35614ecf115f55d93583baa02a85cb63acb6567cf91b17690d1147bac1739ca4" dependencies = [ - "chrono", - "lazy_static", + "once_cell", "rand", - "sentry-types 0.23.0", + "sentry-types", "serde", "serde_json", ] [[package]] -name = "sentry-core" -version = "0.27.0" +name = "sentry-debug-images" +version = "0.31.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a4591a2d128af73b1b819ab95f143bc6a2fbe48cd23a4c45e1ee32177e66ae6" +checksum = "53c4288a1b255e6ff55f111d2e14a48d369da76e86fae15a00ee26a371d82ad4" dependencies = [ + "findshlibs", "once_cell", - "rand", - "sentry-types 0.27.0", - "serde", - "serde_json", + "sentry-core", ] [[package]] name = "sentry-panic" -version = "0.27.0" +version = "0.31.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "696c74c5882d5a0d5b4a31d0ff3989b04da49be7983b7f52a52c667da5b480bf" +checksum = "0a941028a24baf0a5a994d8a39670cecc72a61971bb0155f771537447a46211a" dependencies = [ - "sentry-backtrace 0.27.0", - "sentry-core 0.27.0", + "sentry-backtrace", + "sentry-core", ] [[package]] -name = "sentry-types" -version = "0.23.0" +name = "sentry-tracing" +version = "0.31.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4dd2266fee014a86e250e98e389191ecd23be546b5c42b6a2fb9af2972fadac" +checksum = "eec56ebafd7cfc1175bccdf277be582ccc3308b8c353dca5831261a967a6e28c" dependencies = [ - "chrono", - "debugid 0.7.2", - "serde", - "serde_json", - "thiserror", - "url 2.2.2", - "uuid 0.8.2", + "sentry-backtrace", + "sentry-core", + "tracing-core", + "tracing-subscriber", ] [[package]] name = "sentry-types" -version = "0.27.0" +version = "0.31.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "823923ae5f54a729159d720aa12181673044ee5c79cbda3be09e56f885e5468f" +checksum = "9c56f616602a3b282bf4b4e8e5b4d10bcf9412a987df91c592b95a1f6ef1ee43" dependencies = [ - "debugid 0.8.0", + "debugid", "getrandom", "hex", "serde", "serde_json", "thiserror", "time 0.3.7", - "url 2.2.2", - "uuid 1.1.2", + "url 2.3.1", + "uuid 1.3.3", ] [[package]] name = "serde" -version = "1.0.144" +version = "1.0.163" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860" +checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.144" +version = "1.0.163" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94ed3a816fb1d101812f83e789f888322c34e291f894f19590dc310963e87a00" +checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" dependencies = [ "proc-macro2", - "quote 1.0.21", - "syn 1.0.99", + "quote 1.0.28", + "syn 2.0.18", ] [[package]] name = "serde_json" -version = "1.0.85" +version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44" +checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" dependencies = [ - "itoa 1.0.1", + "itoa 1.0.6", "ryu", "serde", ] +[[package]] +name = "serde_path_to_error" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7f05c1d5476066defcdfacce1f52fc3cae3af1d3089727100c02ae92e5abbe0" +dependencies = [ + "serde", +] + [[package]] name = "serde_qs" -version = "0.10.1" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cac3f1e2ca2fe333923a1ae72caca910b98ed0630bb35ef6f8c8517d6e81afa" +checksum = "0431a35568651e363364210c91983c1da5eb29404d9f0928b67d4ebcfa7d330c" dependencies = [ - "percent-encoding 2.1.0", + "percent-encoding 2.2.0", "serde", "thiserror", ] [[package]] name = "serde_urlencoded" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edfa57a7f8d9c1d260a549e7224100f6c43d43f9103e06dd8b4095a9b2b43ce9" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ "form_urlencoded", - "itoa 0.4.7", + "itoa 1.0.6", "ryu", "serde", ] [[package]] -name = "sha-1" -version = "0.10.0" +name = "sha1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" +checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" + +[[package]] +name = "sha1" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" dependencies = [ "cfg-if", "cpufeatures", "digest", ] -[[package]] -name = "sha1" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" - [[package]] name = "sha2" version = "0.10.5" @@ -2113,9 +2190,9 @@ dependencies = [ [[package]] name = "signal-hook" -version = "0.3.9" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "470c5a6397076fae0094aaf06a08e6ba6f37acb77d3b1b91ea92b4d6c8650c39" +checksum = "732768f1176d21d09e076c23a93123d40bba92d50c4058da34d45c8de8e682b9" dependencies = [ "libc", "signal-hook-registry", @@ -2132,9 +2209,9 @@ dependencies = [ [[package]] name = "signal-hook-tokio" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6c5d32165ff8b94e68e7b3bdecb1b082e958c22434b363482cfb89dcd6f3ff8" +checksum = "213241f76fb1e37e27de3b6aa1b068a2c333233b59cca6634f634b80a27ecf1e" dependencies = [ "futures-core", "libc", @@ -2173,9 +2250,9 @@ checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" [[package]] name = "socket2" -version = "0.4.4" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" dependencies = [ "libc", "winapi", @@ -2189,9 +2266,9 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "sqlformat" -version = "0.1.8" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4b7922be017ee70900be125523f38bdd644f4f06a1b16e8fa5a8ee8c34bffd4" +checksum = "0c12bc9199d1db8234678b7051747c07f517cdcf019262d1847b94ec8b1aee3e" dependencies = [ "itertools", "nom", @@ -2200,9 +2277,9 @@ dependencies = [ [[package]] name = "sqlx" -version = "0.5.9" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7911b0031a0247af40095838002999c7a52fba29d9739e93326e71a5a1bc9d43" +checksum = "f8de3b03a925878ed54a954f621e64bf55a3c1bd29652d0d1a17830405350188" dependencies = [ "sqlx-core", "sqlx-macros", @@ -2210,11 +2287,11 @@ dependencies = [ [[package]] name = "sqlx-core" -version = "0.5.13" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e48c61941ccf5ddcada342cd59e3e5173b007c509e1e8e990dafc830294d9dc5" +checksum = "fa8241483a83a3f33aa5fff7e7d9def398ff9990b2752b6c6112b83c6d246029" dependencies = [ - "ahash", + "ahash 0.7.6", "atoi", "base64 0.13.0", "bigdecimal", @@ -2225,6 +2302,7 @@ dependencies = [ "crc", "crossbeam-queue", "dirs", + "dotenvy", "either", "event-listener", "futures-channel", @@ -2236,19 +2314,19 @@ dependencies = [ "hkdf", "hmac", "indexmap", - "itoa 1.0.1", + "itoa 1.0.6", "libc", "log", "md-5", "memchr", - "num-bigint 0.3.3", + "num-bigint 0.4.3", "once_cell", "paste", - "percent-encoding 2.1.0", + "percent-encoding 2.2.0", "rand", "serde", "serde_json", - "sha-1", + "sha1 0.10.5", "sha2", "smallvec", "sqlformat", @@ -2256,38 +2334,38 @@ dependencies = [ "stringprep", "thiserror", "tokio-stream", - "url 2.2.2", - "uuid 0.8.2", + "url 2.3.1", + "uuid 1.3.3", "whoami", ] [[package]] name = "sqlx-macros" -version = "0.5.13" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc0fba2b0cae21fc00fe6046f8baa4c7fcb49e379f0f592b04696607f69ed2e1" +checksum = "9966e64ae989e7e575b19d7265cb79d7fc3cbbdf179835cb0d716f294c2049c9" dependencies = [ - "dotenv", + "dotenvy", "either", "heck", "hex", "once_cell", "proc-macro2", - "quote 1.0.21", + "quote 1.0.28", "serde", "serde_json", "sha2", "sqlx-core", "sqlx-rt", - "syn 1.0.99", - "url 2.2.2", + "syn 1.0.109", + "url 2.3.1", ] [[package]] name = "sqlx-rt" -version = "0.5.13" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4db708cd3e459078f85f39f96a00960bd841f66ee2a669e90bf36907f5a79aae" +checksum = "804d3f245f894e61b1e6263c84b23ca675d96753b5abfd5cc8597d86806e8024" dependencies = [ "native-tls", "once_cell", @@ -2313,12 +2391,12 @@ checksum = "1e81da0851ada1f3e9d4312c704aa4f8806f0f9d69faaf8df2f3464b4a9437c2" [[package]] name = "svc-agent" -version = "0.19.4" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bece78bf40f66ba111ff076c7aeadcd0d439e7dac0e5f764a4ee1430a9b89fd9" +checksum = "1c90205083c87c6bc25990d5cdd26f98d8c431441e86dbd4e7421b28c428e04d" dependencies = [ "async-channel", - "base64 0.13.0", + "base64 0.21.2", "chrono", "http", "log", @@ -2328,16 +2406,17 @@ dependencies = [ "sqlx", "svc-authn", "tokio", - "uuid 0.8.2", + "uuid 1.3.3", ] [[package]] name = "svc-authn" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f6c85277226df5df79f6e0bee5d98520423b722277b7c4aaa9fc617a2919bfc" +checksum = "eb5cf659f78c8fff863c17ac4e674829517919716eeecab602e8d2941e89c111" dependencies = [ "chrono", + "diesel", "http", "jsonwebtoken", "serde", @@ -2347,9 +2426,9 @@ dependencies = [ [[package]] name = "svc-authz" -version = "0.11.1" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa4bb0e49ec6b226dda9a86121696e869a992448b5619c94bfd0e2784bb85e5" +checksum = "52561530fb6e83913951fb4c66fe4368d6d754ddc252b137bab06b84b6cf07cc" dependencies = [ "async-trait", "bytes", @@ -2368,28 +2447,15 @@ dependencies = [ [[package]] name = "svc-error" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601b25c55d2123e19b0d6981a8579f4811c12d3d883ad73c4d7e97119d1ee704" -dependencies = [ - "anyhow", - "crossbeam-channel", - "http", - "serde", - "serde_derive", -] - -[[package]] -name = "svc-error" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "746114b66fe115121d146776dd3808f4c60c762d72921fa5a0f0817798e26538" +checksum = "3f841d7fd45d6f179e9f3765491fcb5eea100a5bbe50ea47faf3f262031966d9" dependencies = [ "anyhow", "crossbeam-channel", "http", "once_cell", - "sentry 0.23.0", + "sentry", "sentry-anyhow", "serde", "serde_derive", @@ -2401,9 +2467,9 @@ dependencies = [ [[package]] name = "svc-utils" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df7f4355a87d906406754e82854005d909b2a18065165841b3167e0fed5aa722" +checksum = "8b8fd4d41bfda7aec14a80746dd1e6f5ae6c04643a7e9c18065f38388b6a4477" dependencies = [ "axum", "futures", @@ -2413,10 +2479,10 @@ dependencies = [ "prometheus", "svc-agent", "svc-authn", - "svc-error 0.3.0", + "svc-error", "tokio", "tower", - "tower-http 0.2.5", + "tower-http", "tracing", ] @@ -2433,12 +2499,23 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.99" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", - "quote 1.0.21", + "quote 1.0.28", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" +dependencies = [ + "proc-macro2", + "quote 1.0.28", "unicode-ident", ] @@ -2487,8 +2564,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8f2591983642de85c921015f3f070c665a197ed69e417af436115e3a1407487" dependencies = [ "proc-macro2", - "quote 1.0.21", - "syn 1.0.99", + "quote 1.0.28", + "syn 1.0.109", ] [[package]] @@ -2516,7 +2593,7 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "004cbc98f30fa233c61a38bc77e96a9106e65c88f2d3bef182ae952027e5753d" dependencies = [ - "itoa 1.0.1", + "itoa 1.0.6", "libc", "num_threads", ] @@ -2538,33 +2615,32 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.17.0" +version = "1.28.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2af73ac49756f3f7c01172e34a23e5d0216f6c32333757c2c61feb2bbff5a5ee" +checksum = "94d7b1cfd2aa4011f2de74c2c4c63665e27a71006b0a192dcd2710272e73dfa2" dependencies = [ + "autocfg", "bytes", "libc", - "memchr", "mio", "num_cpus", - "once_cell", - "parking_lot 0.12.0", + "parking_lot 0.12.1", "pin-project-lite", "signal-hook-registry", "socket2", "tokio-macros", - "winapi", + "windows-sys 0.48.0", ] [[package]] name = "tokio-macros" -version = "1.7.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", - "quote 1.0.21", - "syn 1.0.99", + "quote 1.0.28", + "syn 2.0.18", ] [[package]] @@ -2601,16 +2677,16 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.6.8" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d3725d3efa29485e87311c5b699de63cde14b00ed4d256b8318aa30ca452cd" +checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" dependencies = [ "bytes", "futures-core", "futures-sink", - "log", "pin-project-lite", "tokio", + "tracing", ] [[package]] @@ -2624,9 +2700,9 @@ dependencies = [ [[package]] name = "tower" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a89fd63ad6adf737582df5db40d286574513c69a11dac5214dc3b5603d6713e" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" dependencies = [ "futures-core", "futures-util", @@ -2640,9 +2716,9 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.2.5" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aba3f3efabf7fb41fae8534fc20a817013dd1c12cb45441efb6c82e6556b4cd8" +checksum = "5d1d42a9b3f3ec46ba828e8d376aec14592ea199f70a06a548587ecd1c4ab658" dependencies = [ "bitflags", "bytes", @@ -2657,30 +2733,11 @@ dependencies = [ "tracing", ] -[[package]] -name = "tower-http" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c530c8675c1dbf98facee631536fa116b5fb6382d7dd6dc1b118d970eafe3ba" -dependencies = [ - "bitflags", - "bytes", - "futures-core", - "futures-util", - "http", - "http-body", - "http-range-header", - "pin-project-lite", - "tower", - "tower-layer", - "tower-service", -] - [[package]] name = "tower-layer" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "343bc9466d3fe6b0f960ef45960509f84480bf4fd96f92901afe7ff3df9d3a62" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" [[package]] name = "tower-service" @@ -2690,9 +2747,9 @@ checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" [[package]] name = "tracing" -version = "0.1.29" +version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "375a639232caf30edfc78e8d89b2d4c375515393e7af7e16f01cd96917fb2105" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ "cfg-if", "log", @@ -2714,22 +2771,23 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.18" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f480b8f81512e825f337ad51e94c1eb5d3bbdf2b363dcd01e2b19a9ffe3f8e" +checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" dependencies = [ "proc-macro2", - "quote 1.0.21", - "syn 1.0.99", + "quote 1.0.28", + "syn 2.0.18", ] [[package]] name = "tracing-core" -version = "0.1.21" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4ed65637b8390770814083d20756f87bfa2c21bf2f110babdc5438351746e4" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" dependencies = [ - "lazy_static", + "once_cell", + "valuable", ] [[package]] @@ -2754,9 +2812,9 @@ dependencies = [ [[package]] name = "tracing-log" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6923477a48e41c1951f1999ef8bb5a3023eb723ceadafe78ffb65dc366761e3" +checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" dependencies = [ "lazy_static", "log", @@ -2765,9 +2823,9 @@ dependencies = [ [[package]] name = "tracing-serde" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb65ea441fbb84f9f6748fd496cf7f63ec9af5bca94dd86456978d055e8eb28b" +checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1" dependencies = [ "serde", "tracing-core", @@ -2775,13 +2833,13 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.7" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5312f325fe3588e277415f5a6cca1f4ccad0f248c4cd5a4bd33032d7286abc22" +checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" dependencies = [ - "ansi_term", - "lazy_static", "matchers", + "nu-ansi-term", + "once_cell", "regex", "serde", "serde_json", @@ -2869,6 +2927,19 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +[[package]] +name = "ureq" +version = "2.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "338b31dd1314f68f3aabf3ed57ab922df95ffcd902476ca7ba3c4ce7b908c46d" +dependencies = [ + "base64 0.13.0", + "log", + "native-tls", + "once_cell", + "url 2.3.1", +] + [[package]] name = "url" version = "1.7.2" @@ -2882,14 +2953,13 @@ dependencies = [ [[package]] name = "url" -version = "2.2.2" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" dependencies = [ "form_urlencoded", - "idna 0.2.3", - "matches", - "percent-encoding 2.1.0", + "idna 0.3.0", + "percent-encoding 2.2.0", "serde", ] @@ -2898,21 +2968,23 @@ name = "uuid" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" -dependencies = [ - "getrandom", - "serde", -] [[package]] name = "uuid" -version = "1.1.2" +version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd6469f4314d5f1ffec476e05f17cc9a78bc7a27a6a857842170bdf8d6f98d2f" +checksum = "345444e32442451b267fc254ae85a209c64be56d2890e601a0c37ff0c3c5ecd2" dependencies = [ "getrandom", "serde", ] +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + [[package]] name = "vcpkg" version = "0.2.15" @@ -2921,18 +2993,18 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "vec1" -version = "1.8.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fc1631c774f0f9570797191e01247cbefde789eebfbf128074cb934115a6133" +checksum = "2bda7c41ca331fe9a1c278a9e7ee055f4be7f5eb1c2b72f079b4ff8b5fce9d5c" dependencies = [ "serde", ] [[package]] name = "version_check" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "want" @@ -2963,8 +3035,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d54ee1d4ed486f78874278e63e4069fc1ab9f6a18ca492076ffb90c5eb2997fd" dependencies = [ "cfg-if", - "serde", - "serde_json", "wasm-bindgen-macro", ] @@ -2978,8 +3048,8 @@ dependencies = [ "lazy_static", "log", "proc-macro2", - "quote 1.0.21", - "syn 1.0.99", + "quote 1.0.28", + "syn 1.0.109", "wasm-bindgen-shared", ] @@ -3001,7 +3071,7 @@ version = "0.2.74" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "088169ca61430fe1e58b8096c24975251700e7b1f6fd91cc9d59b04fb9b18bd4" dependencies = [ - "quote 1.0.21", + "quote 1.0.28", "wasm-bindgen-macro-support", ] @@ -3012,8 +3082,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be2241542ff3d9f241f5e2cb6dd09b37efe786df8851c54957683a49f0987a97" dependencies = [ "proc-macro2", - "quote 1.0.21", - "syn 1.0.99", + "quote 1.0.28", + "syn 1.0.109", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3077,54 +3147,129 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-sys" version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3df6e476185f92a12c072be4a189a0210dcdcf512a1891d6dff9edb874deadc6" dependencies = [ - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_msvc", + "windows_aarch64_msvc 0.32.0", + "windows_i686_gnu 0.32.0", + "windows_i686_msvc 0.32.0", + "windows_x86_64_gnu 0.32.0", + "windows_x86_64_msvc 0.32.0", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc 0.48.0", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + [[package]] name = "windows_aarch64_msvc" version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8e92753b1c443191654ec532f14c199742964a061be25d77d7a96f09db20bf5" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + [[package]] name = "windows_i686_gnu" version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a711c68811799e017b6038e0922cb27a5e2f43a2ddb609fe0b6f3eeda9de615" +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + [[package]] name = "windows_i686_msvc" version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "146c11bb1a02615db74680b32a68e2d61f553cc24c4eb5b4ca10311740e44172" +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + [[package]] name = "windows_x86_64_gnu" version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c912b12f7454c6620635bbff3450962753834be2a594819bd5e945af18ec64bc" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + [[package]] name = "windows_x86_64_msvc" version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "504a2476202769977a040c6364301a3f65d0cc9e3fb08600b2bda150a0488316" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + [[package]] name = "winreg" -version = "0.7.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" dependencies = [ "winapi", ] diff --git a/Cargo.toml b/Cargo.toml index e174a2a6..3897879c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,32 +10,33 @@ edition = "2018" [dependencies] anyhow = "1.0" async-trait = "0.1" -axum = { version = "0.5.14", features = ["headers"] } +axum = { version = "0.6", features = ["headers"] } chrono = { version = "0.4", features = ["serde"] } config = { version = "0.13", default-features = false, features = ["toml"] } futures = "0.3" futures-channel = "0.3" -hashring = "0.3.0" +hashring = "0.3" headers = "0.3" http = "0.2" -humantime-serde = "1" -hyper = { version = "0.14.20", features = ["server"] } -once_cell = "1" +humantime-serde = "1.1" +hyper = { version = "0.14", features = ["server"] } +once_cell = "1.17" +parking_lot = "0.12" percent-encoding = "2.1" -pin-utils = "0.1.0" +pin-utils = "0.1" prometheus = "0.13" prometheus-static-metric = "0.5" quaint = { version = "0.1", features = ["uuid-0_8", "chrono-0_4"] } rand = "0.8" reqwest = "0.11" -sentry = { version = "0.27", default-features = true } +sentry = { version = "0.31", default-features = true, features = ["reqwest"] } serde = "1.0" serde_derive = "1.0" serde_json = "1.0" -serde_qs = "0.10" -signal-hook = "0.3.6" +serde_qs = "0.12" +signal-hook = "0.3" signal-hook-tokio = { version = "0.3", features = ["futures-v0_3"] } -sqlx = { version = "0.5", features = [ +sqlx = { version = "0.6", features = [ "offline", "postgres", "macros", @@ -45,18 +46,18 @@ sqlx = { version = "0.5", features = [ "bigdecimal", "runtime-tokio-native-tls", ] } -svc-agent = { version = "0.19", features = ["sqlx"] } -svc-authn = { version = "0.7", features = ["jose", "sqlx"] } -svc-authz = "0.11" -svc-error = { version = "0.4", features = [ +svc-agent = { version = "0.20", features = ["sqlx"] } +svc-authn = { version = "0.8", features = ["jose", "sqlx"] } +svc-authz = "0.12" +svc-error = { version = "0.5", features = [ "svc-agent", "svc-authn", "svc-authz", "sentry-extension", "sqlx", ] } -svc-utils = { version = "0.6.0", features = ["log-middleware", "metrics-middleware", "cors-middleware", "authn-extractor"] } -tokio = { version = "1.17", features = ["full"] } +svc-utils = { version = "0.7", features = ["log-middleware", "metrics-middleware", "cors-middleware", "authn-extractor"] } +tokio = { version = "1.28", features = ["full"] } tower = { version = "0.4", features = [ "timeout" ] } tracing = "0.1" tracing-appender = "0.2" @@ -65,14 +66,13 @@ tracing-error = "0.2" tracing-futures = "0.2" tracing-log = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] } -url = { version = "2.2.1", features = ["serde"] } -uuid = { version = "0.8", features = ["v4", "serde"] } -vec1 = { version = "1.8.0", features = ["serde"] } -parking_lot = "0.12" +url = { version = "2.3", features = ["serde"] } +uuid = { version = "1.3", features = ["v4", "serde"] } +vec1 = { version = "1.10", features = ["serde"] } [dev-dependencies] lazy_static = "1.4" -mockall = "0.9.1" +mockall = "0.11" [dependencies.dotenv] version = "0.15" diff --git a/docker/Dockerfile b/docker/Dockerfile index aabd8b37..232b403d 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,7 +1,7 @@ ## ----------------------------------------------------------------------------- ## Build ## ----------------------------------------------------------------------------- -FROM rust:1.64.0-slim-buster as build-stage +FROM rust:1.70.0-slim-buster as build-stage RUN apt update && apt install -y --no-install-recommends \ pkg-config \ diff --git a/docker/migration.dockerfile b/docker/migration.dockerfile index 3d9d9912..2969dc31 100644 --- a/docker/migration.dockerfile +++ b/docker/migration.dockerfile @@ -1,4 +1,4 @@ -FROM rust:1.64.0-slim-buster +FROM rust:1.70.0-slim-buster RUN apt update && apt install -y --no-install-recommends \ pkg-config \ diff --git a/rust-toolchain.toml b/rust-toolchain.toml index cc8f987e..f15cd1c9 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -channel = "1.64.0" +channel = "1.70.0" components = ["rustfmt", "clippy"] diff --git a/src/app/api/v1/class/read.rs b/src/app/api/v1/class/read.rs index 138aee0a..e36e4ecf 100644 --- a/src/app/api/v1/class/read.rs +++ b/src/app/api/v1/class/read.rs @@ -502,13 +502,11 @@ mod tests { #[tokio::test] async fn property_filters_query_params_parser() { - fn check( + fn check( uri: impl AsRef, value: T, ) { - let req = axum::extract::RequestParts::new( - http::Request::builder().uri(uri.as_ref()).body(()).unwrap(), - ); + let req = http::Request::builder().uri(uri.as_ref()).body(()).unwrap(); assert_eq!( serde_qs::from_str::(req.uri().query().unwrap_or_default()).unwrap(), value diff --git a/src/app/error.rs b/src/app/error.rs index 30fed9f3..e17f9310 100644 --- a/src/app/error.rs +++ b/src/app/error.rs @@ -241,11 +241,11 @@ impl IntoResponse for Error { let properties: ErrorKindProperties = self.kind.into(); let span = tracing::Span::current(); - span.record("kind", &properties.kind); + span.record("kind", properties.kind); if let Some(err) = &self.err { let detail = err.to_string(); - span.record("detail", &detail.as_str()); + span.record("detail", detail.as_str()); } (properties.status, Json(&self.to_svc_error())).into_response() diff --git a/src/app/http.rs b/src/app/http.rs index 3a6f0e89..e0bec819 100644 --- a/src/app/http.rs +++ b/src/app/http.rs @@ -5,6 +5,7 @@ use axum::{ extract::{rejection::JsonRejection, Extension, FromRequest}, routing::{get, post, Router}, }; +use http::Request; use svc_utils::middleware::{CorsLayer, LogLayer, MeteredRoute}; use super::api::v1::class::{ @@ -171,17 +172,16 @@ fn utils_router() -> Router { pub struct Json(pub T); #[async_trait] -impl FromRequest for Json +impl FromRequest for Json where - axum::Json: FromRequest, + axum::Json: FromRequest, B: Send + 'static, + S: Sync, { type Rejection = super::error::Error; - async fn from_request( - req: &mut axum::extract::RequestParts, - ) -> Result { - match axum::Json::::from_request(req).await { + async fn from_request(req: Request, state: &S) -> Result { + match axum::Json::::from_request(req, state).await { Ok(value) => Ok(Self(value.0)), Err(rejection) => { let kind = match rejection { diff --git a/src/app/postprocessing_strategy/shared_helpers.rs b/src/app/postprocessing_strategy/shared_helpers.rs index 176450c6..48180fce 100644 --- a/src/app/postprocessing_strategy/shared_helpers.rs +++ b/src/app/postprocessing_strategy/shared_helpers.rs @@ -64,10 +64,11 @@ pub fn parse_segments(segments: &str) -> Result<(DateTime, Segments)> { }) .collect::>(); let absolute_started_at = { - let naive_datetime = NaiveDateTime::from_timestamp( + let naive_datetime = NaiveDateTime::from_timestamp_opt( absolute_started_at / 1000, ((absolute_started_at % 1000) * 1_000_000) as u32, - ); + ) + .ok_or(anyhow!("invalid or out-of-range datetime"))?; DateTime::::from_utc(naive_datetime, Utc) }; diff --git a/src/clients/event/types.rs b/src/clients/event/types.rs index b2c96363..c971432e 100644 --- a/src/clients/event/types.rs +++ b/src/clients/event/types.rs @@ -140,7 +140,7 @@ pub trait MqttRequest: Clone + serde::Serialize { fn method(&self) -> &'static str; fn payload(&self) -> JsonValue { - serde_json::to_value(&self).unwrap() + serde_json::to_value(self).unwrap() } } diff --git a/src/db/class/mod.rs b/src/db/class/mod.rs index b9854623..b1eea68a 100644 --- a/src/db/class/mod.rs +++ b/src/db/class/mod.rs @@ -45,9 +45,9 @@ impl std::ops::DerefMut for KeyValueProperties { } impl sqlx::Encode<'_, sqlx::Postgres> for KeyValueProperties { - fn encode_by_ref<'q>( + fn encode_by_ref( &self, - buf: &mut >::ArgumentBuffer, + buf: &mut ::ArgumentBuffer, ) -> sqlx::encode::IsNull { self.clone().into_json().encode_by_ref(buf) } diff --git a/src/db/mod.rs b/src/db/mod.rs index b40625e6..3b378551 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -12,7 +12,7 @@ pub(crate) async fn create_pool( PgPoolOptions::new() .max_connections(size) .min_connections(idle_size.unwrap_or(1)) - .connect_timeout(Duration::from_secs(timeout)) + .acquire_timeout(Duration::from_secs(timeout)) .max_lifetime(Duration::from_secs(max_lifetime)) .connect(url) .await diff --git a/src/serde.rs b/src/serde.rs index 29010cbb..61954b60 100644 --- a/src/serde.rs +++ b/src/serde.rs @@ -82,7 +82,11 @@ pub(crate) mod ts_seconds_bound_tuple { { let lt = match seq.next_element()? { Some(Some(val)) => { - let dt = DateTime::::from_utc(NaiveDateTime::from_timestamp(val, 0), Utc); + let ndt = NaiveDateTime::from_timestamp_opt(val, 0).ok_or( + de::Error::custom(format!("cannot convert {val} secs to NaiveDateTime")), + )?; + let dt = DateTime::::from_utc(ndt, Utc); + Bound::Included(dt) } Some(None) => Bound::Unbounded, @@ -91,7 +95,10 @@ pub(crate) mod ts_seconds_bound_tuple { let rt = match seq.next_element()? { Some(Some(val)) => { - let dt = DateTime::::from_utc(NaiveDateTime::from_timestamp(val, 0), Utc); + let ndt = NaiveDateTime::from_timestamp_opt(val, 0).ok_or( + de::Error::custom(format!("cannot convert {val} secs to NaiveDateTime")), + )?; + let dt = DateTime::::from_utc(ndt, Utc); Bound::Excluded(dt) } Some(None) => Bound::Unbounded, From fffa90c325baca7de0093c8902ee7eed360aac8a Mon Sep 17 00:00:00 2001 From: Mikhail Grachev Date: Mon, 5 Jun 2023 19:37:46 +0300 Subject: [PATCH 20/35] Bump `sqlx-cli` version to `0.6.3` Signed-off-by: Mikhail Grachev --- .github/workflows/check.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 98ad856c..d413b5cd 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -39,7 +39,7 @@ jobs: - name: fmt run: cargo fmt -- --check - name: Install sqlx-cli - run: cargo install sqlx-cli --version 0.5.7 --no-default-features --features postgres + run: cargo install sqlx-cli --version 0.6.3 --no-default-features --features native-tls,postgres - name: Run tests run: > cargo sqlx database create && From 2458b56d45a06554c5ca69687e7c6f9ecf1849ac Mon Sep 17 00:00:00 2001 From: Dmitry Shlagoff Date: Tue, 6 Jun 2023 10:50:17 +0200 Subject: [PATCH 21/35] Fix errors after updates --- src/app/stage/ban.rs | 28 ++++++++++++++-------------- src/test_helpers/state.rs | 14 ++++++++++++-- 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/src/app/stage/ban.rs b/src/app/stage/ban.rs index 6997eff5..1e14fe46 100644 --- a/src/app/stage/ban.rs +++ b/src/app/stage/ban.rs @@ -32,16 +32,16 @@ pub async fn save_intent( ban: bool, class: &db::class::Object, sender: AccountId, - user_account: AccountId, - last_op_id: i64, + target_account: AccountId, + last_operation_id: i64, ) -> Result<(), Error> { let event_id = get_next_event_id(conn).await?; let event = BanIntentV1 { ban, classroom_id: class.id(), - user_account, - last_op_id, sender, + target_account, + last_operation_id, }; nats::publish_event(ctx, class.id(), &event_id, event.into()).await } @@ -78,8 +78,8 @@ pub async fn handle_intent( // attempts to schedule the same message will fail (dedup). let op = ban_account_op::UpsertQuery::new_operation( - intent.user_account.clone(), - intent.last_op_id, + intent.target_account.clone(), + intent.last_operation_id, intent_id.sequence_id(), ) .execute(&mut conn) @@ -116,8 +116,8 @@ pub async fn accept( let event = BanAcceptedV1 { ban: intent.ban, classroom_id: intent.classroom_id, - user_account: intent.user_account, - op_id: intent_id.sequence_id(), + target_account: intent.target_account, + operation_id: intent_id.sequence_id(), }; nats::publish_event(ctx, event.classroom_id, &event_id, event.into()).await @@ -131,8 +131,8 @@ async fn reject( let event = BanRejectedV1 { ban: intent.ban, classroom_id: intent.classroom_id, - user_account: intent.user_account, - op_id: intent_id.sequence_id(), + target_account: intent.target_account, + operation_id: intent_id.sequence_id(), }; let event_id = ( ENTITY_TYPE.to_owned(), @@ -155,8 +155,8 @@ pub async fn handle_video_streaming_banned( .transient()?; let op = ban_account_op::UpsertQuery::new_video_streaming_banned( - video_streaming_banned.user_account.clone(), - video_streaming_banned.op_id, + video_streaming_banned.target_account.clone(), + video_streaming_banned.operation_id, ) .execute(&mut conn) .await @@ -186,8 +186,8 @@ pub async fn handle_collaboration_banned( .transient()?; let op = ban_account_op::UpsertQuery::new_collaboration_banned( - collaboration_banned.user_account.clone(), - collaboration_banned.op_id, + collaboration_banned.target_account.clone(), + collaboration_banned.operation_id, ) .execute(&mut conn) .await diff --git a/src/test_helpers/state.rs b/src/test_helpers/state.rs index 55a27759..d6dff5fa 100644 --- a/src/test_helpers/state.rs +++ b/src/test_helpers/state.rs @@ -13,7 +13,8 @@ use svc_agent::{ }; use svc_authz::ClientMap as Authz; use svc_nats_client::{ - Event, Message, MessageStream, NatsClient, PublishError, SubscribeError, TermMessageError, + AckPolicy, DeliverPolicy, Event, Message, MessageStream, Messages, NatsClient, PublishError, + Subject, SubscribeError, TermMessageError, }; use url::Url; use vec1::{vec1, Vec1}; @@ -111,7 +112,16 @@ impl NatsClient for TestNatsClient { Ok(()) } - async fn subscribe(&self) -> Result { + async fn subscribe_durable(&self) -> Result { + unimplemented!() + } + + async fn subscribe_ephemeral( + &self, + _subject: Subject, + _deliver_policy: DeliverPolicy, + _ack_policy: AckPolicy, + ) -> Result { unimplemented!() } From 357c37eaf12c2474d84be672886f0e977f3d4769 Mon Sep 17 00:00:00 2001 From: Dmitry Shlagoff Date: Tue, 6 Jun 2023 12:50:42 +0200 Subject: [PATCH 22/35] Move transient/permanent errors to svc-nats-client --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- src/app/stage/ban.rs | 8 +++---- src/app/stage/mod.rs | 55 +++++--------------------------------------- 4 files changed, 13 insertions(+), 56 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 33185aed..7d5b0d38 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3025,8 +3025,8 @@ dependencies = [ [[package]] name = "svc-nats-client" -version = "0.3.0" -source = "git+https://github.com/foxford/svc-nats-client/?branch=ULMS-1896/add-consumer-move-event-id#fb16789558956e09547765e329423aecb181ebe1" +version = "0.4.0" +source = "git+https://github.com/foxford/svc-nats-client/?branch=ULMS-1896/add-transient-permanent-errors#705d841c12fbc0b26233fb5935b362540d40ff80" dependencies = [ "anyhow", "async-nats", diff --git a/Cargo.toml b/Cargo.toml index 6718d377..a697403f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,7 +57,7 @@ svc-error = { version = "0.5", features = [ "sqlx", ] } svc-events = "0.7" -svc-nats-client = { git = "https://github.com/foxford/svc-nats-client/", branch = "ULMS-1896/add-consumer-move-event-id" } +svc-nats-client = { git = "https://github.com/foxford/svc-nats-client/", branch = "ULMS-1896/add-transient-permanent-errors" } svc-utils = { version = "0.7", features = ["log-middleware", "metrics-middleware", "cors-middleware", "authn-extractor"] } tokio = { version = "1.28", features = ["full"] } tower = { version = "0.4", features = [ "timeout" ] } diff --git a/src/app/stage/ban.rs b/src/app/stage/ban.rs index 1e14fe46..033ea67e 100644 --- a/src/app/stage/ban.rs +++ b/src/app/stage/ban.rs @@ -17,7 +17,7 @@ use crate::{ db::{self, ban_account_op}, }; -use super::{FailureKind, HandleMsgFailure}; +use super::{FailureKind, HandleMessageFailure}; const ENTITY_TYPE: &str = "ban"; @@ -65,7 +65,7 @@ pub async fn handle_intent( ctx: &dyn AppContext, intent: BanIntentV1, intent_id: EventId, -) -> Result<(), HandleMsgFailure> { +) -> Result<(), HandleMessageFailure> { let mut conn = ctx .get_conn() .await @@ -147,7 +147,7 @@ async fn reject( pub async fn handle_video_streaming_banned( ctx: &dyn AppContext, video_streaming_banned: BanVideoStreamingCompletedV1, -) -> Result<(), HandleMsgFailure> { +) -> Result<(), HandleMessageFailure> { let mut conn = ctx .get_conn() .await @@ -178,7 +178,7 @@ pub async fn handle_video_streaming_banned( pub async fn handle_collaboration_banned( ctx: &dyn AppContext, collaboration_banned: BanCollaborationCompletedV1, -) -> Result<(), HandleMsgFailure> { +) -> Result<(), HandleMessageFailure> { let mut conn = ctx .get_conn() .await diff --git a/src/app/stage/mod.rs b/src/app/stage/mod.rs index 672c6694..b0539d13 100644 --- a/src/app/stage/mod.rs +++ b/src/app/stage/mod.rs @@ -2,7 +2,10 @@ use std::{convert::TryFrom, str::FromStr, sync::Arc}; use anyhow::Context; use svc_events::{Event, EventV1}; -use svc_nats_client::{consumer::HandleMessageOutcome, Subject}; +use svc_nats_client::{ + consumer::{FailureKind, HandleMessageFailure, FailureKindExt}, + Subject, +}; use crate::db; @@ -13,46 +16,7 @@ pub mod ban; pub async fn route_message( ctx: Arc, msg: Arc, -) -> HandleMessageOutcome { - match do_route_msg(ctx, msg).await { - Ok(_) => HandleMessageOutcome::Processed, - Err(HandleMsgFailure::Transient(e)) => { - tracing::error!(%e, "transient failure, retrying"); - HandleMessageOutcome::ProcessLater - } - Err(HandleMsgFailure::Permanent(e)) => { - tracing::error!(%e, "permanent failure, won't process"); - HandleMessageOutcome::WontProcess - } - } -} - -pub enum HandleMsgFailure { - Transient(E), - Permanent(E), -} - -trait FailureKind { - /// This error can be fixed by retrying later. - fn transient(self) -> Result>; - /// This error can't be fixed by retrying later (parse failure, unknown id, etc). - fn permanent(self) -> Result>; -} - -impl FailureKind for Result { - fn transient(self) -> Result> { - self.map_err(|e| HandleMsgFailure::Transient(e)) - } - - fn permanent(self) -> Result> { - self.map_err(|e| HandleMsgFailure::Permanent(e)) - } -} - -async fn do_route_msg( - ctx: Arc, - msg: Arc, -) -> Result<(), HandleMsgFailure> { +) -> Result<(), HandleMessageFailure> { let subject = Subject::from_str(&msg.subject) .context("parse nats subject") .permanent()?; @@ -102,12 +66,5 @@ async fn do_route_msg( }, }; - match r { - Ok(_) => Ok(()), - Err(HandleMsgFailure::Transient(e)) => Err(HandleMsgFailure::Transient(anyhow!(e))), - Err(HandleMsgFailure::Permanent(e)) => { - e.notify_sentry(); - Err(HandleMsgFailure::Permanent(anyhow!(e))) - } - } + FailureKindExt::map_err(r, |e| anyhow::anyhow!(e)) } From 532de1700a3590eddc9a1fd2a5873621e3ce5e38 Mon Sep 17 00:00:00 2001 From: Dmitry Shlagoff Date: Tue, 6 Jun 2023 14:24:32 +0200 Subject: [PATCH 23/35] Fix fmt --- src/app/stage/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/stage/mod.rs b/src/app/stage/mod.rs index b0539d13..423b6ded 100644 --- a/src/app/stage/mod.rs +++ b/src/app/stage/mod.rs @@ -3,7 +3,7 @@ use std::{convert::TryFrom, str::FromStr, sync::Arc}; use anyhow::Context; use svc_events::{Event, EventV1}; use svc_nats_client::{ - consumer::{FailureKind, HandleMessageFailure, FailureKindExt}, + consumer::{FailureKind, FailureKindExt, HandleMessageFailure}, Subject, }; From 57fa5649a145c0092bb5dab97b2ada7d4a24feb3 Mon Sep 17 00:00:00 2001 From: Dmitry Shlagoff Date: Tue, 6 Jun 2023 14:57:51 +0200 Subject: [PATCH 24/35] Use AgentId instead of AccountId for intent.sender --- Cargo.lock | 5 ++--- Cargo.toml | 2 +- src/app/api/v1/account/ban.rs | 10 ++++----- src/app/stage/ban.rs | 40 +++++++++++++++++++++++++++++------ src/clients/nats.rs | 33 ++++++++++++++++++++++++++--- 5 files changed, 72 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7d5b0d38..b51ee450 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3012,8 +3012,7 @@ dependencies = [ [[package]] name = "svc-events" version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15432fcea5a7eaa54236ee253e2b573bd720ef6c94a7849be17804200830b7d5" +source = "git+https://github.com/foxford/svc-events?branch=ULMS-1896/use-agent-id-intent#330d270763929832d32f9f15df448ac95322674a" dependencies = [ "serde", "serde_json", @@ -3026,7 +3025,7 @@ dependencies = [ [[package]] name = "svc-nats-client" version = "0.4.0" -source = "git+https://github.com/foxford/svc-nats-client/?branch=ULMS-1896/add-transient-permanent-errors#705d841c12fbc0b26233fb5935b362540d40ff80" +source = "git+https://github.com/foxford/svc-nats-client/?branch=ULMS-1896/add-transient-permanent-errors#7d5ecf35b2cb6d2278ff48b5bfb65cc969a20b8a" dependencies = [ "anyhow", "async-nats", diff --git a/Cargo.toml b/Cargo.toml index a697403f..08e4ebb1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,7 +56,7 @@ svc-error = { version = "0.5", features = [ "sentry-extension", "sqlx", ] } -svc-events = "0.7" +svc-events = { git = "https://github.com/foxford/svc-events", branch = "ULMS-1896/use-agent-id-intent" } svc-nats-client = { git = "https://github.com/foxford/svc-nats-client/", branch = "ULMS-1896/add-transient-permanent-errors" } svc-utils = { version = "0.7", features = ["log-middleware", "metrics-middleware", "cors-middleware", "authn-extractor"] } tokio = { version = "1.28", features = ["full"] } diff --git a/src/app/api/v1/account/ban.rs b/src/app/api/v1/account/ban.rs index 3ee0718e..ed3b657e 100644 --- a/src/app/api/v1/account/ban.rs +++ b/src/app/api/v1/account/ban.rs @@ -3,10 +3,10 @@ use std::sync::Arc; use axum::{extract::Path, response::Response, Extension, Json}; use hyper::Body; use serde_derive::Deserialize; -use svc_authn::AccountId; +use svc_authn::{AccountId, Authenticable}; use uuid::Uuid; -use svc_utils::extractors::AccountIdExtractor; +use svc_utils::extractors::AgentIdExtractor; use crate::{ app::{ @@ -30,7 +30,7 @@ pub struct BanPayload { pub async fn ban( Extension(ctx): Extension>, Path(account_to_ban): Path, - AccountIdExtractor(account_id): AccountIdExtractor, + AgentIdExtractor(agent_id): AgentIdExtractor, Json(payload): Json, ) -> AppResult { let class = find_class(ctx.as_ref(), payload.class_id) @@ -41,7 +41,7 @@ pub async fn ban( ctx.authz() .authorize( class.audience().to_owned(), - account_id.clone(), + agent_id.as_account_id().clone(), object, "update".into(), ) @@ -73,7 +73,7 @@ pub async fn ban( &mut conn, payload.ban, &class, - account_id, + agent_id, account_to_ban, payload.last_seen_op_id, ) diff --git a/src/app/stage/ban.rs b/src/app/stage/ban.rs index 033ea67e..1726fa90 100644 --- a/src/app/stage/ban.rs +++ b/src/app/stage/ban.rs @@ -1,4 +1,5 @@ use sqlx::PgConnection; +use svc_agent::AgentId; use svc_authn::AccountId; use svc_events::{ ban::{ @@ -31,7 +32,7 @@ pub async fn save_intent( conn: &mut PgConnection, ban: bool, class: &db::class::Object, - sender: AccountId, + sender: AgentId, target_account: AccountId, last_operation_id: i64, ) -> Result<(), Error> { @@ -43,7 +44,14 @@ pub async fn save_intent( target_account, last_operation_id, }; - nats::publish_event(ctx, class.id(), &event_id, event.into()).await + nats::publish_event( + ctx, + class.id(), + &event_id, + event.into(), + nats::Options::default(), + ) + .await } async fn get_next_event_id(conn: &mut PgConnection) -> Result { @@ -120,7 +128,14 @@ pub async fn accept( operation_id: intent_id.sequence_id(), }; - nats::publish_event(ctx, event.classroom_id, &event_id, event.into()).await + nats::publish_event( + ctx, + event.classroom_id, + &event_id, + event.into(), + nats::Options::default(), + ) + .await } async fn reject( @@ -140,8 +155,14 @@ async fn reject( intent_id.sequence_id(), ) .into(); - // TODO: publish as personal notification - nats::publish_event(ctx, event.classroom_id, &event_id, event.into()).await + nats::publish_event( + ctx, + event.classroom_id, + &event_id, + event.into(), + nats::Options::default().receiver_id(intent.sender), + ) + .await } pub async fn handle_video_streaming_banned( @@ -218,5 +239,12 @@ async fn finish( ) .into(); let event: BanCompletedV1 = event.into(); - nats::publish_event(ctx, event.classroom_id, &event_id, event.into()).await + nats::publish_event( + ctx, + event.classroom_id, + &event_id, + event.into(), + nats::Options::default(), + ) + .await } diff --git a/src/clients/nats.rs b/src/clients/nats.rs index 159862ae..4976b36d 100644 --- a/src/clients/nats.rs +++ b/src/clients/nats.rs @@ -1,3 +1,4 @@ +use svc_agent::AgentId; use uuid::Uuid; use svc_events::{Event, EventId}; @@ -9,11 +10,31 @@ use crate::app::{ const SUBJECT_PREFIX: &str = "classroom"; +pub struct Options { + receiver_id: Option, +} + +impl Options { + pub fn receiver_id(self, r_id: AgentId) -> Self { + Self { + receiver_id: Some(r_id), + ..self + } + } +} + +impl Default for Options { + fn default() -> Self { + Self { receiver_id: None } + } +} + pub async fn publish_event( ctx: &dyn AppContext, classroom_id: Uuid, id: &EventId, event: Event, + opts: Options, ) -> Result<(), Error> { let subject = svc_nats_client::Subject::new( SUBJECT_PREFIX.to_string(), @@ -23,13 +44,19 @@ pub async fn publish_event( let payload = serde_json::to_vec(&event).error(AppErrorKind::InternalFailure)?; - let event = svc_nats_client::event::Builder::new( + let event_b = svc_nats_client::event::Builder::new( subject, payload, id.to_owned(), ctx.agent_id().to_owned(), - ) - .build(); + ); + + let event_b = match opts.receiver_id { + Some(r_id) => event_b.receiver_id(r_id), + None => event_b, + }; + + let event = event_b.build(); ctx.nats_client() .ok_or_else(|| anyhow!("nats client not found")) From 111d3dcff5bebc68f7e42ef4ecc0b3abb6c350f2 Mon Sep 17 00:00:00 2001 From: Dmitry Shlagoff Date: Tue, 6 Jun 2023 15:05:33 +0200 Subject: [PATCH 25/35] Fix clippy --- src/app/api/v1/class/create_timestamp.rs | 2 +- src/app/api/v1/class/read.rs | 21 ++++++++----------- src/app/api/v1/class/recreate.rs | 2 +- .../minigroup/tests.rs | 10 ++++----- src/clients/nats.rs | 8 +------ src/test_helpers/agent.rs | 6 +++--- src/test_helpers/mod.rs | 6 +++--- src/test_helpers/state.rs | 2 +- 8 files changed, 24 insertions(+), 33 deletions(-) diff --git a/src/app/api/v1/class/create_timestamp.rs b/src/app/api/v1/class/create_timestamp.rs index db9998b8..4d228bfa 100644 --- a/src/app/api/v1/class/create_timestamp.rs +++ b/src/app/api/v1/class/create_timestamp.rs @@ -175,7 +175,7 @@ mod create_timestamp_tests { let body = response.into_body(); let body = hyper::body::to_bytes(body).await.unwrap(); let body = std::str::from_utf8(&body).unwrap(); - let body = serde_json::from_str::(&body).unwrap(); + let body = serde_json::from_str::(body).unwrap(); let position = body.get("position").unwrap().as_i64().unwrap(); assert_eq!(position, 50); } diff --git a/src/app/api/v1/class/read.rs b/src/app/api/v1/class/read.rs index e36e4ecf..5b8c941c 100644 --- a/src/app/api/v1/class/read.rs +++ b/src/app/api/v1/class/read.rs @@ -244,7 +244,8 @@ mod tests { let webinar = { let mut conn = db_pool.get_conn().await; - let webinar = factory::Webinar::new( + + factory::Webinar::new( random_string(), USR_AUDIENCE.to_string(), (Bound::Unbounded, Bound::Unbounded).into(), @@ -252,9 +253,7 @@ mod tests { Uuid::new_v4(), ) .insert(&mut conn) - .await; - - webinar + .await }; let mut authz = TestAuthz::new(); @@ -289,16 +288,15 @@ mod tests { let p2p = { let mut conn = db_pool.get_conn().await; - let p2p = factory::P2P::new( + + factory::P2P::new( random_string(), USR_AUDIENCE.to_string(), Uuid::new_v4(), Uuid::new_v4(), ) .insert(&mut conn) - .await; - - p2p + .await }; let mut authz = TestAuthz::new(); @@ -341,7 +339,8 @@ mod tests { let webinar = { let mut conn = db_pool.get_conn().await; - let webinar = factory::Webinar::new( + + factory::Webinar::new( random_string(), USR_AUDIENCE.to_string(), (Bound::Unbounded, Bound::Unbounded).into(), @@ -350,9 +349,7 @@ mod tests { ) .properties(properties) .insert(&mut conn) - .await; - - webinar + .await }; let mut authz = TestAuthz::new(); diff --git a/src/app/api/v1/class/recreate.rs b/src/app/api/v1/class/recreate.rs index 290d0b0f..1744b0b3 100644 --- a/src/app/api/v1/class/recreate.rs +++ b/src/app/api/v1/class/recreate.rs @@ -221,7 +221,7 @@ mod tests { // Assert DB changes. let mut conn = state.get_conn().await.expect("Failed to get conn"); - let new_webinar = WebinarReadQuery::by_scope(USR_AUDIENCE, &webinar.scope()) + let new_webinar = WebinarReadQuery::by_scope(USR_AUDIENCE, webinar.scope()) .execute(&mut conn) .await .expect("Failed to fetch webinar") diff --git a/src/app/postprocessing_strategy/minigroup/tests.rs b/src/app/postprocessing_strategy/minigroup/tests.rs index edbc5c11..4bfc660b 100644 --- a/src/app/postprocessing_strategy/minigroup/tests.rs +++ b/src/app/postprocessing_strategy/minigroup/tests.rs @@ -969,19 +969,19 @@ mod collect_pinned_events { .room_id(event_room_id) .set(PIN_EVENT_TYPE.to_string()) .data(EventData::Pin(PinEventData::new(agent_id.to_owned()))) - .occurred_at(2041237_463_815) + .occurred_at(2_041_237_463_815) .build(), EventBuilder::new() .room_id(event_room_id) .set(PIN_EVENT_TYPE.to_string()) .data(EventData::Pin(PinEventData::new(agent_id.to_owned()))) - .occurred_at(2041238_581_600) + .occurred_at(2_041_238_581_600) .build(), EventBuilder::new() .room_id(event_room_id) .set(PIN_EVENT_TYPE.to_string()) .data(EventData::Pin(PinEventData::null())) - .occurred_at(2093817_792_770) + .occurred_at(2_093_817_792_770) .build(), ]; @@ -1013,13 +1013,13 @@ mod collect_pinned_events { .room_id(event_room_id) .set(PIN_EVENT_TYPE.to_string()) .data(EventData::Pin(PinEventData::new(agent_id.to_owned()))) - .occurred_at(3312020_000_001) + .occurred_at(3_312_020_000_001) .build(), EventBuilder::new() .room_id(event_room_id) .set(PIN_EVENT_TYPE.to_string()) .data(EventData::Pin(PinEventData::null())) - .occurred_at(3312020_000_003) + .occurred_at(3_312_020_000_003) .build(), ]; diff --git a/src/clients/nats.rs b/src/clients/nats.rs index 4976b36d..156d4719 100644 --- a/src/clients/nats.rs +++ b/src/clients/nats.rs @@ -10,6 +10,7 @@ use crate::app::{ const SUBJECT_PREFIX: &str = "classroom"; +#[derive(Default)] pub struct Options { receiver_id: Option, } @@ -18,17 +19,10 @@ impl Options { pub fn receiver_id(self, r_id: AgentId) -> Self { Self { receiver_id: Some(r_id), - ..self } } } -impl Default for Options { - fn default() -> Self { - Self { receiver_id: None } - } -} - pub async fn publish_event( ctx: &dyn AppContext, classroom_id: Uuid, diff --git a/src/test_helpers/agent.rs b/src/test_helpers/agent.rs index 9f7a8b11..306aac9b 100644 --- a/src/test_helpers/agent.rs +++ b/src/test_helpers/agent.rs @@ -21,7 +21,7 @@ pub struct TestAgent { impl TestAgent { pub fn new(agent_label: &str, account_label: &str, audience: &str) -> Self { let account_id = AccountId::new(account_label, audience); - let agent_id = AgentId::new(agent_label, account_id.clone()); + let agent_id = AgentId::new(agent_label, account_id); let address = Address::new(agent_id, API_VERSION); Self { address } } @@ -31,11 +31,11 @@ impl TestAgent { } pub fn agent_id(&self) -> &AgentId { - &self.address.id() + self.address.id() } pub fn account_id(&self) -> &AccountId { - &self.address.id().as_account_id() + self.address.id().as_account_id() } pub fn token(&self) -> String { diff --git a/src/test_helpers/mod.rs b/src/test_helpers/mod.rs index 145932b3..b924e1ad 100644 --- a/src/test_helpers/mod.rs +++ b/src/test_helpers/mod.rs @@ -1,6 +1,6 @@ -pub const SVC_AUDIENCE: &'static str = "dev.svc.example.org"; -pub const USR_AUDIENCE: &'static str = "dev.usr.example.com"; -pub const TOKEN_ISSUER: &'static str = "iam.svc.example.com"; +pub const SVC_AUDIENCE: &str = "dev.svc.example.org"; +pub const USR_AUDIENCE: &str = "dev.usr.example.com"; +pub const TOKEN_ISSUER: &str = "iam.svc.example.com"; pub const PUBKEY_PATH: &str = "data/keys/svc.public_key.p8.der.sample"; pub mod prelude { diff --git a/src/test_helpers/state.rs b/src/test_helpers/state.rs index d6dff5fa..8e8e574a 100644 --- a/src/test_helpers/state.rs +++ b/src/test_helpers/state.rs @@ -186,7 +186,7 @@ impl TestState { } pub fn set_turn_hosts(&mut self, hosts: &[&str]) { - let hosts = hosts.into_iter().map(|c| (*c).into()).collect::>(); + let hosts = hosts.iter().map(|c| (*c).into()).collect::>(); let hosts = Vec1::try_from_vec(hosts).unwrap(); self.turn_host_selector = TurnHostSelector::new(&hosts); } From 47d04877f0a7bde7cbdfe515e38eefb3f26be92d Mon Sep 17 00:00:00 2001 From: Dmitry Shlagoff Date: Wed, 7 Jun 2023 12:31:45 +0200 Subject: [PATCH 26/35] Use latest crates from crates.io --- Cargo.lock | 67 ++++++++++++++++++++++++++++++++++++------------------ Cargo.toml | 4 ++-- 2 files changed, 47 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b51ee450..16569155 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -92,9 +92,12 @@ dependencies = [ [[package]] name = "async-nats" version = "0.29.0" -source = "git+https://github.com/foxford/nats.rs?branch=main#f5fa297c236dcca2e406761e5cc0fe78338a0ae0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1174495e436c928905018f10a36160f7a8a6786450f50f4ce7fba05d1539704c" dependencies = [ - "base64 0.21.2", + "async-nats-tokio-rustls-deps", + "base64 0.13.1", + "base64-url", "bytes", "futures", "http", @@ -108,7 +111,6 @@ dependencies = [ "ring", "rustls-native-certs", "rustls-pemfile", - "rustls-webpki", "serde", "serde_json", "serde_nanos", @@ -117,11 +119,21 @@ dependencies = [ "time 0.3.21", "tokio", "tokio-retry", - "tokio-rustls 0.24.0", "tracing", "url 2.4.0", ] +[[package]] +name = "async-nats-tokio-rustls-deps" +version = "0.24.0-ALPHA.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cdefe54cd7867d937c0a507d2a3a830af410044282cd3e4002b5b7860e1892e" +dependencies = [ + "rustls 0.21.1", + "tokio", + "webpki 0.22.0", +] + [[package]] name = "async-trait" version = "0.1.68" @@ -231,6 +243,15 @@ version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" +[[package]] +name = "base64-url" +version = "1.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a99c239d0c7e77c85dddfa9cebce48704b3c49550fcd3b84dd637e4484899f" +dependencies = [ + "base64 0.13.1", +] + [[package]] name = "base64ct" version = "1.1.1" @@ -2236,8 +2257,8 @@ dependencies = [ "pollster", "thiserror", "tokio", - "tokio-rustls 0.22.0", - "webpki", + "tokio-rustls", + "webpki 0.21.4", ] [[package]] @@ -2297,7 +2318,7 @@ dependencies = [ "log", "ring", "sct 0.6.1", - "webpki", + "webpki 0.21.4", ] [[package]] @@ -3011,8 +3032,9 @@ dependencies = [ [[package]] name = "svc-events" -version = "0.7.0" -source = "git+https://github.com/foxford/svc-events?branch=ULMS-1896/use-agent-id-intent#330d270763929832d32f9f15df448ac95322674a" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a30b9e3f342270165a9e7543f9ba85a45bff586cc04f4d495ffd8b4a44447974" dependencies = [ "serde", "serde_json", @@ -3024,8 +3046,9 @@ dependencies = [ [[package]] name = "svc-nats-client" -version = "0.4.0" -source = "git+https://github.com/foxford/svc-nats-client/?branch=ULMS-1896/add-transient-permanent-errors#7d5ecf35b2cb6d2278ff48b5bfb65cc969a20b8a" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c35276c77ae7ff528a5fb243f16bf95065a3ac8c91837356f2ee797c9a697203" dependencies = [ "anyhow", "async-nats", @@ -3280,17 +3303,7 @@ checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6" dependencies = [ "rustls 0.19.1", "tokio", - "webpki", -] - -[[package]] -name = "tokio-rustls" -version = "0.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0d409377ff5b1e3ca6437aa86c1eb7d40c134bfec254e44c830defa92669db5" -dependencies = [ - "rustls 0.21.1", - "tokio", + "webpki 0.21.4", ] [[package]] @@ -3734,6 +3747,16 @@ dependencies = [ "untrusted", ] +[[package]] +name = "webpki" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "whoami" version = "1.4.0" diff --git a/Cargo.toml b/Cargo.toml index 08e4ebb1..2aba090a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,8 +56,8 @@ svc-error = { version = "0.5", features = [ "sentry-extension", "sqlx", ] } -svc-events = { git = "https://github.com/foxford/svc-events", branch = "ULMS-1896/use-agent-id-intent" } -svc-nats-client = { git = "https://github.com/foxford/svc-nats-client/", branch = "ULMS-1896/add-transient-permanent-errors" } +svc-events = "0.9" +svc-nats-client = "0.5" svc-utils = { version = "0.7", features = ["log-middleware", "metrics-middleware", "cors-middleware", "authn-extractor"] } tokio = { version = "1.28", features = ["full"] } tower = { version = "0.4", features = [ "timeout" ] } From aecb21ba2c754a12d194bf66a16630012e61e99d Mon Sep 17 00:00:00 2001 From: Dmitry Shlagoff Date: Thu, 8 Jun 2023 11:47:58 +0200 Subject: [PATCH 27/35] Add docs about ban endpoint --- docs/src/SUMMARY.md | 1 + docs/src/utils/ban.md | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 docs/src/utils/ban.md diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index 8613b757..02b244a9 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -16,3 +16,4 @@ - [API](minigroups/api.md) - [Classes API](classes/api.md) - [Transcoding utils](utils/transcoding.md) + - [Ban](utils/ban.md) diff --git a/docs/src/utils/ban.md b/docs/src/utils/ban.md new file mode 100644 index 00000000..b1b4d555 --- /dev/null +++ b/docs/src/utils/ban.md @@ -0,0 +1,24 @@ +# API + +All routes expect json payloads. + +### Routes +Route | Method | Short description +------------------------------- | ------ | ---------- +/api/v1/account/:account_id/ban | POST | [Bans](#ban) media stream and collaboration for user + +### Ban + +Path variable | Type | Optional | Description +---------------------- | ----------- | -------- | -------------- +account_id | string | | Account id we want to ban + +JSON payload is required with following attributes: + +Attribute | Type | Optional | Description +---------------------- | ----------- | -------- | -------------- +last_seen_op_id | integer | | Last seen operation id which can be found in account info +ban | bool | | Ban/unban +class_id | uuid | | User should be banned in this particular classroom + +Response: status 200 and empty payload or json object with an error description. From 0610782f59d953d285313f15268e3c92b0be8c98 Mon Sep 17 00:00:00 2001 From: Dmitry Shlagoff Date: Thu, 8 Jun 2023 12:21:54 +0200 Subject: [PATCH 28/35] Add endpoint to get last ban operation id --- docs/src/utils/ban.md | 20 +++++++++++++++++++- src/app/api/v1/account/ban.rs | 33 +++++++++++++++++++++++++++++++-- src/app/api/v1/account/mod.rs | 4 +--- src/app/http.rs | 5 ++++- 4 files changed, 55 insertions(+), 7 deletions(-) diff --git a/docs/src/utils/ban.md b/docs/src/utils/ban.md index b1b4d555..cd4c06a0 100644 --- a/docs/src/utils/ban.md +++ b/docs/src/utils/ban.md @@ -2,10 +2,15 @@ All routes expect json payloads. +1. Get last ban operation id. +2. Send it in POST request along with other data. +If there's an error, return to 1. + ### Routes Route | Method | Short description ------------------------------- | ------ | ---------- /api/v1/account/:account_id/ban | POST | [Bans](#ban) media stream and collaboration for user +/api/v1/account/:account_id/ban | GET | [Gets](#get-last-ban-operation) last ban operation id for user ### Ban @@ -17,8 +22,21 @@ JSON payload is required with following attributes: Attribute | Type | Optional | Description ---------------------- | ----------- | -------- | -------------- -last_seen_op_id | integer | | Last seen operation id which can be found in account info +last_seen_op_id | integer | | Last seen operation id which can be found by [GET request](#get-last-ban-operation) ban | bool | | Ban/unban class_id | uuid | | User should be banned in this particular classroom Response: status 200 and empty payload or json object with an error description. + +### Get last ban operation + +Path variable | Type | Optional | Description +---------------------- | ----------- | -------- | -------------- +account_id | string | | Account id we want to ban + +No JSON payload is expected. + +Response: status 200 and following payload or json object with an error description. + +Attribute | Type | Optional | Description +last_seen_op_id | integer | | Last seen operation id which can be used later in POST request diff --git a/src/app/api/v1/account/ban.rs b/src/app/api/v1/account/ban.rs index ed3b657e..c8d2e485 100644 --- a/src/app/api/v1/account/ban.rs +++ b/src/app/api/v1/account/ban.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use axum::{extract::Path, response::Response, Extension, Json}; use hyper::Body; -use serde_derive::Deserialize; +use serde_derive::{Deserialize, Serialize}; use svc_authn::{AccountId, Authenticable}; use uuid::Uuid; @@ -10,7 +10,7 @@ use svc_utils::extractors::AgentIdExtractor; use crate::{ app::{ - api::v1::find_class, + api::{v1::find_class, IntoJsonResponse}, error::{ErrorExt, ErrorKind as AppErrorKind}, metrics::AuthorizeMetrics, stage, AppContext, AuthzObject, @@ -81,3 +81,32 @@ pub async fn ban( Ok(Response::builder().status(200).body(Body::empty()).unwrap()) } + +#[derive(Serialize)] +pub struct GetLastBanOperationResponse { + last_seen_op_id: i64, +} + +pub async fn get_last_ban_operation( + Extension(ctx): Extension>, + Path(account_to_ban): Path, +) -> AppResult { + let mut conn = ctx + .get_conn() + .await + .error(AppErrorKind::DbConnAcquisitionFailed)?; + + let last_ban_account_op = ban_account_op::ReadQuery::by_account_id(&account_to_ban) + .execute(&mut conn) + .await + .error(AppErrorKind::DbQueryFailed)?; + + let r = GetLastBanOperationResponse { + last_seen_op_id: last_ban_account_op.map(|l| l.last_op_id).unwrap_or(0), + }; + + r.into_json_response( + "failed to serialize last ban operation", + http::StatusCode::OK, + ) +} diff --git a/src/app/api/v1/account/mod.rs b/src/app/api/v1/account/mod.rs index e28d2596..fb774e6c 100644 --- a/src/app/api/v1/account/mod.rs +++ b/src/app/api/v1/account/mod.rs @@ -4,9 +4,7 @@ use axum::{extract::Path, Extension}; use svc_agent::AccountId; use svc_utils::extractors::AccountIdExtractor; -mod ban; - -pub use ban::ban; +pub mod ban; use crate::{ app::{ diff --git a/src/app/http.rs b/src/app/http.rs index 042e6189..d4c99971 100644 --- a/src/app/http.rs +++ b/src/app/http.rs @@ -158,7 +158,10 @@ fn utils_router() -> Router { "/api/v1/account/properties/:property_id", get(account::read_property).put(account::update_property), ) - .metered_route("/api/v1/account/:id/ban", post(account::ban)) + .metered_route( + "/api/v1/account/:id/ban", + get(account::ban::get_last_ban_operation).post(account::ban::ban), + ) .metered_route( "/api/v1/transcoding/minigroup/:id/restart", post(restart_transcoding_minigroup), From c4a58038469f007628071e5538e6a23e3a0009cf Mon Sep 17 00:00:00 2001 From: Dmitry Shlagoff Date: Tue, 13 Jun 2023 12:03:40 +0200 Subject: [PATCH 29/35] Update chart with nats config --- chart/templates/app-cm.yaml | 20 ++++++++++++++++++++ chart/values.yaml | 3 +++ 2 files changed, 23 insertions(+) diff --git a/chart/templates/app-cm.yaml b/chart/templates/app-cm.yaml index 09cc16c1..5ae2da5e 100644 --- a/chart/templates/app-cm.yaml +++ b/chart/templates/app-cm.yaml @@ -98,6 +98,26 @@ data: {{- end }} {{- end }} + {{- with .Values.nats }} + {{- println "" }} + [nats] + url = {{ .url | quote }} + creds = {{ .creds | quote }} + subscribe.stream = {{ .subscribe.stream | quote }} + subscribe.consumer = {{ .subscribe.consumer | quote }} + subscribe.batch = {{ .subscribe.batch }} + subscribe.idle_heartbeat = {{ .subscribe.idle_heartbeat | quote }} + {{- end }} + + {{- with .Values.nats_consumer }} + {{- println "" }} + [nats_consumer] + suspend_interval = {{ .suspend_interval | quote }} + max_suspend_interval = {{ .max_suspend_interval | quote }} + suspend_sentry_interval = {{ .suspend_sentry_interval | quote }} + resubscribe_interval = {{ .resubscribe_interval | quote }} + {{- end }} + [storage] {{- if .Values.app.storage }} base_url = {{ .Values.app.storage.url | quote }} diff --git a/chart/values.yaml b/chart/values.yaml index 56093ef0..1820a5f6 100644 --- a/chart/values.yaml +++ b/chart/values.yaml @@ -48,6 +48,9 @@ clusterService: http: 8080 metrics: 8888 +nats: {} +nats_consumer: {} + tls: secretName: tls-certificates From 94600c63d4e3da9ccb2bbd602a21efa9d98d71b8 Mon Sep 17 00:00:00 2001 From: Dmitry Shlagoff Date: Tue, 13 Jun 2023 12:44:44 +0200 Subject: [PATCH 30/35] Fix config values --- chart/templates/app-cm.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/chart/templates/app-cm.yaml b/chart/templates/app-cm.yaml index 5ae2da5e..8d0c49a0 100644 --- a/chart/templates/app-cm.yaml +++ b/chart/templates/app-cm.yaml @@ -103,10 +103,10 @@ data: [nats] url = {{ .url | quote }} creds = {{ .creds | quote }} - subscribe.stream = {{ .subscribe.stream | quote }} - subscribe.consumer = {{ .subscribe.consumer | quote }} - subscribe.batch = {{ .subscribe.batch }} - subscribe.idle_heartbeat = {{ .subscribe.idle_heartbeat | quote }} + subscribe_durable.stream = {{ .subscribe_durable.stream | quote }} + subscribe_durable.consumer = {{ .subscribe_durable.consumer | quote }} + subscribe_durable.batch = {{ .subscribe_durable.batch }} + subscribe_durable.idle_heartbeat = {{ .subscribe_durable.idle_heartbeat | quote }} {{- end }} {{- with .Values.nats_consumer }} From 76666b1446e9e25101e513a2f7d1d38cb13b0a76 Mon Sep 17 00:00:00 2001 From: Dmitry Shlagoff Date: Tue, 11 Jul 2023 13:59:55 +0200 Subject: [PATCH 31/35] Add trace --- src/app/stage/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/app/stage/mod.rs b/src/app/stage/mod.rs index 423b6ded..fc5a9d76 100644 --- a/src/app/stage/mod.rs +++ b/src/app/stage/mod.rs @@ -45,6 +45,8 @@ pub async fn route_message( .permanent()? }; + tracing::info!(?event, class_id = %classroom_id); + let headers = svc_nats_client::Headers::try_from(msg.headers.clone().unwrap_or_default()) .context("parse nats headers") .permanent()?; From 1004e14d4cbd170f2854a85a78cc4d5ff9b3d1ff Mon Sep 17 00:00:00 2001 From: Dmitry Shlagoff Date: Tue, 11 Jul 2023 16:11:51 +0200 Subject: [PATCH 32/35] Fix ban op upsert query --- sqlx-data.json | 148 +++++++++++++++++++-------------------- src/db/ban_account_op.rs | 4 +- 2 files changed, 76 insertions(+), 76 deletions(-) diff --git a/sqlx-data.json b/sqlx-data.json index 94011d06..5ac044a2 100644 --- a/sqlx-data.json +++ b/sqlx-data.json @@ -835,80 +835,6 @@ }, "query": "\n SELECT\n user_account AS \"user_account: _\",\n last_op_id AS \"last_op_id: _\",\n is_video_streaming_banned,\n is_collaboration_banned\n FROM ban_account_op\n WHERE\n user_account = $1\n LIMIT 1;\n " }, - "5a9a081e5b9f08f8558f4549b51dc1183af3da3e34c7de7e5c2bbac6590c0e17": { - "describe": { - "columns": [ - { - "name": "user_account: _", - "ordinal": 0, - "type_info": { - "Custom": { - "kind": { - "Composite": [ - [ - "label", - "Text" - ], - [ - "audience", - "Text" - ] - ] - }, - "name": "account_id" - } - } - }, - { - "name": "last_op_id", - "ordinal": 1, - "type_info": "Int8" - }, - { - "name": "is_video_streaming_banned", - "ordinal": 2, - "type_info": "Bool" - }, - { - "name": "is_collaboration_banned", - "ordinal": 3, - "type_info": "Bool" - } - ], - "nullable": [ - false, - false, - false, - false - ], - "parameters": { - "Left": [ - { - "Custom": { - "kind": { - "Composite": [ - [ - "label", - "Text" - ], - [ - "audience", - "Text" - ] - ] - }, - "name": "account_id" - } - }, - "Int8", - "Bool", - "Bool", - "Int8" - ] - } - }, - "query": "\n INSERT INTO ban_account_op (user_account, last_op_id, is_video_streaming_banned, is_collaboration_banned)\n VALUES ($1, $2, COALESCE($3, false), COALESCE($4, false))\n ON CONFLICT (user_account) DO UPDATE\n SET\n is_video_streaming_banned = COALESCE(EXCLUDED.is_video_streaming_banned, ban_account_op.is_video_streaming_banned),\n is_collaboration_banned = COALESCE(EXCLUDED.is_collaboration_banned, ban_account_op.is_collaboration_banned),\n last_op_id = EXCLUDED.last_op_id\n WHERE\n -- allow to 'complete' operation if there's no change in last_op_id\n -- or allow to do upsert without real changes so we can process\n -- the same message twice\n ban_account_op.last_op_id = EXCLUDED.last_op_id OR\n -- allow change op id if the previous operation is completed\n (\n ban_account_op.last_op_id = $5 AND\n ban_account_op.is_video_streaming_banned = true AND\n ban_account_op.is_collaboration_banned = true\n )\n RETURNING\n user_account AS \"user_account: _\",\n last_op_id,\n is_video_streaming_banned,\n is_collaboration_banned\n " - }, "6016a361f9fb26d49797872a086033a34a01b4f1038125486a47c0cc8713e209": { "describe": { "columns": [ @@ -1949,6 +1875,80 @@ }, "query": "\n INSERT INTO class (\n scope, audience, time, tags, preserve_history, kind,\n conference_room_id, event_room_id,\n original_event_room_id, modified_event_room_id, reserve, room_events_uri,\n established, properties, original_class_id\n )\n VALUES ($1, $2, $3, $4, $5, $6::class_type, $7, $8, $9, $10, $11, $12, $13, $14, $15)\n ON CONFLICT (scope, audience)\n DO UPDATE\n SET time = EXCLUDED.time,\n tags = EXCLUDED.tags,\n preserve_history = EXCLUDED.preserve_history,\n reserve = EXCLUDED.reserve,\n properties = EXCLUDED.properties\n WHERE class.established = 'f'\n RETURNING\n id,\n kind AS \"kind!: ClassType\",\n scope,\n time AS \"time!: Time\",\n audience,\n created_at,\n tags,\n preserve_history,\n reserve,\n properties AS \"properties: _\",\n original_class_id,\n content_id\n " }, + "c6ce697aaad621b2230880c9d46251dffd92dc490f5473cd2ca32a8627dae130": { + "describe": { + "columns": [ + { + "name": "user_account: _", + "ordinal": 0, + "type_info": { + "Custom": { + "kind": { + "Composite": [ + [ + "label", + "Text" + ], + [ + "audience", + "Text" + ] + ] + }, + "name": "account_id" + } + } + }, + { + "name": "last_op_id", + "ordinal": 1, + "type_info": "Int8" + }, + { + "name": "is_video_streaming_banned", + "ordinal": 2, + "type_info": "Bool" + }, + { + "name": "is_collaboration_banned", + "ordinal": 3, + "type_info": "Bool" + } + ], + "nullable": [ + false, + false, + false, + false + ], + "parameters": { + "Left": [ + { + "Custom": { + "kind": { + "Composite": [ + [ + "label", + "Text" + ], + [ + "audience", + "Text" + ] + ] + }, + "name": "account_id" + } + }, + "Int8", + "Bool", + "Bool", + "Int8" + ] + } + }, + "query": "\n INSERT INTO ban_account_op (user_account, last_op_id, is_video_streaming_banned, is_collaboration_banned)\n VALUES ($1, $2, COALESCE($3, false), COALESCE($4, false))\n ON CONFLICT (user_account) DO UPDATE\n SET\n is_video_streaming_banned = COALESCE($3, ban_account_op.is_video_streaming_banned),\n is_collaboration_banned = COALESCE($4, ban_account_op.is_collaboration_banned),\n last_op_id = EXCLUDED.last_op_id\n WHERE\n -- allow to 'complete' operation if there's no change in last_op_id\n -- or allow to do upsert without real changes so we can process\n -- the same message twice\n ban_account_op.last_op_id = EXCLUDED.last_op_id OR\n -- allow change op id if the previous operation is completed\n (\n ban_account_op.last_op_id = $5 AND\n ban_account_op.is_video_streaming_banned = true AND\n ban_account_op.is_collaboration_banned = true\n )\n RETURNING\n user_account AS \"user_account: _\",\n last_op_id,\n is_video_streaming_banned,\n is_collaboration_banned\n " + }, "c7386d4717720730d7762fbcf2d24c5d0f508b246e987f4666684bf37b27ddc2": { "describe": { "columns": [ diff --git a/src/db/ban_account_op.rs b/src/db/ban_account_op.rs index b4f65dae..08d2b54e 100644 --- a/src/db/ban_account_op.rs +++ b/src/db/ban_account_op.rs @@ -107,8 +107,8 @@ impl UpsertQuery { VALUES ($1, $2, COALESCE($3, false), COALESCE($4, false)) ON CONFLICT (user_account) DO UPDATE SET - is_video_streaming_banned = COALESCE(EXCLUDED.is_video_streaming_banned, ban_account_op.is_video_streaming_banned), - is_collaboration_banned = COALESCE(EXCLUDED.is_collaboration_banned, ban_account_op.is_collaboration_banned), + is_video_streaming_banned = COALESCE($3, ban_account_op.is_video_streaming_banned), + is_collaboration_banned = COALESCE($4, ban_account_op.is_collaboration_banned), last_op_id = EXCLUDED.last_op_id WHERE -- allow to 'complete' operation if there's no change in last_op_id From c07fb5ef157b394c3b8c8c9d8f58d5a80c5f3180 Mon Sep 17 00:00:00 2001 From: Dmitry Shlagoff Date: Fri, 14 Jul 2023 21:50:10 +0200 Subject: [PATCH 33/35] Avoid listing recordings whose author were banned during class --- sqlx-data.json | 388 ++++++++++++++++++++++++++++++------------- src/app/stage/ban.rs | 27 ++- src/db/mod.rs | 1 + src/db/recording.rs | 34 ++-- 4 files changed, 316 insertions(+), 134 deletions(-) diff --git a/sqlx-data.json b/sqlx-data.json index 5ac044a2..8aeb70c0 100644 --- a/sqlx-data.json +++ b/sqlx-data.json @@ -1249,124 +1249,6 @@ }, "query": "\n UPDATE recording\n SET modified_segments =\n CASE\n WHEN created_by = $3 THEN $2\n ELSE segments\n END,\n adjusted_at = NOW()\n WHERE class_id = $1 AND deleted_at IS NULL\n RETURNING\n id,\n class_id,\n rtc_id,\n stream_uri,\n segments AS \"segments!: Option\",\n started_at,\n modified_segments AS \"modified_segments!: Option\",\n created_at,\n adjusted_at,\n transcoded_at,\n created_by AS \"created_by: AgentId\",\n deleted_at\n " }, - "7296ad8d156f41d717c88c4f20c012bc010531d460776813fd70b3141485aabe": { - "describe": { - "columns": [ - { - "name": "id", - "ordinal": 0, - "type_info": "Uuid" - }, - { - "name": "class_id", - "ordinal": 1, - "type_info": "Uuid" - }, - { - "name": "rtc_id", - "ordinal": 2, - "type_info": "Uuid" - }, - { - "name": "stream_uri", - "ordinal": 3, - "type_info": "Text" - }, - { - "name": "segments!: Option", - "ordinal": 4, - "type_info": "Int8RangeArray" - }, - { - "name": "modified_segments!: Option", - "ordinal": 5, - "type_info": "Int8RangeArray" - }, - { - "name": "started_at", - "ordinal": 6, - "type_info": "Timestamptz" - }, - { - "name": "created_at", - "ordinal": 7, - "type_info": "Timestamptz" - }, - { - "name": "adjusted_at", - "ordinal": 8, - "type_info": "Timestamptz" - }, - { - "name": "transcoded_at", - "ordinal": 9, - "type_info": "Timestamptz" - }, - { - "name": "created_by: AgentId", - "ordinal": 10, - "type_info": { - "Custom": { - "kind": { - "Composite": [ - [ - "account_id", - { - "Custom": { - "kind": { - "Composite": [ - [ - "label", - "Text" - ], - [ - "audience", - "Text" - ] - ] - }, - "name": "account_id" - } - } - ], - [ - "label", - "Text" - ] - ] - }, - "name": "agent_id" - } - } - }, - { - "name": "deleted_at", - "ordinal": 11, - "type_info": "Timestamptz" - } - ], - "nullable": [ - false, - false, - false, - true, - true, - true, - true, - false, - true, - true, - false, - true - ], - "parameters": { - "Left": [ - "Uuid" - ] - } - }, - "query": "\n SELECT\n id,\n class_id,\n rtc_id,\n stream_uri,\n segments AS \"segments!: Option\",\n modified_segments AS \"modified_segments!: Option\",\n started_at,\n created_at,\n adjusted_at,\n transcoded_at,\n created_by AS \"created_by: AgentId\",\n deleted_at\n FROM recording\n WHERE class_id = $1 AND deleted_at IS NULL\n " - }, "73a64697ca7ad2d67c7b2a77cc1fd6c0fb7a2b262a30d8386e83f8cbd8be5a54": { "describe": { "columns": [ @@ -1753,6 +1635,90 @@ }, "query": "\n INSERT INTO recording (class_id, rtc_id, segments, modified_segments, stream_uri, started_at, adjusted_at, transcoded_at, created_by)\n VALUES ($1, $2, $3, $4, $5, NOW(), NOW(), NOW(), $6)\n RETURNING\n id,\n class_id,\n rtc_id,\n stream_uri,\n segments AS \"segments!: Option\",\n started_at,\n modified_segments AS \"modified_segments!: Option\",\n created_at,\n adjusted_at,\n transcoded_at,\n created_by AS \"created_by: AgentId\",\n deleted_at\n " }, + "b333f6270cc34a0b5d2cd2d23d39c705c219704ba7094dd078cbc2978146e534": { + "describe": { + "columns": [ + { + "name": "target_account: AccountId", + "ordinal": 0, + "type_info": { + "Custom": { + "kind": { + "Composite": [ + [ + "label", + "Text" + ], + [ + "audience", + "Text" + ] + ] + }, + "name": "account_id" + } + } + }, + { + "name": "class_id", + "ordinal": 1, + "type_info": "Uuid" + }, + { + "name": "banned_at", + "ordinal": 2, + "type_info": "Timestamptz" + }, + { + "name": "banned_operation_id", + "ordinal": 3, + "type_info": "Int8" + }, + { + "name": "unbanned_at", + "ordinal": 4, + "type_info": "Timestamptz" + }, + { + "name": "unbanned_operation_id", + "ordinal": 5, + "type_info": "Int8" + } + ], + "nullable": [ + false, + false, + false, + false, + true, + true + ], + "parameters": { + "Left": [ + "Uuid", + { + "Custom": { + "kind": { + "Composite": [ + [ + "label", + "Text" + ], + [ + "audience", + "Text" + ] + ] + }, + "name": "account_id" + } + }, + "Int8" + ] + } + }, + "query": "\n INSERT INTO ban_history (class_id, target_account, banned_operation_id)\n VALUES ($1, $2, $3)\n -- this allows us to run this query many times idempotently\n ON CONFLICT (banned_operation_id) DO NOTHING\n RETURNING\n target_account AS \"target_account: AccountId\",\n class_id,\n banned_at,\n banned_operation_id,\n unbanned_at,\n unbanned_operation_id\n " + }, "c67188dde7672c71f7e14a0ef09047934fbf808e5541e1b35c88004f36c16c8b": { "describe": { "columns": [ @@ -2107,6 +2073,74 @@ }, "query": "\n INSERT INTO recording (\n class_id, rtc_id, stream_uri, segments, modified_segments, started_at, adjusted_at,\n transcoded_at, created_by\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)\n ON CONFLICT (class_id, created_by)\n WHERE deleted_at IS NULL\n DO UPDATE\n SET (rtc_id, stream_uri, segments, modified_segments,\n started_at, adjusted_at, transcoded_at, created_by, created_at) =\n (EXCLUDED.rtc_id, EXCLUDED.stream_uri, EXCLUDED.segments, EXCLUDED.modified_segments, EXCLUDED.started_at, EXCLUDED.adjusted_at,\n EXCLUDED.transcoded_at, EXCLUDED.created_by, NOW())\n RETURNING\n id,\n class_id,\n rtc_id,\n stream_uri,\n segments AS \"segments!: Option\",\n started_at,\n modified_segments AS \"modified_segments!: Option\",\n created_at,\n adjusted_at,\n transcoded_at,\n created_by AS \"created_by: AgentId\",\n deleted_at\n " }, + "cd5e1f8fb8afcf43963183bbe0f1c481e5d65a49f9d4356c9b6bf2dbabc1644c": { + "describe": { + "columns": [ + { + "name": "target_account: AccountId", + "ordinal": 0, + "type_info": { + "Custom": { + "kind": { + "Composite": [ + [ + "label", + "Text" + ], + [ + "audience", + "Text" + ] + ] + }, + "name": "account_id" + } + } + }, + { + "name": "class_id", + "ordinal": 1, + "type_info": "Uuid" + }, + { + "name": "banned_at", + "ordinal": 2, + "type_info": "Timestamptz" + }, + { + "name": "banned_operation_id", + "ordinal": 3, + "type_info": "Int8" + }, + { + "name": "unbanned_at", + "ordinal": 4, + "type_info": "Timestamptz" + }, + { + "name": "unbanned_operation_id", + "ordinal": 5, + "type_info": "Int8" + } + ], + "nullable": [ + false, + false, + false, + false, + true, + true + ], + "parameters": { + "Left": [ + "Uuid", + "Record", + "Int8" + ] + } + }, + "query": "\n UPDATE ban_history\n SET\n unbanned_at = NOW(),\n unbanned_operation_id = $3\n WHERE\n class_id = $1\n AND target_account = $2\n AND unbanned_at IS NULL\n RETURNING\n target_account AS \"target_account: AccountId\",\n class_id,\n banned_at,\n banned_operation_id,\n unbanned_at,\n unbanned_operation_id\n " + }, "ce3fc3fce308f42126eaba1bde5ba12d340cce38b9f06fad4b6622b02de62cd9": { "describe": { "columns": [ @@ -2625,6 +2659,124 @@ }, "query": "\n UPDATE recording\n SET deleted_at = NOW()\n WHERE class_id = $1 AND deleted_at IS NULL\n " }, + "d5327cbe86cd24eef0fa68dff1ea943cb4f86d10663bbd9ad453342012a4e978": { + "describe": { + "columns": [ + { + "name": "id", + "ordinal": 0, + "type_info": "Uuid" + }, + { + "name": "class_id", + "ordinal": 1, + "type_info": "Uuid" + }, + { + "name": "rtc_id", + "ordinal": 2, + "type_info": "Uuid" + }, + { + "name": "stream_uri", + "ordinal": 3, + "type_info": "Text" + }, + { + "name": "segments!: Option", + "ordinal": 4, + "type_info": "Int8RangeArray" + }, + { + "name": "modified_segments!: Option", + "ordinal": 5, + "type_info": "Int8RangeArray" + }, + { + "name": "started_at", + "ordinal": 6, + "type_info": "Timestamptz" + }, + { + "name": "created_at", + "ordinal": 7, + "type_info": "Timestamptz" + }, + { + "name": "adjusted_at", + "ordinal": 8, + "type_info": "Timestamptz" + }, + { + "name": "transcoded_at", + "ordinal": 9, + "type_info": "Timestamptz" + }, + { + "name": "created_by: AgentId", + "ordinal": 10, + "type_info": { + "Custom": { + "kind": { + "Composite": [ + [ + "account_id", + { + "Custom": { + "kind": { + "Composite": [ + [ + "label", + "Text" + ], + [ + "audience", + "Text" + ] + ] + }, + "name": "account_id" + } + } + ], + [ + "label", + "Text" + ] + ] + }, + "name": "agent_id" + } + } + }, + { + "name": "deleted_at", + "ordinal": 11, + "type_info": "Timestamptz" + } + ], + "nullable": [ + false, + false, + false, + true, + true, + true, + true, + false, + true, + true, + false, + true + ], + "parameters": { + "Left": [ + "Uuid" + ] + } + }, + "query": "\n SELECT\n r.id,\n r.class_id,\n r.rtc_id,\n r.stream_uri,\n r.segments AS \"segments!: Option\",\n r.modified_segments AS \"modified_segments!: Option\",\n r.started_at,\n r.created_at,\n r.adjusted_at,\n r.transcoded_at,\n r.created_by AS \"created_by: AgentId\",\n r.deleted_at\n FROM recording AS r\n LEFT JOIN ban_history AS bh\n ON r.class_id = bh.class_id\n AND bh.target_account = (r.created_by).account_id\n WHERE\n r.class_id = $1\n AND r.deleted_at IS NULL\n AND bh.banned_operation_id IS NULL\n " + }, "d7263981e0de43bae99ecbd69405e87e614ff3aa84a1379a01a25be6f13f6687": { "describe": { "columns": [ diff --git a/src/app/stage/ban.rs b/src/app/stage/ban.rs index 1726fa90..de696a19 100644 --- a/src/app/stage/ban.rs +++ b/src/app/stage/ban.rs @@ -15,7 +15,7 @@ use crate::{ AppContext, }, clients::nats, - db::{self, ban_account_op}, + db::{self, ban_account_op, ban_history}, }; use super::{FailureKind, HandleMessageFailure}; @@ -96,7 +96,29 @@ pub async fn handle_intent( .transient()?; match op { - Some(_) => { + Some(op) => { + if intent.ban { + ban_history::InsertQuery::new( + intent.classroom_id, + &intent.target_account, + op.last_op_id, + ) + .execute(&mut conn) + .await + .error(AppErrorKind::DbQueryFailed) + .transient()?; + } else { + ban_history::FinishBanQuery::new( + intent.classroom_id, + &intent.target_account, + op.last_op_id, + ) + .execute(&mut conn) + .await + .error(AppErrorKind::DbQueryFailed) + .transient()?; + } + super::ban::accept(ctx, intent, intent_id) .await .transient()?; @@ -121,6 +143,7 @@ pub async fn accept( intent_id.sequence_id(), ) .into(); + let event = BanAcceptedV1 { ban: intent.ban, classroom_id: intent.classroom_id, diff --git a/src/db/mod.rs b/src/db/mod.rs index b1a406dd..829c79b9 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -22,6 +22,7 @@ pub async fn create_pool( pub mod account; pub mod authz; pub mod ban_account_op; +pub mod ban_history; pub mod class; pub mod frontend; pub mod record_timestamp; diff --git a/src/db/recording.rs b/src/db/recording.rs index cf6f7a30..61afbf7f 100644 --- a/src/db/recording.rs +++ b/src/db/recording.rs @@ -123,20 +123,26 @@ impl RecordingListQuery { Object, r#" SELECT - id, - class_id, - rtc_id, - stream_uri, - segments AS "segments!: Option", - modified_segments AS "modified_segments!: Option", - started_at, - created_at, - adjusted_at, - transcoded_at, - created_by AS "created_by: AgentId", - deleted_at - FROM recording - WHERE class_id = $1 AND deleted_at IS NULL + r.id, + r.class_id, + r.rtc_id, + r.stream_uri, + r.segments AS "segments!: Option", + r.modified_segments AS "modified_segments!: Option", + r.started_at, + r.created_at, + r.adjusted_at, + r.transcoded_at, + r.created_by AS "created_by: AgentId", + r.deleted_at + FROM recording AS r + LEFT JOIN ban_history AS bh + ON r.class_id = bh.class_id + AND bh.target_account = (r.created_by).account_id + WHERE + r.class_id = $1 + AND r.deleted_at IS NULL + AND bh.banned_operation_id IS NULL "#, self.class_id ) From ad3e5a29d82c4f96c57c3ea689dde5b459ea0da5 Mon Sep 17 00:00:00 2001 From: Dmitry Shlagoff Date: Mon, 17 Jul 2023 14:48:30 +0200 Subject: [PATCH 34/35] Add missing files --- migrations/20230714081909_add_ban_history.sql | 11 ++ src/db/ban_history.rs | 103 ++++++++++++++++++ 2 files changed, 114 insertions(+) create mode 100644 migrations/20230714081909_add_ban_history.sql create mode 100644 src/db/ban_history.rs diff --git a/migrations/20230714081909_add_ban_history.sql b/migrations/20230714081909_add_ban_history.sql new file mode 100644 index 00000000..ea4d055b --- /dev/null +++ b/migrations/20230714081909_add_ban_history.sql @@ -0,0 +1,11 @@ +CREATE TABLE IF NOT EXISTS ban_history ( + target_account account_id NOT NULL, + class_id uuid NOT NULL, + banned_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + banned_operation_id BIGINT NOT NULL, + unbanned_at TIMESTAMPTZ, + unbanned_operation_id BIGINT, + + FOREIGN KEY (class_id) REFERENCES class (id) ON DELETE CASCADE, + PRIMARY KEY (banned_operation_id) +); diff --git a/src/db/ban_history.rs b/src/db/ban_history.rs new file mode 100644 index 00000000..36accf4c --- /dev/null +++ b/src/db/ban_history.rs @@ -0,0 +1,103 @@ +use chrono::{DateTime, Utc}; +use svc_authn::AccountId; +use uuid::Uuid; + +#[derive(Debug)] +pub struct Object { + #[allow(unused)] + target_account: AccountId, + #[allow(unused)] + class_id: Uuid, + #[allow(unused)] + banned_at: DateTime, + #[allow(unused)] + banned_operation_id: i64, + #[allow(unused)] + unbanned_at: Option>, + #[allow(unused)] + unbanned_operation_id: Option, +} + +pub struct InsertQuery<'a> { + class_id: Uuid, + target_account: &'a AccountId, + operation_id: i64, +} + +impl<'a> InsertQuery<'a> { + pub fn new(class_id: Uuid, target_account: &'a AccountId, operation_id: i64) -> Self { + Self { + class_id, + target_account, + operation_id, + } + } + + pub async fn execute(&self, conn: &mut sqlx::PgConnection) -> sqlx::Result { + sqlx::query_as!( + Object, + r#" + INSERT INTO ban_history (class_id, target_account, banned_operation_id) + VALUES ($1, $2, $3) + -- this allows us to run this query many times idempotently + ON CONFLICT (banned_operation_id) DO NOTHING + RETURNING + target_account AS "target_account: AccountId", + class_id, + banned_at, + banned_operation_id, + unbanned_at, + unbanned_operation_id + "#, + self.class_id, + self.target_account as &AccountId, + self.operation_id, + ) + .fetch_one(conn) + .await + } +} + +pub struct FinishBanQuery<'a> { + class_id: Uuid, + target_account: &'a AccountId, + operation_id: i64, +} + +impl<'a> FinishBanQuery<'a> { + pub fn new(class_id: Uuid, target_account: &'a AccountId, operation_id: i64) -> Self { + Self { + class_id, + target_account, + operation_id, + } + } + + pub async fn execute(&self, conn: &mut sqlx::PgConnection) -> sqlx::Result> { + sqlx::query_as!( + Object, + r#" + UPDATE ban_history + SET + unbanned_at = NOW(), + unbanned_operation_id = $3 + WHERE + class_id = $1 + AND target_account = $2 + AND unbanned_at IS NULL + RETURNING + target_account AS "target_account: AccountId", + class_id, + banned_at, + banned_operation_id, + unbanned_at, + unbanned_operation_id + "#, + self.class_id, + self.target_account as &AccountId, + self.operation_id, + ) + .fetch_optional(conn) + .await + } +} From 9ea0d2400eb127319f424dd4532e89d665cefb5e Mon Sep 17 00:00:00 2001 From: Dmitry Shlagoff Date: Thu, 20 Jul 2023 18:05:34 +0200 Subject: [PATCH 35/35] Add tests for ban + refactor tests to use sqlx::test macro --- Cargo.lock | 155 +++----- Cargo.toml | 10 +- sqlx-data.json | 371 ++++++++++-------- src/app/api/v1/account/mod.rs | 30 +- src/app/api/v1/authz/mod.rs | 12 +- src/app/api/v1/class/create_timestamp.rs | 14 +- src/app/api/v1/class/properties.rs | 24 +- src/app/api/v1/class/read.rs | 31 +- src/app/api/v1/class/recreate.rs | 12 +- src/app/api/v1/class/update.rs | 12 +- src/app/api/v1/minigroup/mod.rs | 24 +- src/app/api/v1/p2p/mod.rs | 18 +- src/app/api/v1/tests/mod.rs | 12 +- src/app/api/v1/webinar/convert.rs | 24 +- src/app/api/v1/webinar/create/replica.rs | 18 +- src/app/api/v1/webinar/create/webinar.rs | 25 +- src/app/api/v1/webinar/download.rs | 6 +- src/app/error.rs | 7 +- .../minigroup/tests.rs | 30 +- src/app/stage/ban.rs | 365 ++++++++++++++++- src/app/tide_state/mod.rs | 6 +- src/db/ban_account_op.rs | 202 ++++++++-- src/db/ban_history.rs | 124 +++++- src/db/class/insert_query.rs | 12 +- src/db/recording.rs | 51 ++- src/test_helpers/db.rs | 34 +- src/test_helpers/state.rs | 50 +-- 27 files changed, 1161 insertions(+), 518 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 51074a73..fe00578f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -91,26 +91,25 @@ dependencies = [ [[package]] name = "async-nats" -version = "0.29.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1174495e436c928905018f10a36160f7a8a6786450f50f4ce7fba05d1539704c" +checksum = "94e3e851ddf3b62be8a8085e1e453968df9cdbf990a37bbb589b5b4f587c68d7" dependencies = [ - "async-nats-tokio-rustls-deps", - "base64 0.13.1", - "base64-url", + "base64 0.21.2", "bytes", "futures", "http", "itoa 1.0.6", "memchr", "nkeys", - "nuid", + "nuid 0.3.2", "once_cell", "rand", "regex", "ring", "rustls-native-certs", "rustls-pemfile", + "rustls-webpki", "serde", "serde_json", "serde_nanos", @@ -119,21 +118,11 @@ dependencies = [ "time 0.3.21", "tokio", "tokio-retry", + "tokio-rustls 0.24.1", "tracing", "url 2.4.0", ] -[[package]] -name = "async-nats-tokio-rustls-deps" -version = "0.24.0-ALPHA.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cdefe54cd7867d937c0a507d2a3a830af410044282cd3e4002b5b7860e1892e" -dependencies = [ - "rustls 0.21.1", - "tokio", - "webpki 0.22.0", -] - [[package]] name = "async-trait" version = "0.1.68" @@ -243,15 +232,6 @@ version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" -[[package]] -name = "base64-url" -version = "1.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a99c239d0c7e77c85dddfa9cebce48704b3c49550fcd3b84dd637e4484899f" -dependencies = [ - "base64 0.13.1", -] - [[package]] name = "base64ct" version = "1.1.1" @@ -389,6 +369,9 @@ name = "bytes" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +dependencies = [ + "serde", +] [[package]] name = "cc" @@ -584,29 +567,6 @@ dependencies = [ "const-oid", ] -[[package]] -name = "diesel" -version = "1.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b28135ecf6b7d446b43e27e225622a038cc4e2930a1022f51cdb97ada19b8e4d" -dependencies = [ - "bitflags", - "byteorder", - "diesel_derives", - "pq-sys", -] - -[[package]] -name = "diesel_derives" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45f5098f628d02a7a0f68ddba586fb61e80edec3bdc1be3b921f4ceec60858d3" -dependencies = [ - "proc-macro2", - "quote 1.0.28", - "syn 1.0.109", -] - [[package]] name = "difflib" version = "0.4.0" @@ -692,7 +652,7 @@ dependencies = [ "svc-agent", "svc-authn", "svc-authz", - "svc-error", + "svc-error 0.6.0", "svc-events", "svc-nats-client", "svc-utils", @@ -1534,9 +1494,9 @@ dependencies = [ [[package]] name = "nkeys" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e66a7cd1358277b2a6f77078e70aea7315ff2f20db969cc61153103ec162594" +checksum = "c2d151f6ece2f3d1077f6c779268de2516653d8344ddde65addd785cce764fe5" dependencies = [ "byteorder", "data-encoding", @@ -1583,6 +1543,16 @@ dependencies = [ "rand", ] +[[package]] +name = "nuid" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b61b1710432e483e6a67b20b6c60c6afe0e2fad67aabba3bdb912f3f70ff6ae" +dependencies = [ + "once_cell", + "rand", +] + [[package]] name = "num-bigint" version = "0.2.6" @@ -1870,15 +1840,6 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" -[[package]] -name = "pq-sys" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c0052426df997c0cbd30789eb44ca097e3541717a7b8fa36b1c464ee7edebd" -dependencies = [ - "vcpkg", -] - [[package]] name = "predicates" version = "2.1.5" @@ -2257,8 +2218,8 @@ dependencies = [ "pollster", "thiserror", "tokio", - "tokio-rustls", - "webpki 0.21.4", + "tokio-rustls 0.22.0", + "webpki", ] [[package]] @@ -2318,14 +2279,14 @@ dependencies = [ "log", "ring", "sct 0.6.1", - "webpki 0.21.4", + "webpki", ] [[package]] name = "rustls" -version = "0.21.1" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c911ba11bc8433e811ce56fde130ccf32f5127cab0e0194e9c68c5a5b671791e" +checksum = "79ea77c539259495ce8ca47f53e66ae0330a8819f67e23ac96ca02f50e7b7d36" dependencies = [ "log", "ring", @@ -2356,9 +2317,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.100.1" +version = "0.101.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6207cd5ed3d8dca7816f8f3725513a34609c0c765bf652b8c3cb4cfd87db46b" +checksum = "15f36a6828982f422756984e47912a7a51dcbc2a197aa791158f8ca61cd8204e" dependencies = [ "ring", "untrusted", @@ -2956,9 +2917,9 @@ checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "svc-agent" -version = "0.20.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c90205083c87c6bc25990d5cdd26f98d8c431441e86dbd4e7421b28c428e04d" +checksum = "bef9c610b65f24bab61b52f5a7264983d0e014858e8496282aec7d688baf7869" dependencies = [ "async-channel", "base64 0.21.2", @@ -2981,7 +2942,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb5cf659f78c8fff863c17ac4e674829517919716eeecab602e8d2941e89c111" dependencies = [ "chrono", - "diesel", "http", "jsonwebtoken", "serde", @@ -3015,6 +2975,19 @@ name = "svc-error" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f841d7fd45d6f179e9f3765491fcb5eea100a5bbe50ea47faf3f262031966d9" +dependencies = [ + "anyhow", + "crossbeam-channel", + "http", + "serde", + "serde_derive", +] + +[[package]] +name = "svc-error" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad220c6bc89bc2e7b8af01db6dcfa4a513e18d78e7cf2f778e623ac22577eadf" dependencies = [ "anyhow", "crossbeam-channel", @@ -3032,9 +3005,9 @@ dependencies = [ [[package]] name = "svc-events" -version = "0.9.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a30b9e3f342270165a9e7543f9ba85a45bff586cc04f4d495ffd8b4a44447974" +checksum = "3ad84bd15a598b693df7dd08ca832c3414d59d6847f134f479ff547264669735" dependencies = [ "serde", "serde_json", @@ -3046,9 +3019,9 @@ dependencies = [ [[package]] name = "svc-nats-client" -version = "0.5.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c35276c77ae7ff528a5fb243f16bf95065a3ac8c91837356f2ee797c9a697203" +checksum = "cf7705838936003cae1b79e726be255ea9702b8aad516dec9c998c7c93ef6f8d" dependencies = [ "anyhow", "async-nats", @@ -3056,10 +3029,11 @@ dependencies = [ "futures", "futures-util", "humantime-serde", + "nuid 0.4.1", "reqwest", "serde", "svc-agent", - "svc-error", + "svc-error 0.6.0", "svc-events", "thiserror", "tokio", @@ -3069,9 +3043,9 @@ dependencies = [ [[package]] name = "svc-utils" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b8fd4d41bfda7aec14a80746dd1e6f5ae6c04643a7e9c18065f38388b6a4477" +checksum = "b8443737f4d2444dc9e6a140a83d720685290d1ad763e75b59421958fa4a1a96" dependencies = [ "axum", "futures", @@ -3081,11 +3055,12 @@ dependencies = [ "prometheus", "svc-agent", "svc-authn", - "svc-error", + "svc-error 0.5.0", "tokio", "tower", "tower-http", "tracing", + "url 2.4.0", ] [[package]] @@ -3303,7 +3278,17 @@ checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6" dependencies = [ "rustls 0.19.1", "tokio", - "webpki 0.21.4", + "webpki", +] + +[[package]] +name = "tokio-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +dependencies = [ + "rustls 0.21.5", + "tokio", ] [[package]] @@ -3746,16 +3731,6 @@ dependencies = [ "untrusted", ] -[[package]] -name = "webpki" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" -dependencies = [ - "ring", - "untrusted", -] - [[package]] name = "whoami" version = "1.4.0" diff --git a/Cargo.toml b/Cargo.toml index 82314014..f9076619 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,19 +46,19 @@ sqlx = { version = "0.6", features = [ "bigdecimal", "runtime-tokio-native-tls", ] } -svc-agent = { version = "0.20", features = ["sqlx"] } +svc-agent = { version = "0.21", features = ["sqlx"] } svc-authn = { version = "0.8", features = ["jose", "sqlx"] } svc-authz = "0.12" -svc-error = { version = "0.5", features = [ +svc-error = { version = "0.6", features = [ "svc-agent", "svc-authn", "svc-authz", "sentry-extension", "sqlx", ] } -svc-events = "0.9" -svc-nats-client = "0.5" -svc-utils = { version = "0.7", features = ["log-middleware", "metrics-middleware", "cors-middleware", "authn-extractor"] } +svc-events = "0.11" +svc-nats-client = "0.8" +svc-utils = { version = "0.8", features = ["log-middleware", "metrics-middleware", "cors-middleware", "authn-extractor"] } tokio = { version = "1.28", features = ["full"] } tower = { version = "0.4", features = [ "timeout" ] } tracing = "0.1" diff --git a/sqlx-data.json b/sqlx-data.json index 8aeb70c0..af4f610f 100644 --- a/sqlx-data.json +++ b/sqlx-data.json @@ -1249,6 +1249,135 @@ }, "query": "\n UPDATE recording\n SET modified_segments =\n CASE\n WHEN created_by = $3 THEN $2\n ELSE segments\n END,\n adjusted_at = NOW()\n WHERE class_id = $1 AND deleted_at IS NULL\n RETURNING\n id,\n class_id,\n rtc_id,\n stream_uri,\n segments AS \"segments!: Option\",\n started_at,\n modified_segments AS \"modified_segments!: Option\",\n created_at,\n adjusted_at,\n transcoded_at,\n created_by AS \"created_by: AgentId\",\n deleted_at\n " }, + "7027e1415842b3bd10d9c5efb3e691341a7dfd7b51705cbf2dbed9cba3544a03": { + "describe": { + "columns": [ + { + "name": "user_account: _", + "ordinal": 0, + "type_info": { + "Custom": { + "kind": { + "Composite": [ + [ + "label", + "Text" + ], + [ + "audience", + "Text" + ] + ] + }, + "name": "account_id" + } + } + }, + { + "name": "last_op_id", + "ordinal": 1, + "type_info": "Int8" + }, + { + "name": "is_video_streaming_banned", + "ordinal": 2, + "type_info": "Bool" + }, + { + "name": "is_collaboration_banned", + "ordinal": 3, + "type_info": "Bool" + } + ], + "nullable": [ + false, + false, + false, + false + ], + "parameters": { + "Left": [ + { + "Custom": { + "kind": { + "Composite": [ + [ + "label", + "Text" + ], + [ + "audience", + "Text" + ] + ] + }, + "name": "account_id" + } + }, + "Int8", + "Int8" + ] + } + }, + "query": "\n INSERT INTO ban_account_op (user_account, last_op_id)\n VALUES ($1, $2)\n ON CONFLICT (user_account) DO UPDATE\n SET\n -- to reset sub-operation trackers only if op_id has changed\n is_video_streaming_banned = ($2 = $3 AND ban_account_op.is_video_streaming_banned),\n is_collaboration_banned = ($2 = $3 AND ban_account_op.is_collaboration_banned),\n last_op_id = EXCLUDED.last_op_id\n WHERE\n -- allow to 'complete' operation if there's no change in last_op_id\n -- or allow to do upsert without real changes so we can process\n -- the same message twice\n ban_account_op.last_op_id = EXCLUDED.last_op_id OR\n -- allow change op id if the previous operation is completed\n (\n ban_account_op.last_op_id = $3 AND\n ban_account_op.is_video_streaming_banned = true AND\n ban_account_op.is_collaboration_banned = true\n )\n RETURNING\n user_account AS \"user_account: _\",\n last_op_id,\n is_video_streaming_banned,\n is_collaboration_banned\n " + }, + "7174ed47333151e6ef71b45f2f2fe9d15d70ba860e5dc52e1dcff3aea12c4c1d": { + "describe": { + "columns": [ + { + "name": "user_account: _", + "ordinal": 0, + "type_info": { + "Custom": { + "kind": { + "Composite": [ + [ + "label", + "Text" + ], + [ + "audience", + "Text" + ] + ] + }, + "name": "account_id" + } + } + }, + { + "name": "last_op_id", + "ordinal": 1, + "type_info": "Int8" + }, + { + "name": "is_video_streaming_banned", + "ordinal": 2, + "type_info": "Bool" + }, + { + "name": "is_collaboration_banned", + "ordinal": 3, + "type_info": "Bool" + } + ], + "nullable": [ + false, + false, + false, + false + ], + "parameters": { + "Left": [ + "Record", + "Bool", + "Bool", + "Int8" + ] + } + }, + "query": "\n UPDATE ban_account_op\n SET\n is_video_streaming_banned = COALESCE($2, ban_account_op.is_video_streaming_banned),\n is_collaboration_banned = COALESCE($3, ban_account_op.is_collaboration_banned)\n WHERE\n ban_account_op.last_op_id = $4\n AND ban_account_op.user_account = $1\n RETURNING\n user_account AS \"user_account: _\",\n last_op_id,\n is_video_streaming_banned,\n is_collaboration_banned\n " + }, "73a64697ca7ad2d67c7b2a77cc1fd6c0fb7a2b262a30d8386e83f8cbd8be5a54": { "describe": { "columns": [ @@ -1635,90 +1764,6 @@ }, "query": "\n INSERT INTO recording (class_id, rtc_id, segments, modified_segments, stream_uri, started_at, adjusted_at, transcoded_at, created_by)\n VALUES ($1, $2, $3, $4, $5, NOW(), NOW(), NOW(), $6)\n RETURNING\n id,\n class_id,\n rtc_id,\n stream_uri,\n segments AS \"segments!: Option\",\n started_at,\n modified_segments AS \"modified_segments!: Option\",\n created_at,\n adjusted_at,\n transcoded_at,\n created_by AS \"created_by: AgentId\",\n deleted_at\n " }, - "b333f6270cc34a0b5d2cd2d23d39c705c219704ba7094dd078cbc2978146e534": { - "describe": { - "columns": [ - { - "name": "target_account: AccountId", - "ordinal": 0, - "type_info": { - "Custom": { - "kind": { - "Composite": [ - [ - "label", - "Text" - ], - [ - "audience", - "Text" - ] - ] - }, - "name": "account_id" - } - } - }, - { - "name": "class_id", - "ordinal": 1, - "type_info": "Uuid" - }, - { - "name": "banned_at", - "ordinal": 2, - "type_info": "Timestamptz" - }, - { - "name": "banned_operation_id", - "ordinal": 3, - "type_info": "Int8" - }, - { - "name": "unbanned_at", - "ordinal": 4, - "type_info": "Timestamptz" - }, - { - "name": "unbanned_operation_id", - "ordinal": 5, - "type_info": "Int8" - } - ], - "nullable": [ - false, - false, - false, - false, - true, - true - ], - "parameters": { - "Left": [ - "Uuid", - { - "Custom": { - "kind": { - "Composite": [ - [ - "label", - "Text" - ], - [ - "audience", - "Text" - ] - ] - }, - "name": "account_id" - } - }, - "Int8" - ] - } - }, - "query": "\n INSERT INTO ban_history (class_id, target_account, banned_operation_id)\n VALUES ($1, $2, $3)\n -- this allows us to run this query many times idempotently\n ON CONFLICT (banned_operation_id) DO NOTHING\n RETURNING\n target_account AS \"target_account: AccountId\",\n class_id,\n banned_at,\n banned_operation_id,\n unbanned_at,\n unbanned_operation_id\n " - }, "c67188dde7672c71f7e14a0ef09047934fbf808e5541e1b35c88004f36c16c8b": { "describe": { "columns": [ @@ -1841,80 +1886,6 @@ }, "query": "\n INSERT INTO class (\n scope, audience, time, tags, preserve_history, kind,\n conference_room_id, event_room_id,\n original_event_room_id, modified_event_room_id, reserve, room_events_uri,\n established, properties, original_class_id\n )\n VALUES ($1, $2, $3, $4, $5, $6::class_type, $7, $8, $9, $10, $11, $12, $13, $14, $15)\n ON CONFLICT (scope, audience)\n DO UPDATE\n SET time = EXCLUDED.time,\n tags = EXCLUDED.tags,\n preserve_history = EXCLUDED.preserve_history,\n reserve = EXCLUDED.reserve,\n properties = EXCLUDED.properties\n WHERE class.established = 'f'\n RETURNING\n id,\n kind AS \"kind!: ClassType\",\n scope,\n time AS \"time!: Time\",\n audience,\n created_at,\n tags,\n preserve_history,\n reserve,\n properties AS \"properties: _\",\n original_class_id,\n content_id\n " }, - "c6ce697aaad621b2230880c9d46251dffd92dc490f5473cd2ca32a8627dae130": { - "describe": { - "columns": [ - { - "name": "user_account: _", - "ordinal": 0, - "type_info": { - "Custom": { - "kind": { - "Composite": [ - [ - "label", - "Text" - ], - [ - "audience", - "Text" - ] - ] - }, - "name": "account_id" - } - } - }, - { - "name": "last_op_id", - "ordinal": 1, - "type_info": "Int8" - }, - { - "name": "is_video_streaming_banned", - "ordinal": 2, - "type_info": "Bool" - }, - { - "name": "is_collaboration_banned", - "ordinal": 3, - "type_info": "Bool" - } - ], - "nullable": [ - false, - false, - false, - false - ], - "parameters": { - "Left": [ - { - "Custom": { - "kind": { - "Composite": [ - [ - "label", - "Text" - ], - [ - "audience", - "Text" - ] - ] - }, - "name": "account_id" - } - }, - "Int8", - "Bool", - "Bool", - "Int8" - ] - } - }, - "query": "\n INSERT INTO ban_account_op (user_account, last_op_id, is_video_streaming_banned, is_collaboration_banned)\n VALUES ($1, $2, COALESCE($3, false), COALESCE($4, false))\n ON CONFLICT (user_account) DO UPDATE\n SET\n is_video_streaming_banned = COALESCE($3, ban_account_op.is_video_streaming_banned),\n is_collaboration_banned = COALESCE($4, ban_account_op.is_collaboration_banned),\n last_op_id = EXCLUDED.last_op_id\n WHERE\n -- allow to 'complete' operation if there's no change in last_op_id\n -- or allow to do upsert without real changes so we can process\n -- the same message twice\n ban_account_op.last_op_id = EXCLUDED.last_op_id OR\n -- allow change op id if the previous operation is completed\n (\n ban_account_op.last_op_id = $5 AND\n ban_account_op.is_video_streaming_banned = true AND\n ban_account_op.is_collaboration_banned = true\n )\n RETURNING\n user_account AS \"user_account: _\",\n last_op_id,\n is_video_streaming_banned,\n is_collaboration_banned\n " - }, "c7386d4717720730d7762fbcf2d24c5d0f508b246e987f4666684bf37b27ddc2": { "describe": { "columns": [ @@ -3401,6 +3372,90 @@ }, "query": "\n INSERT INTO class (\n scope, audience, time, tags, preserve_history, kind,\n conference_room_id, event_room_id, properties\n )\n VALUES ($1, $2, $3, $4, $5, $6::class_type, $7, $8, $9)\n RETURNING\n id,\n scope,\n kind AS \"kind!: ClassType\",\n audience,\n time AS \"time!: Time\",\n tags,\n properties AS \"properties: _\",\n preserve_history,\n created_at,\n event_room_id AS \"event_room_id!: Uuid\",\n conference_room_id AS \"conference_room_id!: Uuid\",\n original_event_room_id,\n modified_event_room_id,\n reserve,\n room_events_uri,\n host AS \"host: AgentId\",\n timed_out,\n original_class_id,\n content_id\n " }, + "f9aeac4c3a1e1747314db755f8e0790b01a0d2d603f20cdd069b7b57268ccdbe": { + "describe": { + "columns": [ + { + "name": "target_account!: AccountId", + "ordinal": 0, + "type_info": { + "Custom": { + "kind": { + "Composite": [ + [ + "label", + "Text" + ], + [ + "audience", + "Text" + ] + ] + }, + "name": "account_id" + } + } + }, + { + "name": "class_id!: _", + "ordinal": 1, + "type_info": "Uuid" + }, + { + "name": "banned_at!: _", + "ordinal": 2, + "type_info": "Timestamptz" + }, + { + "name": "banned_operation_id!: _", + "ordinal": 3, + "type_info": "Int8" + }, + { + "name": "unbanned_at", + "ordinal": 4, + "type_info": "Timestamptz" + }, + { + "name": "unbanned_operation_id", + "ordinal": 5, + "type_info": "Int8" + } + ], + "nullable": [ + null, + null, + null, + null, + null, + null + ], + "parameters": { + "Left": [ + "Uuid", + { + "Custom": { + "kind": { + "Composite": [ + [ + "label", + "Text" + ], + [ + "audience", + "Text" + ] + ] + }, + "name": "account_id" + } + }, + "Int8" + ] + } + }, + "query": "\n WITH i AS (\n INSERT INTO ban_history (class_id, target_account, banned_operation_id)\n VALUES ($1, $2, $3)\n -- this allows us to run this query many times idempotently\n ON CONFLICT (banned_operation_id) DO NOTHING\n RETURNING *\n )\n -- gets row if there was no conflict\n SELECT\n target_account AS \"target_account!: AccountId\",\n class_id AS \"class_id!: _\",\n banned_at AS \"banned_at!: _\",\n banned_operation_id AS \"banned_operation_id!: _\",\n unbanned_at,\n unbanned_operation_id\n FROM i\n -- or selects original row if there was a conflict\n UNION\n SELECT\n target_account AS \"target_account: AccountId\",\n class_id,\n banned_at,\n banned_operation_id,\n unbanned_at,\n unbanned_operation_id\n FROM ban_history\n WHERE\n banned_operation_id = $3\n " + }, "fe7779aca18f7e0fe8dcee465db39f3669a6b1c80064bab63e82f8f5aa99d7d1": { "describe": { "columns": [ diff --git a/src/app/api/v1/account/mod.rs b/src/app/api/v1/account/mod.rs index fb774e6c..f0f7f1d4 100644 --- a/src/app/api/v1/account/mod.rs +++ b/src/app/api/v1/account/mod.rs @@ -126,11 +126,11 @@ mod tests { use super::*; use crate::test_helpers::prelude::*; - #[tokio::test] - async fn update_property() { + #[sqlx::test] + async fn update_property(pool: sqlx::PgPool) { let agent = TestAgent::new("web", "user1", USR_AUDIENCE); - let db_pool = TestDb::new().await; + let db_pool = TestDb::new(pool); let authz = TestAuthz::new(); let state = TestState::new_with_pool(db_pool, authz); let state = Arc::new(state); @@ -151,11 +151,11 @@ mod tests { assert!(props.contains_key(&property_id)); } - #[tokio::test] - async fn read_property() { + #[sqlx::test] + async fn read_property(pool: sqlx::PgPool) { let agent = TestAgent::new("web", "user2", USR_AUDIENCE); - let db_pool = TestDb::new().await; + let db_pool = TestDb::new(pool); let authz = TestAuthz::new(); let state = TestState::new_with_pool(db_pool, authz); let state = Arc::new(state); @@ -185,11 +185,11 @@ mod tests { assert_eq!(property_value, expected_property_value); } - #[tokio::test] - async fn merge_properties() { + #[sqlx::test] + async fn merge_properties(pool: sqlx::PgPool) { let agent = TestAgent::new("web", "user3", USR_AUDIENCE); - let db_pool = TestDb::new().await; + let db_pool = TestDb::new(pool); let authz = TestAuthz::new(); let state = TestState::new_with_pool(db_pool, authz); let state = Arc::new(state); @@ -243,11 +243,11 @@ mod tests { assert_eq!(property_value, second_prop_value); } - #[tokio::test] - async fn overwrite_property() { + #[sqlx::test] + async fn overwrite_property(pool: sqlx::PgPool) { let agent = TestAgent::new("web", "user4", USR_AUDIENCE); - let db_pool = TestDb::new().await; + let db_pool = TestDb::new(pool); let authz = TestAuthz::new(); let state = TestState::new_with_pool(db_pool, authz); let state = Arc::new(state); @@ -300,11 +300,11 @@ mod tests { assert_eq!(property_value, second_prop_value); } - #[tokio::test] - async fn overwrite_property_complex() { + #[sqlx::test] + async fn overwrite_property_complex(pool: sqlx::PgPool) { let agent = TestAgent::new("web", "user5", USR_AUDIENCE); - let db_pool = TestDb::new().await; + let db_pool = TestDb::new(pool); let authz = TestAuthz::new(); let state = TestState::new_with_pool(db_pool, authz); let state = Arc::new(state); diff --git a/src/app/api/v1/authz/mod.rs b/src/app/api/v1/authz/mod.rs index 5fe038af..a7d5f0eb 100644 --- a/src/app/api/v1/authz/mod.rs +++ b/src/app/api/v1/authz/mod.rs @@ -489,12 +489,12 @@ fn test_extract_rtc_id() { assert_eq!(r, "14aa9730-26e1-487c-9153-bc8cb28d8eb0"); } -#[tokio::test] -async fn test_transform_tq_authz_request() { +#[sqlx::test] +async fn test_transform_tq_authz_request(pool: sqlx::PgPool) { use crate::test_helpers::prelude::TestAuthz; use crate::test_helpers::state::TestState; - let test_state = TestState::new(TestAuthz::new()).await; + let test_state = TestState::new(pool, TestAuthz::new()).await; // ["classrooms", CLASSROOM_ID, "priorities", priority]::* // becomes ["classrooms", CLASSROOM_ID]::* let mut authz_req: AuthzRequest = serde_json::from_str( @@ -617,12 +617,12 @@ fn test_transform_nats_gatekeeper_authz_request() { ); } -#[tokio::test] -async fn test_transform_storage_v1_authz_request() { +#[sqlx::test] +async fn test_transform_storage_v1_authz_request(pool: sqlx::PgPool) { use crate::test_helpers::prelude::TestAuthz; use crate::test_helpers::state::TestState; - let test_state = TestState::new(TestAuthz::new()).await; + let test_state = TestState::new(pool, TestAuthz::new()).await; let mut authz_req: AuthzRequest = serde_json::from_str( r#" diff --git a/src/app/api/v1/class/create_timestamp.rs b/src/app/api/v1/class/create_timestamp.rs index 4d228bfa..3746855c 100644 --- a/src/app/api/v1/class/create_timestamp.rs +++ b/src/app/api/v1/class/create_timestamp.rs @@ -74,11 +74,10 @@ mod create_timestamp_tests { use std::ops::Bound; use uuid::Uuid; - #[tokio::test] - async fn create_timestamp_unauthorized() { + #[sqlx::test] + async fn create_timestamp_unauthorized(pool: sqlx::PgPool) { let agent = TestAgent::new("web", "user1", USR_AUDIENCE); - - let db_pool = TestDb::new().await; + let db_pool = TestDb::new(pool); let webinar = { let mut conn = db_pool.get_conn().await; @@ -111,11 +110,10 @@ mod create_timestamp_tests { .expect_err("Unexpected success, should fail due to authz"); } - #[tokio::test] - async fn create_webinar_timestamp() { + #[sqlx::test] + async fn create_webinar_timestamp(pool: sqlx::PgPool) { let agent = TestAgent::new("web", "user1", USR_AUDIENCE); - - let db_pool = TestDb::new().await; + let db_pool = TestDb::new(pool); let webinar = { let mut conn = db_pool.get_conn().await; diff --git a/src/app/api/v1/class/properties.rs b/src/app/api/v1/class/properties.rs index e8535d8c..ec4a1c18 100644 --- a/src/app/api/v1/class/properties.rs +++ b/src/app/api/v1/class/properties.rs @@ -164,13 +164,13 @@ mod tests { use mockall::predicate as pred; use uuid::Uuid; - #[tokio::test] - async fn read_property_unauthorized() { + #[sqlx::test] + async fn read_property_unauthorized(pool: sqlx::PgPool) { let agent = TestAgent::new("web", "user1", USR_AUDIENCE); let event_room_id = Uuid::new_v4(); let conference_room_id = Uuid::new_v4(); - let state = TestState::new(TestAuthz::new()).await; + let state = TestState::new(pool, TestAuthz::new()).await; let webinar = { let mut conn = state.get_conn().await.expect("Failed to fetch connection"); @@ -204,13 +204,13 @@ mod tests { .expect_err("Unexpectedly succeeded"); } - #[tokio::test] - async fn read_property() { + #[sqlx::test] + async fn read_property(pool: sqlx::PgPool) { let agent = TestAgent::new("web", "user1", USR_AUDIENCE); let event_room_id = Uuid::new_v4(); let conference_room_id = Uuid::new_v4(); - let db_pool = TestDb::new().await; + let db_pool = TestDb::new(pool); let webinar = { let mut conn = db_pool.get_conn().await; @@ -256,13 +256,13 @@ mod tests { assert_eq!(property_value, serde_json::json!("test2")); } - #[tokio::test] - async fn update_property_unauthorized() { + #[sqlx::test] + async fn update_property_unauthorized(pool: sqlx::PgPool) { let agent = TestAgent::new("web", "user1", USR_AUDIENCE); let event_room_id = Uuid::new_v4(); let conference_room_id = Uuid::new_v4(); - let state = TestState::new(TestAuthz::new()).await; + let state = TestState::new(pool, TestAuthz::new()).await; let webinar = { let mut conn = state.get_conn().await.expect("Failed to fetch connection"); @@ -297,13 +297,13 @@ mod tests { .expect_err("Unexpectedly succeeded"); } - #[tokio::test] - async fn update_property() { + #[sqlx::test] + async fn update_property(pool: sqlx::PgPool) { let agent = TestAgent::new("web", "user1", USR_AUDIENCE); let event_room_id = Uuid::new_v4(); let conference_room_id = Uuid::new_v4(); - let db_pool = TestDb::new().await; + let db_pool = TestDb::new(pool); let webinar = { let mut conn = db_pool.get_conn().await; diff --git a/src/app/api/v1/class/read.rs b/src/app/api/v1/class/read.rs index 5b8c941c..ccd394e9 100644 --- a/src/app/api/v1/class/read.rs +++ b/src/app/api/v1/class/read.rs @@ -219,11 +219,10 @@ mod tests { }; use serde_json::Value; - #[tokio::test] - async fn read_webinar_unauthorized() { + #[sqlx::test] + async fn read_webinar_unauthorized(pool: sqlx::PgPool) { let agent = TestAgent::new("web", "user1", USR_AUDIENCE); - - let state = TestState::new(TestAuthz::new()).await; + let state = TestState::new(pool, TestAuthz::new()).await; let state = Arc::new(state); @@ -237,10 +236,10 @@ mod tests { .expect_err("Unexpectedly succeeded"); } - #[tokio::test] - async fn read_webinar() { + #[sqlx::test] + async fn read_webinar(pool: sqlx::PgPool) { let agent = TestAgent::new("web", "user1", USR_AUDIENCE); - let db_pool = TestDb::new().await; + let db_pool = TestDb::new(pool); let webinar = { let mut conn = db_pool.get_conn().await; @@ -281,10 +280,10 @@ mod tests { assert_eq!(v.get("turn_host").unwrap().as_str(), Some("turn0")); } - #[tokio::test] - async fn read_p2p() { + #[sqlx::test] + async fn read_p2p(pool: sqlx::PgPool) { let agent = TestAgent::new("web", "user1", USR_AUDIENCE); - let db_pool = TestDb::new().await; + let db_pool = TestDb::new(pool); let p2p = { let mut conn = db_pool.get_conn().await; @@ -329,10 +328,10 @@ mod tests { assert_eq!(turns.into_iter().collect::>().len(), 1); } - #[tokio::test] - async fn read_class_properties() { + #[sqlx::test] + async fn read_class_properties(pool: sqlx::PgPool) { let agent = TestAgent::new("web", "user1", USR_AUDIENCE); - let db_pool = TestDb::new().await; + let db_pool = TestDb::new(pool); let mut properties = KeyValueProperties::new(); properties.insert("test".to_owned(), serde_json::json!("test")); @@ -410,10 +409,10 @@ mod tests { ) } - #[tokio::test] - async fn read_account_properties() { + #[sqlx::test] + async fn read_account_properties(pool: sqlx::PgPool) { let agent = TestAgent::new("web", "user1", USR_AUDIENCE); - let db_pool = TestDb::new().await; + let db_pool = TestDb::new(pool); let mut properties = KeyValueProperties::new(); properties.insert("test".to_owned(), serde_json::json!("test")); diff --git a/src/app/api/v1/class/recreate.rs b/src/app/api/v1/class/recreate.rs index b76d3c61..b1f423d9 100644 --- a/src/app/api/v1/class/recreate.rs +++ b/src/app/api/v1/class/recreate.rs @@ -140,11 +140,11 @@ mod tests { use mockall::predicate as pred; use uuid::Uuid; - #[tokio::test] - async fn recreate_webinar_unauthorized() { + #[sqlx::test] + async fn recreate_webinar_unauthorized(pool: sqlx::PgPool) { let agent = TestAgent::new("web", "user1", USR_AUDIENCE); - let db_pool = TestDb::new().await; + let db_pool = TestDb::new(pool); let webinar = { let mut conn = db_pool.get_conn().await; @@ -172,14 +172,14 @@ mod tests { .expect_err("Unexpected success, should fail due to authz"); } - #[tokio::test] - async fn recreate_webinar() { + #[sqlx::test] + async fn recreate_webinar(pool: sqlx::PgPool) { let agent = TestAgent::new("web", "user1", USR_AUDIENCE); let recreated_event_room_id = Uuid::new_v4(); let recreated_conference_room_id = Uuid::new_v4(); - let db_pool = TestDb::new().await; + let db_pool = TestDb::new(pool); let webinar = { let mut conn = db_pool.get_conn().await; diff --git a/src/app/api/v1/class/update.rs b/src/app/api/v1/class/update.rs index da11c6b3..ca8e4fed 100644 --- a/src/app/api/v1/class/update.rs +++ b/src/app/api/v1/class/update.rs @@ -185,13 +185,13 @@ mod tests { let _update: ClassUpdate = serde_json::from_str(update).unwrap(); } - #[tokio::test] - async fn update_webinar_unauthorized() { + #[sqlx::test] + async fn update_webinar_unauthorized(pool: sqlx::PgPool) { let agent = TestAgent::new("web", "user1", USR_AUDIENCE); let event_room_id = Uuid::new_v4(); let conference_room_id = Uuid::new_v4(); - let state = TestState::new(TestAuthz::new()).await; + let state = TestState::new(pool, TestAuthz::new()).await; let webinar = { let mut conn = state.get_conn().await.expect("Failed to fetch connection"); factory::Webinar::new( @@ -220,13 +220,13 @@ mod tests { .expect_err("Unexpectedly succeeded"); } - #[tokio::test] - async fn update_webinar() { + #[sqlx::test] + async fn update_webinar(pool: sqlx::PgPool) { let agent = TestAgent::new("web", "user1", USR_AUDIENCE); let event_room_id = Uuid::new_v4(); let conference_room_id = Uuid::new_v4(); - let db_pool = TestDb::new().await; + let db_pool = TestDb::new(pool); let webinar = { let mut conn = db_pool.get_conn().await; diff --git a/src/app/api/v1/minigroup/mod.rs b/src/app/api/v1/minigroup/mod.rs index 3551b76e..c5ae2da8 100644 --- a/src/app/api/v1/minigroup/mod.rs +++ b/src/app/api/v1/minigroup/mod.rs @@ -247,14 +247,14 @@ mod tests { use mockall::predicate as pred; use uuid::Uuid; - #[tokio::test] - async fn create_minigroup_no_time() { + #[sqlx::test] + async fn create_minigroup_no_time(pool: sqlx::PgPool) { let agent = TestAgent::new("web", "user1", USR_AUDIENCE); let mut authz = TestAuthz::new(); authz.allow(agent.account_id(), vec!["classrooms"], "create"); - let mut state = TestState::new(authz).await; + let mut state = TestState::new(pool, authz).await; let event_room_id = Uuid::new_v4(); let conference_room_id = Uuid::new_v4(); @@ -289,14 +289,14 @@ mod tests { assert_eq!(new_minigroup.reserve(), Some(10),); } - #[tokio::test] - async fn create_minigroup_with_time() { + #[sqlx::test] + async fn create_minigroup_with_time(pool: sqlx::PgPool) { let agent = TestAgent::new("web", "user1", USR_AUDIENCE); let mut authz = TestAuthz::new(); authz.allow(agent.account_id(), vec!["classrooms"], "create"); - let mut state = TestState::new(authz).await; + let mut state = TestState::new(pool, authz).await; let event_room_id = Uuid::new_v4(); let conference_room_id = Uuid::new_v4(); @@ -337,11 +337,11 @@ mod tests { assert_eq!(new_minigroup.reserve(), Some(10),); } - #[tokio::test] - async fn create_minigroup_unauthorized() { + #[sqlx::test] + async fn create_minigroup_unauthorized(pool: sqlx::PgPool) { let agent = TestAgent::new("web", "user1", USR_AUDIENCE); - let state = TestState::new(TestAuthz::new()).await; + let state = TestState::new(pool, TestAuthz::new()).await; let scope = random_string(); @@ -362,14 +362,14 @@ mod tests { .expect_err("Unexpectedly succeeded"); } - #[tokio::test] - async fn create_minigroup_with_properties() { + #[sqlx::test] + async fn create_minigroup_with_properties(pool: sqlx::PgPool) { let agent = TestAgent::new("web", "user1", USR_AUDIENCE); let mut authz = TestAuthz::new(); authz.allow(agent.account_id(), vec!["classrooms"], "create"); - let mut state = TestState::new(authz).await; + let mut state = TestState::new(pool, authz).await; let event_room_id = Uuid::new_v4(); let conference_room_id = Uuid::new_v4(); diff --git a/src/app/api/v1/p2p/mod.rs b/src/app/api/v1/p2p/mod.rs index 3c9ae843..a05607f9 100644 --- a/src/app/api/v1/p2p/mod.rs +++ b/src/app/api/v1/p2p/mod.rs @@ -242,14 +242,14 @@ mod tests { use mockall::predicate as pred; use uuid::Uuid; - #[tokio::test] - async fn create_p2p() { + #[sqlx::test] + async fn create_p2p(pool: sqlx::PgPool) { let agent = TestAgent::new("web", "user1", USR_AUDIENCE); let mut authz = TestAuthz::new(); authz.allow(agent.account_id(), vec!["classrooms"], "create"); - let mut state = TestState::new(authz).await; + let mut state = TestState::new(pool, authz).await; let event_room_id = Uuid::new_v4(); let conference_room_id = Uuid::new_v4(); @@ -279,11 +279,11 @@ mod tests { .expect("p2p not found"); } - #[tokio::test] - async fn create_p2p_unauthorized() { + #[sqlx::test] + async fn create_p2p_unauthorized(pool: sqlx::PgPool) { let agent = TestAgent::new("web", "user1", USR_AUDIENCE); - let state = TestState::new(TestAuthz::new()).await; + let state = TestState::new(pool, TestAuthz::new()).await; let scope = random_string(); @@ -301,14 +301,14 @@ mod tests { .expect_err("Unexpectedly succeeded"); } - #[tokio::test] - async fn create_p2p_with_properties() { + #[sqlx::test] + async fn create_p2p_with_properties(pool: sqlx::PgPool) { let agent = TestAgent::new("web", "user1", USR_AUDIENCE); let mut authz = TestAuthz::new(); authz.allow(agent.account_id(), vec!["classrooms"], "create"); - let mut state = TestState::new(authz).await; + let mut state = TestState::new(pool, authz).await; let event_room_id = Uuid::new_v4(); let conference_room_id = Uuid::new_v4(); diff --git a/src/app/api/v1/tests/mod.rs b/src/app/api/v1/tests/mod.rs index f189daf6..43addf16 100644 --- a/src/app/api/v1/tests/mod.rs +++ b/src/app/api/v1/tests/mod.rs @@ -8,9 +8,9 @@ use super::*; use crate::app::http; use crate::test_helpers::prelude::*; -#[tokio::test] -async fn test_healthz() { - let state = TestState::new(TestAuthz::new()).await; +#[sqlx::test] +async fn test_healthz(pool: sqlx::PgPool) { + let state = TestState::new(pool, TestAuthz::new()).await; let state = Arc::new(state) as Arc; let app = http::router(state, HashMap::new()); @@ -29,15 +29,15 @@ async fn test_healthz() { assert_eq!(&body[..], b"Ok"); } -#[tokio::test] -async fn test_api_rollback() { +#[sqlx::test] +async fn test_api_rollback(pool: sqlx::PgPool) { let agent = TestAgent::new("web", "user123", USR_AUDIENCE); let token = agent.token(); let mut authz = TestAuthz::new(); authz.set_audience(SVC_AUDIENCE); authz.allow(agent.account_id(), vec!["scopes"], "rollback"); - let state = TestState::new(authz).await; + let state = TestState::new(pool, authz).await; let state = Arc::new(state) as Arc; let app = crate::app::http::router(state.clone(), make_authn()); diff --git a/src/app/api/v1/webinar/convert.rs b/src/app/api/v1/webinar/convert.rs index c6042abd..d3bcaaad 100644 --- a/src/app/api/v1/webinar/convert.rs +++ b/src/app/api/v1/webinar/convert.rs @@ -240,11 +240,11 @@ mod tests { use mockall::predicate as pred; use serde_json::{json, Value}; - #[tokio::test] - async fn convert_webinar_unauthorized() { + #[sqlx::test] + async fn convert_webinar_unauthorized(pool: sqlx::PgPool) { let agent = TestAgent::new("web", "user1", USR_AUDIENCE); - let state = TestState::new(TestAuthz::new()).await; + let state = TestState::new(pool, TestAuthz::new()).await; let event_room_id = Uuid::new_v4(); let conference_room_id = Uuid::new_v4(); @@ -269,14 +269,14 @@ mod tests { .expect_err("Unexpectedly succeeded"); } - #[tokio::test] - async fn convert_webinar() { + #[sqlx::test] + async fn convert_webinar(pool: sqlx::PgPool) { let agent = TestAgent::new("web", "user1", USR_AUDIENCE); let mut authz = TestAuthz::new(); authz.allow(agent.account_id(), vec!["classrooms"], "convert"); - let mut state = TestState::new(authz).await; + let mut state = TestState::new(pool, authz).await; let event_room_id = Uuid::new_v4(); let conference_room_id = Uuid::new_v4(); convert_webinar_mocks(&mut state, event_room_id, conference_room_id); @@ -313,14 +313,14 @@ mod tests { ); } - #[tokio::test] - async fn convert_webinar_with_recording() { + #[sqlx::test] + async fn convert_webinar_with_recording(pool: sqlx::PgPool) { let agent = TestAgent::new("web", "user1", USR_AUDIENCE); let mut authz = TestAuthz::new(); authz.allow(agent.account_id(), vec!["classrooms"], "convert"); - let mut state = TestState::new(authz).await; + let mut state = TestState::new(pool, authz).await; let event_room_id = Uuid::new_v4(); let conference_room_id = Uuid::new_v4(); convert_webinar_mocks(&mut state, event_room_id, conference_room_id); @@ -365,14 +365,14 @@ mod tests { ); } - #[tokio::test] - async fn convert_webinar_unspecified_time() { + #[sqlx::test] + async fn convert_webinar_unspecified_time(pool: sqlx::PgPool) { let agent = TestAgent::new("web", "user1", USR_AUDIENCE); let mut authz = TestAuthz::new(); authz.allow(agent.account_id(), vec!["classrooms"], "convert"); - let mut state = TestState::new(authz).await; + let mut state = TestState::new(pool, authz).await; let event_room_id = Uuid::new_v4(); let conference_room_id = Uuid::new_v4(); convert_unspecified_time_webinar_mocks(&mut state, event_room_id, conference_room_id); diff --git a/src/app/api/v1/webinar/create/replica.rs b/src/app/api/v1/webinar/create/replica.rs index eeeed083..cdf354a4 100644 --- a/src/app/api/v1/webinar/create/replica.rs +++ b/src/app/api/v1/webinar/create/replica.rs @@ -171,10 +171,10 @@ mod tests { use mockall::predicate as pred; use uuid::Uuid; - #[tokio::test] - async fn create_replica_unauthorized() { + #[sqlx::test] + async fn create_replica_unauthorized(pool: sqlx::PgPool) { let agent = TestAgent::new("web", "user1", USR_AUDIENCE); - let state = TestState::new(TestAuthz::new()).await; + let state = TestState::new(pool, TestAuthz::new()).await; let scope = random_string(); let class_id = Uuid::new_v4(); @@ -189,14 +189,14 @@ mod tests { .expect_err("Unexpectedly succeeded"); } - #[tokio::test] - async fn create_replica_original_webinar_not_found() { + #[sqlx::test] + async fn create_replica_original_webinar_not_found(pool: sqlx::PgPool) { let agent = TestAgent::new("web", "user1", USR_AUDIENCE); let mut authz = TestAuthz::new(); authz.allow(agent.account_id(), vec!["classrooms"], "create"); - let mut state = TestState::new(authz).await; + let mut state = TestState::new(pool, authz).await; let event_room_id = Uuid::new_v4(); let conference_room_id = Uuid::new_v4(); let class_id = Uuid::new_v4(); @@ -216,14 +216,14 @@ mod tests { .expect_err("Unexpectedly succeeded"); } - #[tokio::test] - async fn create_replica_from_original_webinar() { + #[sqlx::test] + async fn create_replica_from_original_webinar(pool: sqlx::PgPool) { let agent = TestAgent::new("web", "user1", USR_AUDIENCE); let mut authz = TestAuthz::new(); authz.allow(agent.account_id(), vec!["classrooms"], "create"); - let mut state = TestState::new(authz).await; + let mut state = TestState::new(pool, authz).await; let event_room_id = Uuid::new_v4(); let conference_room_id = Uuid::new_v4(); diff --git a/src/app/api/v1/webinar/create/webinar.rs b/src/app/api/v1/webinar/create/webinar.rs index 35af28d5..1b08315a 100644 --- a/src/app/api/v1/webinar/create/webinar.rs +++ b/src/app/api/v1/webinar/create/webinar.rs @@ -178,14 +178,14 @@ mod tests { use mockall::predicate as pred; use uuid::Uuid; - #[tokio::test] - async fn create_webinar_no_time() { + #[sqlx::test] + async fn create_webinar_no_time(pool: sqlx::PgPool) { let agent = TestAgent::new("web", "user1", USR_AUDIENCE); let mut authz = TestAuthz::new(); authz.allow(agent.account_id(), vec!["classrooms"], "create"); - let mut state = TestState::new(authz).await; + let mut state = TestState::new(pool, authz).await; let event_room_id = Uuid::new_v4(); let conference_room_id = Uuid::new_v4(); @@ -220,14 +220,14 @@ mod tests { assert_eq!(new_webinar.reserve(), Some(10)); } - #[tokio::test] - async fn create_webinar_with_time() { + #[sqlx::test] + async fn create_webinar_with_time(pool: sqlx::PgPool) { let agent = TestAgent::new("web", "user1", USR_AUDIENCE); let mut authz = TestAuthz::new(); authz.allow(agent.account_id(), vec!["classrooms"], "create"); - let mut state = TestState::new(authz).await; + let mut state = TestState::new(pool, authz).await; let event_room_id = Uuid::new_v4(); let conference_room_id = Uuid::new_v4(); @@ -268,11 +268,10 @@ mod tests { assert_eq!(new_webinar.reserve(), Some(10)); } - #[tokio::test] - async fn create_webinar_unauthorized() { + #[sqlx::test] + async fn create_webinar_unauthorized(pool: sqlx::PgPool) { let agent = TestAgent::new("web", "user1", USR_AUDIENCE); - - let state = TestState::new(TestAuthz::new()).await; + let state = TestState::new(pool, TestAuthz::new()).await; let scope = random_string(); @@ -293,14 +292,14 @@ mod tests { .expect_err("Unexpectedly succeeded"); } - #[tokio::test] - async fn create_webinar_with_properties() { + #[sqlx::test] + async fn create_webinar_with_properties(pool: sqlx::PgPool) { let agent = TestAgent::new("web", "user1", USR_AUDIENCE); let mut authz = TestAuthz::new(); authz.allow(agent.account_id(), vec!["classrooms"], "create"); - let mut state = TestState::new(authz).await; + let mut state = TestState::new(pool, authz).await; let event_room_id = Uuid::new_v4(); let conference_room_id = Uuid::new_v4(); diff --git a/src/app/api/v1/webinar/download.rs b/src/app/api/v1/webinar/download.rs index 249e59a8..eb3ddcc5 100644 --- a/src/app/api/v1/webinar/download.rs +++ b/src/app/api/v1/webinar/download.rs @@ -79,11 +79,11 @@ mod tests { use chrono::{Duration, Utc}; use hyper::body::to_bytes; - #[tokio::test] - async fn create_webinar_timestamp() { + #[sqlx::test] + async fn create_webinar_timestamp(pool: sqlx::PgPool) { let agent = TestAgent::new("web", "user1", USR_AUDIENCE); - let db_pool = TestDb::new().await; + let db_pool = TestDb::new(pool); let webinar = { let mut conn = db_pool.get_conn().await; diff --git a/src/app/error.rs b/src/app/error.rs index 339d13db..2e8da560 100644 --- a/src/app/error.rs +++ b/src/app/error.rs @@ -16,7 +16,7 @@ struct ErrorKindProperties { is_notify_sentry: bool, } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ErrorKind { AccessDenied, AuthorizationFailed, @@ -276,6 +276,11 @@ impl Error { } } } + + #[cfg(test)] + pub fn kind(&self) -> ErrorKind { + self.kind + } } impl IntoResponse for Error { diff --git a/src/app/postprocessing_strategy/minigroup/tests.rs b/src/app/postprocessing_strategy/minigroup/tests.rs index 4bfc660b..2209a7af 100644 --- a/src/app/postprocessing_strategy/minigroup/tests.rs +++ b/src/app/postprocessing_strategy/minigroup/tests.rs @@ -14,10 +14,10 @@ mod handle_upload { use super::super::super::PostprocessingStrategy; use super::super::*; - #[tokio::test] - async fn handle_upload_stream() { + #[sqlx::test] + async fn handle_upload_stream(pool: sqlx::PgPool) { let now = Utc::now(); - let mut state = TestState::new(TestAuthz::new()).await; + let mut state = TestState::new(pool, TestAuthz::new()).await; let conference_room_id = Uuid::new_v4(); let event_room_id = Uuid::new_v4(); let agent1 = TestAgent::new("web", "user1", USR_AUDIENCE); @@ -221,10 +221,10 @@ mod handle_upload { assert_eq!(&recording2.created_by, agent2.agent_id()); } - #[tokio::test] - async fn handle_upload_mjr() { + #[sqlx::test] + async fn handle_upload_mjr(pool: sqlx::PgPool) { let now = Utc::now(); - let mut state = TestState::new(TestAuthz::new()).await; + let mut state = TestState::new(pool, TestAuthz::new()).await; let conference_room_id = Uuid::new_v4(); let event_room_id = Uuid::new_v4(); @@ -342,12 +342,12 @@ mod handle_adjust { use super::super::super::PostprocessingStrategy; use super::super::*; - #[tokio::test] - async fn handle_adjust() { + #[sqlx::test] + async fn handle_adjust(pool: sqlx::PgPool) { let now = Utc::now(); let agent1 = TestAgent::new("web", "user1", USR_AUDIENCE); let agent2 = TestAgent::new("web", "user2", USR_AUDIENCE); - let mut state = TestState::new(TestAuthz::new()).await; + let mut state = TestState::new(pool, TestAuthz::new()).await; let event_room_id = Uuid::new_v4(); let original_event_room_id = Uuid::new_v4(); let modified_event_room_id = Uuid::new_v4(); @@ -582,12 +582,12 @@ mod handle_adjust { } } - #[tokio::test] - async fn handle_adjust_with_pin_and_unpin() { + #[sqlx::test] + async fn handle_adjust_with_pin_and_unpin(pool: sqlx::PgPool) { let now = Utc::now(); let agent1 = TestAgent::new("web", "user1", USR_AUDIENCE); let agent2 = TestAgent::new("web", "user2", USR_AUDIENCE); - let mut state = TestState::new(TestAuthz::new()).await; + let mut state = TestState::new(pool, TestAuthz::new()).await; let event_room_id = Uuid::new_v4(); let original_event_room_id = Uuid::new_v4(); let modified_event_room_id = Uuid::new_v4(); @@ -822,12 +822,12 @@ mod handle_transcoding_completion { use super::super::super::PostprocessingStrategy; use super::super::*; - #[tokio::test] - async fn handle_transcoding_completion() { + #[sqlx::test] + async fn handle_transcoding_completion(pool: sqlx::PgPool) { let now = Utc::now(); let agent1 = TestAgent::new("web", "user1", USR_AUDIENCE); let agent2 = TestAgent::new("web", "user2", USR_AUDIENCE); - let state = TestState::new(TestAuthz::new()).await; + let state = TestState::new(pool, TestAuthz::new()).await; // Insert a minigroup with recordings. let (minigroup, recording1, recording2) = { diff --git a/src/app/stage/ban.rs b/src/app/stage/ban.rs index de696a19..6735b49b 100644 --- a/src/app/stage/ban.rs +++ b/src/app/stage/ban.rs @@ -198,7 +198,7 @@ pub async fn handle_video_streaming_banned( .error(AppErrorKind::DbConnAcquisitionFailed) .transient()?; - let op = ban_account_op::UpsertQuery::new_video_streaming_banned( + let op = ban_account_op::UpdateQuery::new_video_streaming_banned( video_streaming_banned.target_account.clone(), video_streaming_banned.operation_id, ) @@ -229,7 +229,7 @@ pub async fn handle_collaboration_banned( .error(AppErrorKind::DbConnAcquisitionFailed) .transient()?; - let op = ban_account_op::UpsertQuery::new_collaboration_banned( + let op = ban_account_op::UpdateQuery::new_collaboration_banned( collaboration_banned.target_account.clone(), collaboration_banned.operation_id, ) @@ -271,3 +271,364 @@ async fn finish( ) .await } + +#[cfg(test)] +mod tests { + use svc_events::{Event, EventV1}; + + use super::*; + + use crate::app::AppContext; + use crate::test_helpers::prelude::*; + + #[sqlx::test] + async fn handles_intents(pool: sqlx::PgPool) { + let state = TestState::new(pool, TestAuthz::new()).await; + + let minigroup = { + let mut conn = state.get_conn().await.expect("Failed to fetch connection"); + factory::Minigroup::new( + random_string(), + USR_AUDIENCE.to_string(), + (Bound::Unbounded, Bound::Unbounded).into(), + Uuid::new_v4(), + Uuid::new_v4(), + ) + .insert(&mut conn) + .await + }; + + let agent1 = TestAgent::new("web", "user1", USR_AUDIENCE); + + let intent = BanIntentV1 { + classroom_id: minigroup.id(), + ban: true, + sender: agent1.agent_id().clone(), + last_operation_id: 0, + target_account: agent1.account_id().clone(), + }; + let intent_id: EventId = ("ban".to_string(), "intent".to_string(), 0).into(); + + handle_intent(&state, intent.clone(), intent_id) + .await + .expect("failed to handle intent"); + + { + let pub_reqs = state.inspect_nats_client().get_publish_requests(); + assert_eq!(pub_reqs.len(), 1); + + let payload = serde_json::from_slice::(&pub_reqs[0].payload) + .expect("failed to parse event"); + assert!(matches!(payload, Event::V1(EventV1::BanAccepted(..)))); + } + + // should fail b/c we already started an operation with another sequence id + let intent_id: EventId = ("ban".to_string(), "intent".to_string(), 1).into(); + + handle_intent(&state, intent.clone(), intent_id) + .await + .expect("failed to handle intent"); + + { + let pub_reqs = state.inspect_nats_client().get_publish_requests(); + assert_eq!(pub_reqs.len(), 2); + + let payload = serde_json::from_slice::(&pub_reqs[1].payload) + .expect("failed to parse event"); + assert!(matches!(payload, Event::V1(EventV1::BanRejected(..)))); + } + } + + #[sqlx::test] + async fn fails_to_handle_video_streaming_banned_if_there_was_no_intent(pool: sqlx::PgPool) { + let state = TestState::new(pool, TestAuthz::new()).await; + let agent1 = TestAgent::new("web", "user1", USR_AUDIENCE); + + let video_streaming_banned = BanVideoStreamingCompletedV1 { + ban: true, + classroom_id: Uuid::new_v4(), + target_account: agent1.account_id().clone(), + operation_id: 0, + parent: ("ban".to_owned(), "accepted".to_owned(), 0).into(), + }; + + let r = handle_video_streaming_banned(&state, video_streaming_banned).await; + assert!(matches!( + r, + Err(HandleMessageFailure::Permanent( + e @ crate::app::error::Error { .. } + )) if e.kind() == AppErrorKind::OperationFailed + )); + + { + let mut conn = state.get_conn().await.expect("Failed to fetch connection"); + let r = db::ban_account_op::ReadQuery::by_account_id(agent1.account_id()) + .execute(&mut conn) + .await + .expect("failed to fetch ban account op entry"); + assert!(r.is_none()); + } + + { + let pub_reqs = state.inspect_nats_client().get_publish_requests(); + assert_eq!(pub_reqs.len(), 0); + } + } + + #[sqlx::test] + async fn fails_to_handle_collaboration_banned_if_there_was_no_intent(pool: sqlx::PgPool) { + let state = TestState::new(pool, TestAuthz::new()).await; + let agent1 = TestAgent::new("web", "user1", USR_AUDIENCE); + + let collaboration_banned = BanCollaborationCompletedV1 { + ban: true, + classroom_id: Uuid::new_v4(), + target_account: agent1.account_id().clone(), + operation_id: 0, + parent: ("ban".to_owned(), "accepted".to_owned(), 0).into(), + }; + + let r = handle_collaboration_banned(&state, collaboration_banned).await; + assert!(matches!( + r, + Err(HandleMessageFailure::Permanent( + e @ crate::app::error::Error { .. } + )) if e.kind() == AppErrorKind::OperationFailed + )); + + { + let mut conn = state.get_conn().await.expect("Failed to fetch connection"); + let r = db::ban_account_op::ReadQuery::by_account_id(agent1.account_id()) + .execute(&mut conn) + .await + .expect("failed to fetch ban account op entry"); + assert!(r.is_none()); + } + + { + let pub_reqs = state.inspect_nats_client().get_publish_requests(); + assert_eq!(pub_reqs.len(), 0); + } + } + + #[sqlx::test] + async fn handles_video_streaming_banned(pool: sqlx::PgPool) { + let state = TestState::new(pool, TestAuthz::new()).await; + + let minigroup = { + let mut conn = state.get_conn().await.expect("Failed to fetch connection"); + factory::Minigroup::new( + random_string(), + USR_AUDIENCE.to_string(), + (Bound::Unbounded, Bound::Unbounded).into(), + Uuid::new_v4(), + Uuid::new_v4(), + ) + .insert(&mut conn) + .await + }; + + let agent1 = TestAgent::new("web", "user-video", USR_AUDIENCE); + + { + let mut conn = state.get_conn().await.expect("Failed to fetch connection"); + db::ban_account_op::UpsertQuery::new_operation(agent1.account_id().clone(), 0, 0) + .execute(&mut conn) + .await + .expect("failed to run upsert query"); + }; + + let video_streaming_banned = BanVideoStreamingCompletedV1 { + ban: true, + classroom_id: minigroup.id(), + target_account: agent1.account_id().clone(), + operation_id: 0, + parent: ("ban".to_owned(), "accepted".to_owned(), 0).into(), + }; + + handle_video_streaming_banned(&state, video_streaming_banned) + .await + .expect("failed to handle video streaming banned"); + + { + let mut conn = state.get_conn().await.expect("Failed to fetch connection"); + let r = db::ban_account_op::ReadQuery::by_account_id(agent1.account_id()) + .execute(&mut conn) + .await + .expect("failed to fetch ban account op entry"); + assert!(matches!( + r, + Some(db::ban_account_op::Object { + last_op_id: 0, + is_video_streaming_banned: true, + .. + }) + )); + } + + { + let pub_reqs = state.inspect_nats_client().get_publish_requests(); + assert_eq!(pub_reqs.len(), 0); + } + } + + #[sqlx::test] + async fn handles_collaboration_banned(pool: sqlx::PgPool) { + let state = TestState::new(pool, TestAuthz::new()).await; + + let minigroup = { + let mut conn = state.get_conn().await.expect("Failed to fetch connection"); + factory::Minigroup::new( + random_string(), + USR_AUDIENCE.to_string(), + (Bound::Unbounded, Bound::Unbounded).into(), + Uuid::new_v4(), + Uuid::new_v4(), + ) + .insert(&mut conn) + .await + }; + + let agent1 = TestAgent::new("web", "user-collab", USR_AUDIENCE); + + { + let mut conn = state.get_conn().await.expect("Failed to fetch connection"); + db::ban_account_op::UpsertQuery::new_operation(agent1.account_id().clone(), 0, 0) + .execute(&mut conn) + .await + .expect("failed to run upsert query"); + }; + + let collaboration_banned = BanCollaborationCompletedV1 { + ban: true, + classroom_id: minigroup.id(), + target_account: agent1.account_id().clone(), + operation_id: 0, + parent: ("ban".to_owned(), "accepted".to_owned(), 0).into(), + }; + + handle_collaboration_banned(&state, collaboration_banned) + .await + .expect("failed to handle collaboration banned"); + + { + let mut conn = state.get_conn().await.expect("Failed to fetch connection"); + let r = db::ban_account_op::ReadQuery::by_account_id(agent1.account_id()) + .execute(&mut conn) + .await + .expect("failed to fetch ban account op entry"); + assert!(matches!( + r, + Some(db::ban_account_op::Object { + last_op_id: 0, + is_collaboration_banned: true, + .. + }) + )); + } + + { + let pub_reqs = state.inspect_nats_client().get_publish_requests(); + assert_eq!(pub_reqs.len(), 0); + } + } + + #[sqlx::test] + async fn finishes_operation(pool: sqlx::PgPool) { + let state = TestState::new(pool, TestAuthz::new()).await; + + let minigroup = { + let mut conn = state.get_conn().await.expect("Failed to fetch connection"); + factory::Minigroup::new( + random_string(), + USR_AUDIENCE.to_string(), + (Bound::Unbounded, Bound::Unbounded).into(), + Uuid::new_v4(), + Uuid::new_v4(), + ) + .insert(&mut conn) + .await + }; + + let agent1 = TestAgent::new("web", "user-finish", USR_AUDIENCE); + + { + let mut conn = state.get_conn().await.expect("Failed to fetch connection"); + db::ban_account_op::UpsertQuery::new_operation(agent1.account_id().clone(), 0, 0) + .execute(&mut conn) + .await + .expect("failed to run upsert query"); + }; + + let video_streaming_banned = BanVideoStreamingCompletedV1 { + ban: true, + classroom_id: minigroup.id(), + target_account: agent1.account_id().clone(), + operation_id: 0, + parent: ("ban".to_owned(), "accepted".to_owned(), 0).into(), + }; + + handle_video_streaming_banned(&state, video_streaming_banned) + .await + .expect("failed to handle video streaming banned"); + + { + let mut conn = state.get_conn().await.expect("Failed to fetch connection"); + let r = db::ban_account_op::ReadQuery::by_account_id(agent1.account_id()) + .execute(&mut conn) + .await + .expect("failed to fetch ban account op entry"); + assert!(matches!( + r, + Some(db::ban_account_op::Object { + last_op_id: 0, + is_video_streaming_banned: true, + .. + }) + )); + } + + { + let pub_reqs = state.inspect_nats_client().get_publish_requests(); + assert_eq!(pub_reqs.len(), 0); + } + + let collaboration_banned = BanCollaborationCompletedV1 { + ban: true, + classroom_id: minigroup.id(), + target_account: agent1.account_id().clone(), + operation_id: 0, + parent: ("ban".to_owned(), "accepted".to_owned(), 0).into(), + }; + + handle_collaboration_banned(&state, collaboration_banned) + .await + .expect("failed to handle collaboration banned"); + + { + let mut conn = state.get_conn().await.expect("Failed to fetch connection"); + let r = db::ban_account_op::ReadQuery::by_account_id(agent1.account_id()) + .execute(&mut conn) + .await + .expect("failed to fetch ban account op entry"); + assert!(matches!( + r, + Some(db::ban_account_op::Object { + last_op_id: 0, + is_collaboration_banned: true, + is_video_streaming_banned: true, + .. + }) + )); + } + + { + let pub_reqs = state.inspect_nats_client().get_publish_requests(); + assert_eq!(pub_reqs.len(), 1); + + let payload = serde_json::from_slice::(&pub_reqs[0].payload) + .expect("failed to parse event"); + assert!(matches!(payload, Event::V1(EventV1::BanCompleted(..)))); + } + } +} diff --git a/src/app/tide_state/mod.rs b/src/app/tide_state/mod.rs index 867a9bac..52ece2fa 100644 --- a/src/app/tide_state/mod.rs +++ b/src/app/tide_state/mod.rs @@ -33,7 +33,7 @@ pub trait AppContext: Sync + Send { fn config(&self) -> &Config; fn agent(&self) -> Option<&Agent>; fn turn_host_selector(&self) -> &TurnHostSelector; - fn nats_client(&self) -> Option<&dyn NatsClient>; + fn nats_client(&self) -> Option>; fn get_preroll_offset(&self, audience: &str) -> i64 { self.config() @@ -158,8 +158,8 @@ impl AppContext for TideState { &self.turn_host_selector } - fn nats_client(&self) -> Option<&dyn NatsClient> { - self.nats_client.as_deref() + fn nats_client(&self) -> Option> { + self.nats_client.clone() } } diff --git a/src/db/ban_account_op.rs b/src/db/ban_account_op.rs index 08d2b54e..bebe4254 100644 --- a/src/db/ban_account_op.rs +++ b/src/db/ban_account_op.rs @@ -1,6 +1,7 @@ use sqlx::PgConnection; use svc_authn::AccountId; +#[derive(Debug)] pub struct Object { pub user_account: AccountId, pub last_op_id: i64, @@ -62,8 +63,6 @@ pub async fn get_next_seq_id(conn: &mut PgConnection) -> sqlx::Result /// Returns `None` if `last_op_id` in database differs. pub struct UpsertQuery { user_account: AccountId, - is_video_streaming_banned: Option, - is_collaboration_banned: Option, last_op_id: i64, new_op_id: i64, } @@ -72,20 +71,63 @@ impl UpsertQuery { pub fn new_operation(user_account: AccountId, last_op_id: i64, new_op_id: i64) -> Self { Self { user_account, - is_video_streaming_banned: None, - is_collaboration_banned: None, last_op_id, new_op_id, } } + pub async fn execute(self, conn: &mut PgConnection) -> sqlx::Result> { + sqlx::query_as!( + Object, + r#" + INSERT INTO ban_account_op (user_account, last_op_id) + VALUES ($1, $2) + ON CONFLICT (user_account) DO UPDATE + SET + -- to reset sub-operation trackers only if op_id has changed + is_video_streaming_banned = ($2 = $3 AND ban_account_op.is_video_streaming_banned), + is_collaboration_banned = ($2 = $3 AND ban_account_op.is_collaboration_banned), + last_op_id = EXCLUDED.last_op_id + WHERE + -- allow to 'complete' operation if there's no change in last_op_id + -- or allow to do upsert without real changes so we can process + -- the same message twice + ban_account_op.last_op_id = EXCLUDED.last_op_id OR + -- allow change op id if the previous operation is completed + ( + ban_account_op.last_op_id = $3 AND + ban_account_op.is_video_streaming_banned = true AND + ban_account_op.is_collaboration_banned = true + ) + RETURNING + user_account AS "user_account: _", + last_op_id, + is_video_streaming_banned, + is_collaboration_banned + "#, + self.user_account as AccountId, + self.new_op_id, + self.last_op_id + ) + .fetch_optional(conn) + .await + } +} + +pub struct UpdateQuery { + user_account: AccountId, + is_video_streaming_banned: Option, + is_collaboration_banned: Option, + last_op_id: i64, +} + +impl UpdateQuery { pub fn new_video_streaming_banned(user_account: AccountId, op_id: i64) -> Self { Self { user_account, is_video_streaming_banned: Some(true), is_collaboration_banned: None, last_op_id: op_id, - new_op_id: op_id, } } @@ -95,7 +137,6 @@ impl UpsertQuery { is_video_streaming_banned: None, is_collaboration_banned: Some(true), last_op_id: op_id, - new_op_id: op_id, } } @@ -103,24 +144,13 @@ impl UpsertQuery { sqlx::query_as!( Object, r#" - INSERT INTO ban_account_op (user_account, last_op_id, is_video_streaming_banned, is_collaboration_banned) - VALUES ($1, $2, COALESCE($3, false), COALESCE($4, false)) - ON CONFLICT (user_account) DO UPDATE + UPDATE ban_account_op SET - is_video_streaming_banned = COALESCE($3, ban_account_op.is_video_streaming_banned), - is_collaboration_banned = COALESCE($4, ban_account_op.is_collaboration_banned), - last_op_id = EXCLUDED.last_op_id + is_video_streaming_banned = COALESCE($2, ban_account_op.is_video_streaming_banned), + is_collaboration_banned = COALESCE($3, ban_account_op.is_collaboration_banned) WHERE - -- allow to 'complete' operation if there's no change in last_op_id - -- or allow to do upsert without real changes so we can process - -- the same message twice - ban_account_op.last_op_id = EXCLUDED.last_op_id OR - -- allow change op id if the previous operation is completed - ( - ban_account_op.last_op_id = $5 AND - ban_account_op.is_video_streaming_banned = true AND - ban_account_op.is_collaboration_banned = true - ) + ban_account_op.last_op_id = $4 + AND ban_account_op.user_account = $1 RETURNING user_account AS "user_account: _", last_op_id, @@ -128,7 +158,6 @@ impl UpsertQuery { is_collaboration_banned "#, self.user_account as AccountId, - self.new_op_id, self.is_video_streaming_banned, self.is_collaboration_banned, self.last_op_id @@ -137,3 +166,130 @@ impl UpsertQuery { .await } } + +#[cfg(test)] +mod tests { + use super::*; + + use crate::app::AppContext; + use crate::test_helpers::prelude::*; + + #[sqlx::test] + async fn doesnt_allow_to_change_op_id_until_operation_is_completed(pool: sqlx::PgPool) { + let state = TestState::new(pool, TestAuthz::new()).await; + let mut conn = state.get_conn().await.expect("Failed to fetch connection"); + + let agent1 = TestAgent::new("web", "user1", USR_AUDIENCE); + + let r = UpsertQuery::new_operation(agent1.account_id().clone(), 0, 0) + .execute(&mut conn) + .await + .expect("failed to start new ban operation"); + assert!(r.is_some()); + + async fn test_possible_failure_states(agent: &TestAgent, conn: &mut sqlx::PgConnection) { + // should fail b/c previous operation is not completed yet + let r = UpsertQuery::new_operation(agent.account_id().clone(), 0, 100) + .execute(conn) + .await + .expect("failed to start new ban operation"); + assert!(r.is_none()); + + // should fail b/c we passed the wrong previous operation id + let r = UpsertQuery::new_operation(agent.account_id().clone(), 50, 100) + .execute(conn) + .await + .expect("failed to start new ban operation"); + assert!(r.is_none()); + + // should be ok since we didn't change anything (idempotency) + let r = UpsertQuery::new_operation(agent.account_id().clone(), 0, 0) + .execute(conn) + .await + .expect("failed to start new ban operation"); + assert!(r.is_some()); + } + + test_possible_failure_states(&agent1, &mut conn).await; + + // should fail b/c we passed to wrong operation id + let r = UpdateQuery::new_collaboration_banned(agent1.account_id().clone(), 10) + .execute(&mut conn) + .await + .expect("failed to complete collaboration ban"); + assert!(r.is_none()); + + let r = UpdateQuery::new_collaboration_banned(agent1.account_id().clone(), 0) + .execute(&mut conn) + .await + .expect("failed to complete collaboration ban"); + assert!(r.is_some()); + assert!(matches!( + r, + Some(Object { + is_collaboration_banned: true, + last_op_id: 0, + .. + }) + )); + + // should be ok to run twice (idempotency) + let r = UpdateQuery::new_collaboration_banned(agent1.account_id().clone(), 0) + .execute(&mut conn) + .await + .expect("failed to complete collaboration ban"); + assert!(r.is_some()); + + test_possible_failure_states(&agent1, &mut conn).await; + + // should fail b/c we passed to wrong operation id + let r = UpdateQuery::new_video_streaming_banned(agent1.account_id().clone(), 10) + .execute(&mut conn) + .await + .expect("failed to complete video streaming ban"); + assert!(r.is_none()); + + let r = UpdateQuery::new_video_streaming_banned(agent1.account_id().clone(), 0) + .execute(&mut conn) + .await + .expect("failed to complete video streaming ban"); + assert!(r.is_some()); + assert!(matches!( + r, + Some(Object { + is_video_streaming_banned: true, + is_collaboration_banned: true, + last_op_id: 0, + .. + }) + )); + + // should be ok to run twice (idempotency) + let r = UpdateQuery::new_video_streaming_banned(agent1.account_id().clone(), 0) + .execute(&mut conn) + .await + .expect("failed to complete video streaming ban"); + assert!(r.is_some()); + + // should fail b/c we passed the wrong previous operation id + let r = UpsertQuery::new_operation(agent1.account_id().clone(), 50, 100) + .execute(&mut conn) + .await + .expect("failed to start new ban operation"); + assert!(r.is_none()); + + // should be ok since we didn't change anything (idempotency) + let r = UpsertQuery::new_operation(agent1.account_id().clone(), 0, 0) + .execute(&mut conn) + .await + .expect("failed to start new ban operation"); + assert!(r.is_some()); + + // should be ok to start new operation afterwards + let r = UpsertQuery::new_operation(agent1.account_id().clone(), 0, 1) + .execute(&mut conn) + .await + .expect("failed to complete video streaming ban"); + assert!(r.is_some()); + } +} diff --git a/src/db/ban_history.rs b/src/db/ban_history.rs index 36accf4c..13cab186 100644 --- a/src/db/ban_history.rs +++ b/src/db/ban_history.rs @@ -2,7 +2,7 @@ use chrono::{DateTime, Utc}; use svc_authn::AccountId; use uuid::Uuid; -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq)] pub struct Object { #[allow(unused)] target_account: AccountId, @@ -37,17 +37,34 @@ impl<'a> InsertQuery<'a> { sqlx::query_as!( Object, r#" - INSERT INTO ban_history (class_id, target_account, banned_operation_id) - VALUES ($1, $2, $3) - -- this allows us to run this query many times idempotently - ON CONFLICT (banned_operation_id) DO NOTHING - RETURNING - target_account AS "target_account: AccountId", - class_id, - banned_at, - banned_operation_id, + WITH i AS ( + INSERT INTO ban_history (class_id, target_account, banned_operation_id) + VALUES ($1, $2, $3) + -- this allows us to run this query many times idempotently + ON CONFLICT (banned_operation_id) DO NOTHING + RETURNING * + ) + -- gets row if there was no conflict + SELECT + target_account AS "target_account!: AccountId", + class_id AS "class_id!: _", + banned_at AS "banned_at!: _", + banned_operation_id AS "banned_operation_id!: _", unbanned_at, unbanned_operation_id + FROM i + -- or selects original row if there was a conflict + UNION + SELECT + target_account AS "target_account: AccountId", + class_id, + banned_at, + banned_operation_id, + unbanned_at, + unbanned_operation_id + FROM ban_history + WHERE + banned_operation_id = $3 "#, self.class_id, self.target_account as &AccountId, @@ -101,3 +118,90 @@ impl<'a> FinishBanQuery<'a> { .await } } + +#[cfg(test)] +mod tests { + use super::*; + + use crate::app::AppContext; + use crate::test_helpers::prelude::*; + + #[sqlx::test] + async fn finishes_ban(pool: sqlx::PgPool) { + let state = TestState::new(pool, TestAuthz::new()).await; + let mut conn = state.get_conn().await.expect("Failed to fetch connection"); + + let minigroup = factory::Minigroup::new( + random_string(), + USR_AUDIENCE.to_string(), + (Bound::Unbounded, Bound::Unbounded).into(), + Uuid::new_v4(), + Uuid::new_v4(), + ) + .insert(&mut conn) + .await; + + let entry_start = InsertQuery::new(minigroup.id(), &AccountId::new("test", "test"), 0) + .execute(&mut conn) + .await + .expect("failed to insert ban history entry"); + + let entry_finish = FinishBanQuery::new(minigroup.id(), &entry_start.target_account, 1) + .execute(&mut conn) + .await + .expect("failed to run finish ban query") + .expect("failed to find entry"); + + assert_eq!(entry_start.target_account, entry_finish.target_account); + assert_eq!(entry_start.class_id, entry_finish.class_id); + assert_eq!(entry_start.banned_at, entry_finish.banned_at); + assert_eq!( + entry_start.banned_operation_id, + entry_finish.banned_operation_id + ); + assert_eq!(entry_finish.unbanned_operation_id, Some(1)); + } + + #[sqlx::test] + async fn inserts_idempotently(pool: sqlx::PgPool) { + let state = TestState::new(pool, TestAuthz::new()).await; + let mut conn = state.get_conn().await.expect("Failed to fetch connection"); + + let minigroup1 = factory::Minigroup::new( + random_string(), + USR_AUDIENCE.to_string(), + (Bound::Unbounded, Bound::Unbounded).into(), + Uuid::new_v4(), + Uuid::new_v4(), + ) + .insert(&mut conn) + .await; + + let entry1 = InsertQuery::new(minigroup1.id(), &AccountId::new("test", "test"), 0) + .execute(&mut conn) + .await + .expect("failed to insert ban history entry"); + + let minigroup2 = factory::Minigroup::new( + random_string(), + USR_AUDIENCE.to_string(), + (Bound::Unbounded, Bound::Unbounded).into(), + Uuid::new_v4(), + Uuid::new_v4(), + ) + .insert(&mut conn) + .await; + + let entry2 = InsertQuery::new( + minigroup2.id(), + &AccountId::new("test-another", "test-another"), + 0, + ) + .execute(&mut conn) + .await + .expect("failed to insert ban history entry"); + + // so we just got original entry back + assert_eq!(entry1, entry2); + } +} diff --git a/src/db/class/insert_query.rs b/src/db/class/insert_query.rs index 47829646..28f97b53 100644 --- a/src/db/class/insert_query.rs +++ b/src/db/class/insert_query.rs @@ -214,9 +214,9 @@ mod tests { use super::*; use crate::test_helpers::prelude::*; - #[tokio::test] - async fn insert_already_established_webinar() { - let db = TestDb::new().await; + #[sqlx::test] + async fn insert_already_established_webinar(pool: sqlx::PgPool) { + let db = TestDb::new(pool); let mut conn = db.get_conn().await; let webinar = { @@ -255,9 +255,9 @@ mod tests { assert_eq!(time.0, Bound::Unbounded); } - #[tokio::test] - async fn insert_not_established_webinar() { - let db = TestDb::new().await; + #[sqlx::test] + async fn insert_not_established_webinar(pool: sqlx::PgPool) { + let db = TestDb::new(pool); let mut conn = db.get_conn().await; let dummy = InsertQuery::new( diff --git a/src/db/recording.rs b/src/db/recording.rs index 61afbf7f..660c7eaf 100644 --- a/src/db/recording.rs +++ b/src/db/recording.rs @@ -10,7 +10,7 @@ use serde_derive::{Deserialize, Serialize}; //////////////////////////////////////////////////////////////////////////////// #[allow(dead_code)] -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct Object { id: Uuid, class_id: Uuid, @@ -573,11 +573,11 @@ pub mod tests { use crate::app::AppContext; use crate::test_helpers::prelude::*; - #[tokio::test] - async fn test_minigroup_adjust_not_using_deleted_recordings() { + #[sqlx::test] + async fn test_minigroup_adjust_not_using_deleted_recordings(pool: sqlx::PgPool) { let agent = TestAgent::new("web", "user1", USR_AUDIENCE); - let state = TestState::new(TestAuthz::new()).await; + let state = TestState::new(pool, TestAuthz::new()).await; let mut conn = state.get_conn().await.expect("Failed to fetch connection"); let minigroup = factory::Minigroup::new( random_string(), @@ -614,6 +614,49 @@ pub mod tests { assert_eq!(recordings[0].rtc_id(), recording.rtc_id()); } + #[sqlx::test] + async fn list_recording_skips_records_from_banned_users(pool: sqlx::PgPool) { + let agent1 = TestAgent::new("web", "user1", USR_AUDIENCE); + let agent2 = TestAgent::new("web", "user2", USR_AUDIENCE); + + let state = TestState::new(pool, TestAuthz::new()).await; + let mut conn = state.get_conn().await.expect("Failed to fetch connection"); + let minigroup = factory::Minigroup::new( + random_string(), + USR_AUDIENCE.to_string(), + (Bound::Unbounded, Bound::Unbounded).into(), + Uuid::new_v4(), + Uuid::new_v4(), + ) + .insert(&mut conn) + .await; + + // Recording of banned agent + let _rec1 = + factory::Recording::new(minigroup.id(), Uuid::new_v4(), agent1.agent_id().to_owned()) + .insert(&mut conn) + .await; + + crate::db::ban_history::InsertQuery::new(minigroup.id(), agent1.account_id(), 0) + .execute(&mut conn) + .await + .expect("failed to insert ban history entry"); + + // Recording of good agent + let rec2 = + factory::Recording::new(minigroup.id(), Uuid::new_v4(), agent2.agent_id().to_owned()) + .insert(&mut conn) + .await; + + let recs = RecordingListQuery::new(minigroup.id()) + .execute(&mut conn) + .await + .expect("failed to run list query"); + + assert_eq!(recs.len(), 1); + assert_eq!(recs[0], rec2); + } + pub struct RecordingInsertQuery { class_id: Uuid, rtc_id: Uuid, diff --git a/src/test_helpers/db.rs b/src/test_helpers/db.rs index 94aad054..98384e1a 100644 --- a/src/test_helpers/db.rs +++ b/src/test_helpers/db.rs @@ -1,41 +1,13 @@ -use std::env::var; +use sqlx::pool::PoolConnection; +use sqlx::postgres::{PgPool, Postgres}; -use sqlx::postgres::{PgPool, PgPoolOptions, Postgres}; -use sqlx::{pool::PoolConnection, Executor}; -use tokio::sync::OnceCell; - -static DB_TRUNCATE: OnceCell = OnceCell::const_new(); #[derive(Clone)] pub struct TestDb { pool: PgPool, } impl TestDb { - pub async fn new() -> Self { - #[cfg(feature = "dotenv")] - dotenv::dotenv().ok(); - - let url = var("DATABASE_URL").expect("DATABASE_URL must be specified"); - - let pool = PgPoolOptions::new() - .min_connections(1) - .max_connections(1) - .connect(&url) - .await - .expect("Failed to connect to the DB"); - - // todo: we should actually run every test in transaction, but thats not possible for now, maybe in sqlx 0.6 - DB_TRUNCATE - .get_or_init(|| async { - let mut conn = pool.acquire().await.expect("Failed to get DB connection"); - - conn.execute("TRUNCATE class CASCADE;") - .await - .expect("Failed to truncate class table"); - - true - }) - .await; + pub fn new(pool: PgPool) -> Self { Self { pool } } diff --git a/src/test_helpers/state.rs b/src/test_helpers/state.rs index 603ca3f4..45e2743a 100644 --- a/src/test_helpers/state.rs +++ b/src/test_helpers/state.rs @@ -1,5 +1,6 @@ use parking_lot::Mutex; use std::sync::Arc; +use svc_nats_client::test_helpers::TestNatsClient; use anyhow::Result; use async_trait::async_trait; @@ -12,10 +13,7 @@ use svc_agent::{ AgentId, }; use svc_authz::ClientMap as Authz; -use svc_nats_client::{ - AckPolicy, DeliverPolicy, Event, Message, MessageStream, Messages, NatsClient, PublishError, - Subject, SubscribeError, TermMessageError, -}; +use svc_nats_client::NatsClient; use url::Url; use vec1::{vec1, Vec1}; @@ -45,7 +43,7 @@ pub struct TestState { tq_client: Arc, authz: Authz, turn_host_selector: TurnHostSelector, - nats_client: Option>, + nats_client: Arc, } fn build_config() -> Config { @@ -102,34 +100,8 @@ fn build_config() -> Config { serde_json::from_value::(config).expect("Failed to parse test config") } -struct TestNatsClient; - -#[async_trait] -impl NatsClient for TestNatsClient { - async fn publish(&self, _event: &Event) -> Result<(), PublishError> { - Ok(()) - } - - async fn subscribe_durable(&self) -> Result { - unimplemented!() - } - - async fn subscribe_ephemeral( - &self, - _subject: Subject, - _deliver_policy: DeliverPolicy, - _ack_policy: AckPolicy, - ) -> Result { - unimplemented!() - } - - async fn terminate(&self, _message: &Message) -> Result<(), TermMessageError> { - unimplemented!() - } -} - impl TestState { - pub async fn new(authz: TestAuthz) -> Self { + pub async fn new(db: sqlx::PgPool, authz: TestAuthz) -> Self { let config = build_config(); let agent = TestAgent::new(&config.agent_label, config.id.label(), config.id.audience()); @@ -137,7 +109,7 @@ impl TestState { let address = agent.address().to_owned(); Self { - db_pool: TestDb::new().await, + db_pool: TestDb::new(db), turn_host_selector: TurnHostSelector::new(&config.turn_hosts), config, agent, @@ -146,7 +118,7 @@ impl TestState { event_client: Arc::new(MockEventClient::new()), tq_client: Arc::new(MockTqClient::new()), authz: authz.into(), - nats_client: Some(Arc::new(TestNatsClient {}) as Arc), + nats_client: Arc::new(TestNatsClient::new()), } } @@ -167,7 +139,7 @@ impl TestState { tq_client: Arc::new(MockTqClient::new()), authz: authz.into(), turn_host_selector: TurnHostSelector::new(&vec1!["turn.example.org".into()]), - nats_client: Some(Arc::new(TestNatsClient {}) as Arc), + nats_client: Arc::new(TestNatsClient::new()), } } @@ -188,6 +160,10 @@ impl TestState { let hosts = Vec1::try_from_vec(hosts).unwrap(); self.turn_host_selector = TurnHostSelector::new(&hosts); } + + pub fn inspect_nats_client(&self) -> &TestNatsClient { + &self.nats_client + } } impl TestState { @@ -263,8 +239,8 @@ impl AppContext for TestState { &self.turn_host_selector } - fn nats_client(&self) -> Option<&dyn NatsClient> { - self.nats_client.as_deref() + fn nats_client(&self) -> Option> { + Some(self.nats_client.clone()) } }