From e25743172f3e479fcc3a9c14c12ab1842866b7db Mon Sep 17 00:00:00 2001 From: rnarubin Date: Thu, 7 Dec 2023 10:08:30 -0800 Subject: [PATCH] Refactor PubSub acks to independent requests (#37) * Refactor PubSub acks to independent requests Previously pubsub acknowledgements were sent over the client stream of the streaming pull gRPC. This provided limited feedback for the ack/nack/modify callers, who had no (good) way to know their request was actually submitted. Furthermore there's evidence that some bug existed around connection resets and long ack times, although a definitive cause was not identified. This change uses explicit `acknowledge` and `modify_ack_deadline` rpc calls to submit acks instead of the client stream. These enable much clearer feedback for ack callers, as well as better backpressure regulation in general. This implementation was loosely inspired by the approach in the golang pubsub library[1]. [1]: https://github.com/googleapis/google-cloud-go/blob/94d040898cc9e85fdac76560765b01cfd019d0b4/pubsub/iterator.go#L422-L446 --- .github/workflows/ci.yml | 1 + Cargo.lock | 167 +- Cargo.toml | 9 +- generators/src/grpc.rs | 4 +- src/bigtable/mod.rs | 7 +- src/generated/google.bigtable.admin.v2.rs | 1629 ++++++++++++++ src/generated/google.bigtable.v2.rs | 675 ++++++ src/generated/google.iam.v1.rs | 329 +++ src/generated/google.longrunning.rs | 437 ++++ src/generated/google.pubsub.v1.rs | 2380 +++++++++++++++++++++ src/pubsub/mod.rs | 29 +- src/pubsub/streaming_subscription.rs | 1425 +++++++++--- tests/pubsub_client.rs | 143 +- 13 files changed, 6782 insertions(+), 453 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9dca806..0e8e6e3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -119,5 +119,6 @@ jobs: with: toolchain: nightly-2023-04-18 command: doc + args: --all-features env: RUSTDOCFLAGS: "--cfg docsrs" diff --git a/Cargo.lock b/Cargo.lock index 2c38500..42d3178 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,21 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "aho-corasick" version = "0.7.20" @@ -44,17 +59,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "async-channel" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf46fee83e5ccffc220104713af3292ff9bc7c64c7de289f66dae8e38d826833" -dependencies = [ - "concurrent-queue", - "event-listener", - "futures-core", -] - [[package]] name = "async-stream" version = "0.3.5" @@ -150,6 +154,21 @@ dependencies = [ "tower-service", ] +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "base64" version = "0.13.1" @@ -236,15 +255,6 @@ dependencies = [ "unicode-width", ] -[[package]] -name = "concurrent-queue" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62ec6771ecfa0762d24683ee5a32ad78487a3d3afdc0fb8cae19d2c5deb50b7c" -dependencies = [ - "crossbeam-utils", -] - [[package]] name = "convert_case" version = "0.4.0" @@ -267,15 +277,6 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" -[[package]] -name = "crossbeam-utils" -version = "0.8.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" -dependencies = [ - "cfg-if", -] - [[package]] name = "cxx" version = "1.0.94" @@ -349,12 +350,6 @@ dependencies = [ "regex", ] -[[package]] -name = "event-listener" -version = "2.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" - [[package]] name = "fnv" version = "1.0.7" @@ -476,6 +471,12 @@ dependencies = [ "wasi 0.11.0+wasi-snapshot-preview1", ] +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + [[package]] name = "h2" version = "0.3.18" @@ -595,7 +596,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2", + "socket2 0.4.9", "tokio", "tower-service", "tracing", @@ -755,16 +756,24 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + [[package]] name = "mio" -version = "0.8.6" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" dependencies = [ "libc", - "log", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] @@ -815,6 +824,15 @@ dependencies = [ "libc", ] +[[package]] +name = "object" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.17.1" @@ -867,9 +885,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.9" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "pin-utils" @@ -909,9 +927,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.56" +version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" dependencies = [ "unicode-ident", ] @@ -1110,6 +1128,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + [[package]] name = "rustc_version" version = "0.4.0" @@ -1320,6 +1344,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "socket2" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + [[package]] name = "spin" version = "0.5.2" @@ -1522,20 +1556,20 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.27.0" +version = "1.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0de47a4eecbe11f498978a9b29d792f0d2692d1dd003650c24c76510e3bc001" +checksum = "d0c014766411e834f7af5b8f4cf46257aab4036ca95e9d2c144a10f59ad6f5b9" dependencies = [ - "autocfg", + "backtrace", "bytes", "libc", "mio", "num_cpus", "pin-project-lite", "signal-hook-registry", - "socket2", + "socket2 0.5.5", "tokio-macros", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] @@ -1550,9 +1584,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.0.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61a573bdc87985e9d6ddeed1b3d864e8a302c847e40d647746df2f1de209d1ce" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", @@ -1952,7 +1986,7 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" dependencies = [ - "windows-targets 0.48.0", + "windows-targets", ] [[package]] @@ -1970,37 +2004,13 @@ dependencies = [ "windows_x86_64_msvc 0.42.2", ] -[[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", + "windows-targets", ] [[package]] @@ -2104,10 +2114,9 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "ya-gcp" -version = "0.11.0" +version = "0.11.1" dependencies = [ "approx", - "async-channel", "async-stream", "cfg-if", "derive_more", diff --git a/Cargo.toml b/Cargo.toml index 8e30f0c..d124b4e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ya-gcp" -version = "0.11.0" +version = "0.11.1" authors = ["Renar Narubin "] edition = "2021" description = "APIs for using Google Cloud Platform services" @@ -39,7 +39,7 @@ webpki-roots = ["dep:webpki-roots", "tonic?/tls-webpki-roots"] grpc = ["tonic", "prost", "prost-types", "tower", "derive_more"] bigtable = ["async-stream", "grpc", "prost", "tower"] -pubsub = ["grpc", "uuid", "async-stream", "pin-project", "async-channel", "tokio/time"] +pubsub = ["grpc", "uuid", "async-stream", "pin-project", "tokio/sync"] storage = ["tame-gcs", "tower"] # whether to include service emulator implementations. useful for testing @@ -57,12 +57,11 @@ rand = "0.8" rustls = "0.21.8" serde = { version = "1", features = ["derive"] } thiserror = "1" -tokio = { version = "1", features = ["time"] } +tokio = { version = "1.34", features = ["time"] } tracing = "0.1.37" yup-oauth2 = "8.3.0" async-stream = { version = "0.3", optional = true } -async-channel = { version = "1", optional = true } derive_more = { version = "0.99", optional = true } pin-project = { version = "1.0.11", optional = true } prost = { version = "0.12.3", optional = true } @@ -82,7 +81,7 @@ quickcheck = "1" quickcheck_macros = "1" serde_json = "1" structopt = "0.3" # for examples -tokio = { version = "1.4.0", features = ["rt-multi-thread", "time", "test-util"] } +tokio = { version = "1.34.0", features = ["rt-multi-thread", "time", "test-util"] } tracing-subscriber = { version = "0.3", features = ["env-filter"] } tracing-tree = "0.2" diff --git a/generators/src/grpc.rs b/generators/src/grpc.rs index a6bc095..55d8648 100644 --- a/generators/src/grpc.rs +++ b/generators/src/grpc.rs @@ -76,7 +76,9 @@ fn main() -> Result<(), Error> { tonic_build::configure() .build_client(true) - .build_server(false) + .build_server(true) // build servers for tests + .server_mod_attribute(".", "#[cfg(test)]") + .generate_default_stubs(true) .out_dir(&args.output_dir) .compile_with_config( prost_config, diff --git a/src/bigtable/mod.rs b/src/bigtable/mod.rs index cdf1540..97fdba9 100644 --- a/src/bigtable/mod.rs +++ b/src/bigtable/mod.rs @@ -30,7 +30,12 @@ pub use mutation::{MutateRowRequest, MutateRowsError, MutateRowsRequest}; #[cfg_attr(docsrs, doc(cfg(feature = "emulators")))] pub mod emulator; -#[allow(rustdoc::broken_intra_doc_links, rustdoc::bare_urls, missing_docs)] +#[allow( + rustdoc::broken_intra_doc_links, + rustdoc::bare_urls, + missing_docs, + unreachable_pub +)] pub mod api { pub mod rpc { include!("../generated/google.rpc.rs"); diff --git a/src/generated/google.bigtable.admin.v2.rs b/src/generated/google.bigtable.admin.v2.rs index 6c0a186..08cb0df 100644 --- a/src/generated/google.bigtable.admin.v2.rs +++ b/src/generated/google.bigtable.admin.v2.rs @@ -2469,3 +2469,1632 @@ pub mod bigtable_table_admin_client { } } } +/// Generated server implementations. +#[cfg(test)] +pub mod bigtable_table_admin_server { + #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] + use tonic::codegen::*; + /// Generated trait containing gRPC methods that should be implemented for use with BigtableTableAdminServer. + #[async_trait] + pub trait BigtableTableAdmin: Send + Sync + 'static { + /// Creates a new table in the specified instance. + /// The table can be created with a full set of initial column families, + /// specified in the request. + async fn create_table( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status> { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Creates a new table from the specified snapshot. The target table must + /// not exist. The snapshot and the table must be in the same instance. + /// + /// Note: This is a private alpha release of Cloud Bigtable snapshots. This + /// feature is not currently available to most Cloud Bigtable customers. This + /// feature might be changed in backward-incompatible ways and is not + /// recommended for production use. It is not subject to any SLA or deprecation + /// policy. + async fn create_table_from_snapshot( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Lists all tables served from a specified instance. + async fn list_tables( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Gets metadata information about the specified table. + async fn get_table( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status> { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Updates a specified table. + async fn update_table( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Permanently deletes a specified table and all of its data. + async fn delete_table( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status> { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Restores a specified table which was accidentally deleted. + async fn undelete_table( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Performs a series of column family modifications on the specified table. + /// Either all or none of the modifications will occur before this method + /// returns, but data requests received prior to that point may see a table + /// where only some modifications have taken effect. + async fn modify_column_families( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status> { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Permanently drop/delete a row range from a specified table. The request can + /// specify whether to delete all rows in a table, or only those that match a + /// particular prefix. + async fn drop_row_range( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status> { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Generates a consistency token for a Table, which can be used in + /// CheckConsistency to check whether mutations to the table that finished + /// before this call started have been replicated. The tokens will be available + /// for 90 days. + async fn generate_consistency_token( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Checks replication consistency based on a consistency token, that is, if + /// replication has caught up based on the conditions specified in the token + /// and the check request. + async fn check_consistency( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Creates a new snapshot in the specified cluster from the specified + /// source table. The cluster and the table must be in the same instance. + /// + /// Note: This is a private alpha release of Cloud Bigtable snapshots. This + /// feature is not currently available to most Cloud Bigtable customers. This + /// feature might be changed in backward-incompatible ways and is not + /// recommended for production use. It is not subject to any SLA or deprecation + /// policy. + async fn snapshot_table( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Gets metadata information about the specified snapshot. + /// + /// Note: This is a private alpha release of Cloud Bigtable snapshots. This + /// feature is not currently available to most Cloud Bigtable customers. This + /// feature might be changed in backward-incompatible ways and is not + /// recommended for production use. It is not subject to any SLA or deprecation + /// policy. + async fn get_snapshot( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status> { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Lists all snapshots associated with the specified cluster. + /// + /// Note: This is a private alpha release of Cloud Bigtable snapshots. This + /// feature is not currently available to most Cloud Bigtable customers. This + /// feature might be changed in backward-incompatible ways and is not + /// recommended for production use. It is not subject to any SLA or deprecation + /// policy. + async fn list_snapshots( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Permanently deletes the specified snapshot. + /// + /// Note: This is a private alpha release of Cloud Bigtable snapshots. This + /// feature is not currently available to most Cloud Bigtable customers. This + /// feature might be changed in backward-incompatible ways and is not + /// recommended for production use. It is not subject to any SLA or deprecation + /// policy. + async fn delete_snapshot( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status> { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Starts creating a new Cloud Bigtable Backup. The returned backup + /// [long-running operation][google.longrunning.Operation] can be used to + /// track creation of the backup. The + /// [metadata][google.longrunning.Operation.metadata] field type is + /// [CreateBackupMetadata][google.bigtable.admin.v2.CreateBackupMetadata]. The + /// [response][google.longrunning.Operation.response] field type is + /// [Backup][google.bigtable.admin.v2.Backup], if successful. Cancelling the + /// returned operation will stop the creation and delete the backup. + async fn create_backup( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Gets metadata on a pending or completed Cloud Bigtable Backup. + async fn get_backup( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status> { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Updates a pending or completed Cloud Bigtable Backup. + async fn update_backup( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status> { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Deletes a pending or completed Cloud Bigtable backup. + async fn delete_backup( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status> { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Lists Cloud Bigtable backups. Returns both completed and pending + /// backups. + async fn list_backups( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Create a new table by restoring from a completed backup. The + /// returned table [long-running operation][google.longrunning.Operation] can + /// be used to track the progress of the operation, and to cancel it. The + /// [metadata][google.longrunning.Operation.metadata] field type is + /// [RestoreTableMetadata][google.bigtable.admin.RestoreTableMetadata]. The + /// [response][google.longrunning.Operation.response] type is + /// [Table][google.bigtable.admin.v2.Table], if successful. + async fn restore_table( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Copy a Cloud Bigtable backup to a new backup in the destination cluster + /// located in the destination instance and project. + async fn copy_backup( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Gets the access control policy for a Table or Backup resource. + /// Returns an empty policy if the resource exists but does not have a policy + /// set. + async fn get_iam_policy( + &self, + request: tonic::Request< + super::super::super::super::iam::v1::GetIamPolicyRequest, + >, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Sets the access control policy on a Table or Backup resource. + /// Replaces any existing policy. + async fn set_iam_policy( + &self, + request: tonic::Request< + super::super::super::super::iam::v1::SetIamPolicyRequest, + >, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Returns permissions that the caller has on the specified Table or Backup + /// resource. + async fn test_iam_permissions( + &self, + request: tonic::Request< + super::super::super::super::iam::v1::TestIamPermissionsRequest, + >, + ) -> std::result::Result< + tonic::Response< + super::super::super::super::iam::v1::TestIamPermissionsResponse, + >, + tonic::Status, + > { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + } + /// Service for creating, configuring, and deleting Cloud Bigtable tables. + /// + /// + /// Provides access to the table schemas only, not the data stored within + /// the tables. + #[derive(Debug)] + pub struct BigtableTableAdminServer { + inner: _Inner, + accept_compression_encodings: EnabledCompressionEncodings, + send_compression_encodings: EnabledCompressionEncodings, + max_decoding_message_size: Option, + max_encoding_message_size: Option, + } + struct _Inner(Arc); + impl BigtableTableAdminServer { + pub fn new(inner: T) -> Self { + Self::from_arc(Arc::new(inner)) + } + pub fn from_arc(inner: Arc) -> Self { + let inner = _Inner(inner); + Self { + inner, + accept_compression_encodings: Default::default(), + send_compression_encodings: Default::default(), + max_decoding_message_size: None, + max_encoding_message_size: None, + } + } + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> InterceptedService + where + F: tonic::service::Interceptor, + { + InterceptedService::new(Self::new(inner), interceptor) + } + /// Enable decompressing requests with the given encoding. + #[must_use] + pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.accept_compression_encodings.enable(encoding); + self + } + /// Compress responses with the given encoding, if the client supports it. + #[must_use] + pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.send_compression_encodings.enable(encoding); + self + } + /// Limits the maximum size of a decoded message. + /// + /// Default: `4MB` + #[must_use] + pub fn max_decoding_message_size(mut self, limit: usize) -> Self { + self.max_decoding_message_size = Some(limit); + self + } + /// Limits the maximum size of an encoded message. + /// + /// Default: `usize::MAX` + #[must_use] + pub fn max_encoding_message_size(mut self, limit: usize) -> Self { + self.max_encoding_message_size = Some(limit); + self + } + } + impl tonic::codegen::Service> for BigtableTableAdminServer + where + T: BigtableTableAdmin, + B: Body + Send + 'static, + B::Error: Into + Send + 'static, + { + type Response = http::Response; + type Error = std::convert::Infallible; + type Future = BoxFuture; + fn poll_ready( + &mut self, + _cx: &mut Context<'_>, + ) -> Poll> { + Poll::Ready(Ok(())) + } + fn call(&mut self, req: http::Request) -> Self::Future { + let inner = self.inner.clone(); + match req.uri().path() { + "/google.bigtable.admin.v2.BigtableTableAdmin/CreateTable" => { + #[allow(non_camel_case_types)] + struct CreateTableSvc(pub Arc); + impl< + T: BigtableTableAdmin, + > tonic::server::UnaryService + for CreateTableSvc { + type Response = super::Table; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::create_table(&inner, request) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = CreateTableSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.bigtable.admin.v2.BigtableTableAdmin/CreateTableFromSnapshot" => { + #[allow(non_camel_case_types)] + struct CreateTableFromSnapshotSvc(pub Arc); + impl< + T: BigtableTableAdmin, + > tonic::server::UnaryService + for CreateTableFromSnapshotSvc { + type Response = super::super::super::super::longrunning::Operation; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request< + super::CreateTableFromSnapshotRequest, + >, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::create_table_from_snapshot( + &inner, + request, + ) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = CreateTableFromSnapshotSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.bigtable.admin.v2.BigtableTableAdmin/ListTables" => { + #[allow(non_camel_case_types)] + struct ListTablesSvc(pub Arc); + impl< + T: BigtableTableAdmin, + > tonic::server::UnaryService + for ListTablesSvc { + type Response = super::ListTablesResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::list_tables(&inner, request) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = ListTablesSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.bigtable.admin.v2.BigtableTableAdmin/GetTable" => { + #[allow(non_camel_case_types)] + struct GetTableSvc(pub Arc); + impl< + T: BigtableTableAdmin, + > tonic::server::UnaryService + for GetTableSvc { + type Response = super::Table; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::get_table(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = GetTableSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.bigtable.admin.v2.BigtableTableAdmin/UpdateTable" => { + #[allow(non_camel_case_types)] + struct UpdateTableSvc(pub Arc); + impl< + T: BigtableTableAdmin, + > tonic::server::UnaryService + for UpdateTableSvc { + type Response = super::super::super::super::longrunning::Operation; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::update_table(&inner, request) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = UpdateTableSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.bigtable.admin.v2.BigtableTableAdmin/DeleteTable" => { + #[allow(non_camel_case_types)] + struct DeleteTableSvc(pub Arc); + impl< + T: BigtableTableAdmin, + > tonic::server::UnaryService + for DeleteTableSvc { + type Response = (); + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::delete_table(&inner, request) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = DeleteTableSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.bigtable.admin.v2.BigtableTableAdmin/UndeleteTable" => { + #[allow(non_camel_case_types)] + struct UndeleteTableSvc(pub Arc); + impl< + T: BigtableTableAdmin, + > tonic::server::UnaryService + for UndeleteTableSvc { + type Response = super::super::super::super::longrunning::Operation; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::undelete_table(&inner, request) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = UndeleteTableSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.bigtable.admin.v2.BigtableTableAdmin/ModifyColumnFamilies" => { + #[allow(non_camel_case_types)] + struct ModifyColumnFamiliesSvc(pub Arc); + impl< + T: BigtableTableAdmin, + > tonic::server::UnaryService + for ModifyColumnFamiliesSvc { + type Response = super::Table; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::modify_column_families( + &inner, + request, + ) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = ModifyColumnFamiliesSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.bigtable.admin.v2.BigtableTableAdmin/DropRowRange" => { + #[allow(non_camel_case_types)] + struct DropRowRangeSvc(pub Arc); + impl< + T: BigtableTableAdmin, + > tonic::server::UnaryService + for DropRowRangeSvc { + type Response = (); + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::drop_row_range(&inner, request) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = DropRowRangeSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.bigtable.admin.v2.BigtableTableAdmin/GenerateConsistencyToken" => { + #[allow(non_camel_case_types)] + struct GenerateConsistencyTokenSvc( + pub Arc, + ); + impl< + T: BigtableTableAdmin, + > tonic::server::UnaryService + for GenerateConsistencyTokenSvc { + type Response = super::GenerateConsistencyTokenResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request< + super::GenerateConsistencyTokenRequest, + >, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::generate_consistency_token( + &inner, + request, + ) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = GenerateConsistencyTokenSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.bigtable.admin.v2.BigtableTableAdmin/CheckConsistency" => { + #[allow(non_camel_case_types)] + struct CheckConsistencySvc(pub Arc); + impl< + T: BigtableTableAdmin, + > tonic::server::UnaryService + for CheckConsistencySvc { + type Response = super::CheckConsistencyResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::check_consistency( + &inner, + request, + ) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = CheckConsistencySvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.bigtable.admin.v2.BigtableTableAdmin/SnapshotTable" => { + #[allow(non_camel_case_types)] + struct SnapshotTableSvc(pub Arc); + impl< + T: BigtableTableAdmin, + > tonic::server::UnaryService + for SnapshotTableSvc { + type Response = super::super::super::super::longrunning::Operation; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::snapshot_table(&inner, request) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = SnapshotTableSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.bigtable.admin.v2.BigtableTableAdmin/GetSnapshot" => { + #[allow(non_camel_case_types)] + struct GetSnapshotSvc(pub Arc); + impl< + T: BigtableTableAdmin, + > tonic::server::UnaryService + for GetSnapshotSvc { + type Response = super::Snapshot; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::get_snapshot(&inner, request) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = GetSnapshotSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.bigtable.admin.v2.BigtableTableAdmin/ListSnapshots" => { + #[allow(non_camel_case_types)] + struct ListSnapshotsSvc(pub Arc); + impl< + T: BigtableTableAdmin, + > tonic::server::UnaryService + for ListSnapshotsSvc { + type Response = super::ListSnapshotsResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::list_snapshots(&inner, request) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = ListSnapshotsSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.bigtable.admin.v2.BigtableTableAdmin/DeleteSnapshot" => { + #[allow(non_camel_case_types)] + struct DeleteSnapshotSvc(pub Arc); + impl< + T: BigtableTableAdmin, + > tonic::server::UnaryService + for DeleteSnapshotSvc { + type Response = (); + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::delete_snapshot(&inner, request) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = DeleteSnapshotSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.bigtable.admin.v2.BigtableTableAdmin/CreateBackup" => { + #[allow(non_camel_case_types)] + struct CreateBackupSvc(pub Arc); + impl< + T: BigtableTableAdmin, + > tonic::server::UnaryService + for CreateBackupSvc { + type Response = super::super::super::super::longrunning::Operation; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::create_backup(&inner, request) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = CreateBackupSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.bigtable.admin.v2.BigtableTableAdmin/GetBackup" => { + #[allow(non_camel_case_types)] + struct GetBackupSvc(pub Arc); + impl< + T: BigtableTableAdmin, + > tonic::server::UnaryService + for GetBackupSvc { + type Response = super::Backup; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::get_backup(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = GetBackupSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.bigtable.admin.v2.BigtableTableAdmin/UpdateBackup" => { + #[allow(non_camel_case_types)] + struct UpdateBackupSvc(pub Arc); + impl< + T: BigtableTableAdmin, + > tonic::server::UnaryService + for UpdateBackupSvc { + type Response = super::Backup; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::update_backup(&inner, request) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = UpdateBackupSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.bigtable.admin.v2.BigtableTableAdmin/DeleteBackup" => { + #[allow(non_camel_case_types)] + struct DeleteBackupSvc(pub Arc); + impl< + T: BigtableTableAdmin, + > tonic::server::UnaryService + for DeleteBackupSvc { + type Response = (); + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::delete_backup(&inner, request) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = DeleteBackupSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.bigtable.admin.v2.BigtableTableAdmin/ListBackups" => { + #[allow(non_camel_case_types)] + struct ListBackupsSvc(pub Arc); + impl< + T: BigtableTableAdmin, + > tonic::server::UnaryService + for ListBackupsSvc { + type Response = super::ListBackupsResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::list_backups(&inner, request) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = ListBackupsSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.bigtable.admin.v2.BigtableTableAdmin/RestoreTable" => { + #[allow(non_camel_case_types)] + struct RestoreTableSvc(pub Arc); + impl< + T: BigtableTableAdmin, + > tonic::server::UnaryService + for RestoreTableSvc { + type Response = super::super::super::super::longrunning::Operation; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::restore_table(&inner, request) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = RestoreTableSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.bigtable.admin.v2.BigtableTableAdmin/CopyBackup" => { + #[allow(non_camel_case_types)] + struct CopyBackupSvc(pub Arc); + impl< + T: BigtableTableAdmin, + > tonic::server::UnaryService + for CopyBackupSvc { + type Response = super::super::super::super::longrunning::Operation; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::copy_backup(&inner, request) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = CopyBackupSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.bigtable.admin.v2.BigtableTableAdmin/GetIamPolicy" => { + #[allow(non_camel_case_types)] + struct GetIamPolicySvc(pub Arc); + impl< + T: BigtableTableAdmin, + > tonic::server::UnaryService< + super::super::super::super::iam::v1::GetIamPolicyRequest, + > for GetIamPolicySvc { + type Response = super::super::super::super::iam::v1::Policy; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request< + super::super::super::super::iam::v1::GetIamPolicyRequest, + >, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::get_iam_policy(&inner, request) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = GetIamPolicySvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.bigtable.admin.v2.BigtableTableAdmin/SetIamPolicy" => { + #[allow(non_camel_case_types)] + struct SetIamPolicySvc(pub Arc); + impl< + T: BigtableTableAdmin, + > tonic::server::UnaryService< + super::super::super::super::iam::v1::SetIamPolicyRequest, + > for SetIamPolicySvc { + type Response = super::super::super::super::iam::v1::Policy; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request< + super::super::super::super::iam::v1::SetIamPolicyRequest, + >, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::set_iam_policy(&inner, request) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = SetIamPolicySvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.bigtable.admin.v2.BigtableTableAdmin/TestIamPermissions" => { + #[allow(non_camel_case_types)] + struct TestIamPermissionsSvc(pub Arc); + impl< + T: BigtableTableAdmin, + > tonic::server::UnaryService< + super::super::super::super::iam::v1::TestIamPermissionsRequest, + > for TestIamPermissionsSvc { + type Response = super::super::super::super::iam::v1::TestIamPermissionsResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request< + super::super::super::super::iam::v1::TestIamPermissionsRequest, + >, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::test_iam_permissions( + &inner, + request, + ) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = TestIamPermissionsSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + _ => { + Box::pin(async move { + Ok( + http::Response::builder() + .status(200) + .header("grpc-status", "12") + .header("content-type", "application/grpc") + .body(empty_body()) + .unwrap(), + ) + }) + } + } + } + } + impl Clone for BigtableTableAdminServer { + fn clone(&self) -> Self { + let inner = self.inner.clone(); + Self { + inner, + accept_compression_encodings: self.accept_compression_encodings, + send_compression_encodings: self.send_compression_encodings, + max_decoding_message_size: self.max_decoding_message_size, + max_encoding_message_size: self.max_encoding_message_size, + } + } + } + impl Clone for _Inner { + fn clone(&self) -> Self { + Self(Arc::clone(&self.0)) + } + } + impl std::fmt::Debug for _Inner { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self.0) + } + } + impl tonic::server::NamedService + for BigtableTableAdminServer { + const NAME: &'static str = "google.bigtable.admin.v2.BigtableTableAdmin"; + } +} diff --git a/src/generated/google.bigtable.v2.rs b/src/generated/google.bigtable.v2.rs index c228efc..95820d9 100644 --- a/src/generated/google.bigtable.v2.rs +++ b/src/generated/google.bigtable.v2.rs @@ -1880,3 +1880,678 @@ pub mod bigtable_client { } } } +/// Generated server implementations. +#[cfg(test)] +pub mod bigtable_server { + #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] + use tonic::codegen::*; + /// Generated trait containing gRPC methods that should be implemented for use with BigtableServer. + #[async_trait] + pub trait Bigtable: Send + Sync + 'static { + /// Streams back the contents of all requested rows in key order, optionally + /// applying the same Reader filter to each. Depending on their size, + /// rows and cells may be broken up across multiple responses, but + /// atomicity of each row will still be preserved. See the + /// ReadRowsResponse documentation for details. + async fn read_rows( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response>, + tonic::Status, + > { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Returns a sample of row keys in the table. The returned row keys will + /// delimit contiguous sections of the table of approximately equal size, + /// which can be used to break up the data for distributed tasks like + /// mapreduces. + async fn sample_row_keys( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response>, + tonic::Status, + > { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Mutates a row atomically. Cells already present in the row are left + /// unchanged unless explicitly changed by `mutation`. + async fn mutate_row( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Mutates multiple rows in a batch. Each individual row is mutated + /// atomically as in MutateRow, but the entire batch is not executed + /// atomically. + async fn mutate_rows( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response>, + tonic::Status, + > { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Mutates a row atomically based on the output of a predicate Reader filter. + async fn check_and_mutate_row( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Warm up associated instance metadata for this connection. + /// This call is not required but may be useful for connection keep-alive. + async fn ping_and_warm( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Modifies a row atomically on the server. The method reads the latest + /// existing timestamp and value from the specified columns and writes a new + /// entry based on pre-defined read/modify/write rules. The new value for the + /// timestamp is the greater of the existing timestamp or the current server + /// time. The method returns the new contents of all modified cells. + async fn read_modify_write_row( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// NOTE: This API is intended to be used by Apache Beam BigtableIO. + /// Returns the current list of partitions that make up the table's + /// change stream. The union of partitions will cover the entire keyspace. + /// Partitions can be read with `ReadChangeStream`. + async fn generate_initial_change_stream_partitions( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response< + BoxStream, + >, + tonic::Status, + > { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// NOTE: This API is intended to be used by Apache Beam BigtableIO. + /// Reads changes from a table's change stream. Changes will + /// reflect both user-initiated mutations and mutations that are caused by + /// garbage collection. + async fn read_change_stream( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response>, + tonic::Status, + > { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + } + /// Service for reading from and writing to existing Bigtable tables. + #[derive(Debug)] + pub struct BigtableServer { + inner: _Inner, + accept_compression_encodings: EnabledCompressionEncodings, + send_compression_encodings: EnabledCompressionEncodings, + max_decoding_message_size: Option, + max_encoding_message_size: Option, + } + struct _Inner(Arc); + impl BigtableServer { + pub fn new(inner: T) -> Self { + Self::from_arc(Arc::new(inner)) + } + pub fn from_arc(inner: Arc) -> Self { + let inner = _Inner(inner); + Self { + inner, + accept_compression_encodings: Default::default(), + send_compression_encodings: Default::default(), + max_decoding_message_size: None, + max_encoding_message_size: None, + } + } + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> InterceptedService + where + F: tonic::service::Interceptor, + { + InterceptedService::new(Self::new(inner), interceptor) + } + /// Enable decompressing requests with the given encoding. + #[must_use] + pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.accept_compression_encodings.enable(encoding); + self + } + /// Compress responses with the given encoding, if the client supports it. + #[must_use] + pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.send_compression_encodings.enable(encoding); + self + } + /// Limits the maximum size of a decoded message. + /// + /// Default: `4MB` + #[must_use] + pub fn max_decoding_message_size(mut self, limit: usize) -> Self { + self.max_decoding_message_size = Some(limit); + self + } + /// Limits the maximum size of an encoded message. + /// + /// Default: `usize::MAX` + #[must_use] + pub fn max_encoding_message_size(mut self, limit: usize) -> Self { + self.max_encoding_message_size = Some(limit); + self + } + } + impl tonic::codegen::Service> for BigtableServer + where + T: Bigtable, + B: Body + Send + 'static, + B::Error: Into + Send + 'static, + { + type Response = http::Response; + type Error = std::convert::Infallible; + type Future = BoxFuture; + fn poll_ready( + &mut self, + _cx: &mut Context<'_>, + ) -> Poll> { + Poll::Ready(Ok(())) + } + fn call(&mut self, req: http::Request) -> Self::Future { + let inner = self.inner.clone(); + match req.uri().path() { + "/google.bigtable.v2.Bigtable/ReadRows" => { + #[allow(non_camel_case_types)] + struct ReadRowsSvc(pub Arc); + impl< + T: Bigtable, + > tonic::server::ServerStreamingService + for ReadRowsSvc { + type Response = super::ReadRowsResponse; + type ResponseStream = BoxStream; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::read_rows(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = ReadRowsSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.server_streaming(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.bigtable.v2.Bigtable/SampleRowKeys" => { + #[allow(non_camel_case_types)] + struct SampleRowKeysSvc(pub Arc); + impl< + T: Bigtable, + > tonic::server::ServerStreamingService + for SampleRowKeysSvc { + type Response = super::SampleRowKeysResponse; + type ResponseStream = BoxStream; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::sample_row_keys(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = SampleRowKeysSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.server_streaming(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.bigtable.v2.Bigtable/MutateRow" => { + #[allow(non_camel_case_types)] + struct MutateRowSvc(pub Arc); + impl< + T: Bigtable, + > tonic::server::UnaryService + for MutateRowSvc { + type Response = super::MutateRowResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::mutate_row(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = MutateRowSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.bigtable.v2.Bigtable/MutateRows" => { + #[allow(non_camel_case_types)] + struct MutateRowsSvc(pub Arc); + impl< + T: Bigtable, + > tonic::server::ServerStreamingService + for MutateRowsSvc { + type Response = super::MutateRowsResponse; + type ResponseStream = BoxStream; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::mutate_rows(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = MutateRowsSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.server_streaming(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.bigtable.v2.Bigtable/CheckAndMutateRow" => { + #[allow(non_camel_case_types)] + struct CheckAndMutateRowSvc(pub Arc); + impl< + T: Bigtable, + > tonic::server::UnaryService + for CheckAndMutateRowSvc { + type Response = super::CheckAndMutateRowResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::check_and_mutate_row(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = CheckAndMutateRowSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.bigtable.v2.Bigtable/PingAndWarm" => { + #[allow(non_camel_case_types)] + struct PingAndWarmSvc(pub Arc); + impl< + T: Bigtable, + > tonic::server::UnaryService + for PingAndWarmSvc { + type Response = super::PingAndWarmResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::ping_and_warm(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = PingAndWarmSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.bigtable.v2.Bigtable/ReadModifyWriteRow" => { + #[allow(non_camel_case_types)] + struct ReadModifyWriteRowSvc(pub Arc); + impl< + T: Bigtable, + > tonic::server::UnaryService + for ReadModifyWriteRowSvc { + type Response = super::ReadModifyWriteRowResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::read_modify_write_row(&inner, request) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = ReadModifyWriteRowSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.bigtable.v2.Bigtable/GenerateInitialChangeStreamPartitions" => { + #[allow(non_camel_case_types)] + struct GenerateInitialChangeStreamPartitionsSvc( + pub Arc, + ); + impl< + T: Bigtable, + > tonic::server::ServerStreamingService< + super::GenerateInitialChangeStreamPartitionsRequest, + > for GenerateInitialChangeStreamPartitionsSvc { + type Response = super::GenerateInitialChangeStreamPartitionsResponse; + type ResponseStream = BoxStream< + super::GenerateInitialChangeStreamPartitionsResponse, + >; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request< + super::GenerateInitialChangeStreamPartitionsRequest, + >, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::generate_initial_change_stream_partitions( + &inner, + request, + ) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = GenerateInitialChangeStreamPartitionsSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.server_streaming(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.bigtable.v2.Bigtable/ReadChangeStream" => { + #[allow(non_camel_case_types)] + struct ReadChangeStreamSvc(pub Arc); + impl< + T: Bigtable, + > tonic::server::ServerStreamingService< + super::ReadChangeStreamRequest, + > for ReadChangeStreamSvc { + type Response = super::ReadChangeStreamResponse; + type ResponseStream = BoxStream; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::read_change_stream(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = ReadChangeStreamSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.server_streaming(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + _ => { + Box::pin(async move { + Ok( + http::Response::builder() + .status(200) + .header("grpc-status", "12") + .header("content-type", "application/grpc") + .body(empty_body()) + .unwrap(), + ) + }) + } + } + } + } + impl Clone for BigtableServer { + fn clone(&self) -> Self { + let inner = self.inner.clone(); + Self { + inner, + accept_compression_encodings: self.accept_compression_encodings, + send_compression_encodings: self.send_compression_encodings, + max_decoding_message_size: self.max_decoding_message_size, + max_encoding_message_size: self.max_encoding_message_size, + } + } + } + impl Clone for _Inner { + fn clone(&self) -> Self { + Self(Arc::clone(&self.0)) + } + } + impl std::fmt::Debug for _Inner { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self.0) + } + } + impl tonic::server::NamedService for BigtableServer { + const NAME: &'static str = "google.bigtable.v2.Bigtable"; + } +} diff --git a/src/generated/google.iam.v1.rs b/src/generated/google.iam.v1.rs index c1ab902..bcc034d 100644 --- a/src/generated/google.iam.v1.rs +++ b/src/generated/google.iam.v1.rs @@ -652,3 +652,332 @@ pub mod iam_policy_client { } } } +/// Generated server implementations. +#[cfg(test)] +pub mod iam_policy_server { + #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] + use tonic::codegen::*; + /// Generated trait containing gRPC methods that should be implemented for use with IamPolicyServer. + #[async_trait] + pub trait IamPolicy: Send + Sync + 'static { + /// Sets the access control policy on the specified resource. Replaces any + /// existing policy. + /// + /// Can return `NOT_FOUND`, `INVALID_ARGUMENT`, and `PERMISSION_DENIED` errors. + async fn set_iam_policy( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status> { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Gets the access control policy for a resource. + /// Returns an empty policy if the resource exists and does not have a policy + /// set. + async fn get_iam_policy( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status> { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Returns permissions that a caller has on the specified resource. + /// If the resource does not exist, this will return an empty set of + /// permissions, not a `NOT_FOUND` error. + /// + /// Note: This operation is designed to be used for building permission-aware + /// UIs and command-line tools, not for authorization checking. This operation + /// may "fail open" without warning. + async fn test_iam_permissions( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + } + /// API Overview + /// + /// + /// Manages Identity and Access Management (IAM) policies. + /// + /// Any implementation of an API that offers access control features + /// implements the google.iam.v1.IAMPolicy interface. + /// + /// ## Data model + /// + /// Access control is applied when a principal (user or service account), takes + /// some action on a resource exposed by a service. Resources, identified by + /// URI-like names, are the unit of access control specification. Service + /// implementations can choose the granularity of access control and the + /// supported permissions for their resources. + /// For example one database service may allow access control to be + /// specified only at the Table level, whereas another might allow access control + /// to also be specified at the Column level. + /// + /// ## Policy Structure + /// + /// See google.iam.v1.Policy + /// + /// This is intentionally not a CRUD style API because access control policies + /// are created and deleted implicitly with the resources to which they are + /// attached. + #[derive(Debug)] + pub struct IamPolicyServer { + inner: _Inner, + accept_compression_encodings: EnabledCompressionEncodings, + send_compression_encodings: EnabledCompressionEncodings, + max_decoding_message_size: Option, + max_encoding_message_size: Option, + } + struct _Inner(Arc); + impl IamPolicyServer { + pub fn new(inner: T) -> Self { + Self::from_arc(Arc::new(inner)) + } + pub fn from_arc(inner: Arc) -> Self { + let inner = _Inner(inner); + Self { + inner, + accept_compression_encodings: Default::default(), + send_compression_encodings: Default::default(), + max_decoding_message_size: None, + max_encoding_message_size: None, + } + } + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> InterceptedService + where + F: tonic::service::Interceptor, + { + InterceptedService::new(Self::new(inner), interceptor) + } + /// Enable decompressing requests with the given encoding. + #[must_use] + pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.accept_compression_encodings.enable(encoding); + self + } + /// Compress responses with the given encoding, if the client supports it. + #[must_use] + pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.send_compression_encodings.enable(encoding); + self + } + /// Limits the maximum size of a decoded message. + /// + /// Default: `4MB` + #[must_use] + pub fn max_decoding_message_size(mut self, limit: usize) -> Self { + self.max_decoding_message_size = Some(limit); + self + } + /// Limits the maximum size of an encoded message. + /// + /// Default: `usize::MAX` + #[must_use] + pub fn max_encoding_message_size(mut self, limit: usize) -> Self { + self.max_encoding_message_size = Some(limit); + self + } + } + impl tonic::codegen::Service> for IamPolicyServer + where + T: IamPolicy, + B: Body + Send + 'static, + B::Error: Into + Send + 'static, + { + type Response = http::Response; + type Error = std::convert::Infallible; + type Future = BoxFuture; + fn poll_ready( + &mut self, + _cx: &mut Context<'_>, + ) -> Poll> { + Poll::Ready(Ok(())) + } + fn call(&mut self, req: http::Request) -> Self::Future { + let inner = self.inner.clone(); + match req.uri().path() { + "/google.iam.v1.IAMPolicy/SetIamPolicy" => { + #[allow(non_camel_case_types)] + struct SetIamPolicySvc(pub Arc); + impl< + T: IamPolicy, + > tonic::server::UnaryService + for SetIamPolicySvc { + type Response = super::Policy; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::set_iam_policy(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = SetIamPolicySvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.iam.v1.IAMPolicy/GetIamPolicy" => { + #[allow(non_camel_case_types)] + struct GetIamPolicySvc(pub Arc); + impl< + T: IamPolicy, + > tonic::server::UnaryService + for GetIamPolicySvc { + type Response = super::Policy; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::get_iam_policy(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = GetIamPolicySvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.iam.v1.IAMPolicy/TestIamPermissions" => { + #[allow(non_camel_case_types)] + struct TestIamPermissionsSvc(pub Arc); + impl< + T: IamPolicy, + > tonic::server::UnaryService + for TestIamPermissionsSvc { + type Response = super::TestIamPermissionsResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::test_iam_permissions(&inner, request) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = TestIamPermissionsSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + _ => { + Box::pin(async move { + Ok( + http::Response::builder() + .status(200) + .header("grpc-status", "12") + .header("content-type", "application/grpc") + .body(empty_body()) + .unwrap(), + ) + }) + } + } + } + } + impl Clone for IamPolicyServer { + fn clone(&self) -> Self { + let inner = self.inner.clone(); + Self { + inner, + accept_compression_encodings: self.accept_compression_encodings, + send_compression_encodings: self.send_compression_encodings, + max_decoding_message_size: self.max_decoding_message_size, + max_encoding_message_size: self.max_encoding_message_size, + } + } + } + impl Clone for _Inner { + fn clone(&self) -> Self { + Self(Arc::clone(&self.0)) + } + } + impl std::fmt::Debug for _Inner { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self.0) + } + } + impl tonic::server::NamedService for IamPolicyServer { + const NAME: &'static str = "google.iam.v1.IAMPolicy"; + } +} diff --git a/src/generated/google.longrunning.rs b/src/generated/google.longrunning.rs index cd10e2a..d1b1880 100644 --- a/src/generated/google.longrunning.rs +++ b/src/generated/google.longrunning.rs @@ -411,3 +411,440 @@ pub mod operations_client { } } } +/// Generated server implementations. +#[cfg(test)] +pub mod operations_server { + #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] + use tonic::codegen::*; + /// Generated trait containing gRPC methods that should be implemented for use with OperationsServer. + #[async_trait] + pub trait Operations: Send + Sync + 'static { + /// Lists operations that match the specified filter in the request. If the + /// server doesn't support this method, it returns `UNIMPLEMENTED`. + /// + /// NOTE: the `name` binding allows API services to override the binding + /// to use different resource name schemes, such as `users/*/operations`. To + /// override the binding, API services can add a binding such as + /// `"/v1/{name=users/*}/operations"` to their service configuration. + /// For backwards compatibility, the default name includes the operations + /// collection id, however overriding users must ensure the name binding + /// is the parent resource, without the operations collection id. + async fn list_operations( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Gets the latest state of a long-running operation. Clients can use this + /// method to poll the operation result at intervals as recommended by the API + /// service. + async fn get_operation( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status> { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Deletes a long-running operation. This method indicates that the client is + /// no longer interested in the operation result. It does not cancel the + /// operation. If the server doesn't support this method, it returns + /// `google.rpc.Code.UNIMPLEMENTED`. + async fn delete_operation( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status> { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Starts asynchronous cancellation on a long-running operation. The server + /// makes a best effort to cancel the operation, but success is not + /// guaranteed. If the server doesn't support this method, it returns + /// `google.rpc.Code.UNIMPLEMENTED`. Clients can use + /// [Operations.GetOperation][google.longrunning.Operations.GetOperation] or + /// other methods to check whether the cancellation succeeded or whether the + /// operation completed despite cancellation. On successful cancellation, + /// the operation is not deleted; instead, it becomes an operation with + /// an [Operation.error][google.longrunning.Operation.error] value with a [google.rpc.Status.code][google.rpc.Status.code] of 1, + /// corresponding to `Code.CANCELLED`. + async fn cancel_operation( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status> { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Waits until the specified long-running operation is done or reaches at most + /// a specified timeout, returning the latest state. If the operation is + /// already done, the latest state is immediately returned. If the timeout + /// specified is greater than the default HTTP/RPC timeout, the HTTP/RPC + /// timeout is used. If the server does not support this method, it returns + /// `google.rpc.Code.UNIMPLEMENTED`. + /// Note that this method is on a best-effort basis. It may return the latest + /// state before the specified timeout (including immediately), meaning even an + /// immediate response is no guarantee that the operation is done. + async fn wait_operation( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status> { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + } + /// Manages long-running operations with an API service. + /// + /// When an API method normally takes long time to complete, it can be designed + /// to return [Operation][google.longrunning.Operation] to the client, and the client can use this + /// interface to receive the real response asynchronously by polling the + /// operation resource, or pass the operation resource to another API (such as + /// Google Cloud Pub/Sub API) to receive the response. Any API service that + /// returns long-running operations should implement the `Operations` interface + /// so developers can have a consistent client experience. + #[derive(Debug)] + pub struct OperationsServer { + inner: _Inner, + accept_compression_encodings: EnabledCompressionEncodings, + send_compression_encodings: EnabledCompressionEncodings, + max_decoding_message_size: Option, + max_encoding_message_size: Option, + } + struct _Inner(Arc); + impl OperationsServer { + pub fn new(inner: T) -> Self { + Self::from_arc(Arc::new(inner)) + } + pub fn from_arc(inner: Arc) -> Self { + let inner = _Inner(inner); + Self { + inner, + accept_compression_encodings: Default::default(), + send_compression_encodings: Default::default(), + max_decoding_message_size: None, + max_encoding_message_size: None, + } + } + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> InterceptedService + where + F: tonic::service::Interceptor, + { + InterceptedService::new(Self::new(inner), interceptor) + } + /// Enable decompressing requests with the given encoding. + #[must_use] + pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.accept_compression_encodings.enable(encoding); + self + } + /// Compress responses with the given encoding, if the client supports it. + #[must_use] + pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.send_compression_encodings.enable(encoding); + self + } + /// Limits the maximum size of a decoded message. + /// + /// Default: `4MB` + #[must_use] + pub fn max_decoding_message_size(mut self, limit: usize) -> Self { + self.max_decoding_message_size = Some(limit); + self + } + /// Limits the maximum size of an encoded message. + /// + /// Default: `usize::MAX` + #[must_use] + pub fn max_encoding_message_size(mut self, limit: usize) -> Self { + self.max_encoding_message_size = Some(limit); + self + } + } + impl tonic::codegen::Service> for OperationsServer + where + T: Operations, + B: Body + Send + 'static, + B::Error: Into + Send + 'static, + { + type Response = http::Response; + type Error = std::convert::Infallible; + type Future = BoxFuture; + fn poll_ready( + &mut self, + _cx: &mut Context<'_>, + ) -> Poll> { + Poll::Ready(Ok(())) + } + fn call(&mut self, req: http::Request) -> Self::Future { + let inner = self.inner.clone(); + match req.uri().path() { + "/google.longrunning.Operations/ListOperations" => { + #[allow(non_camel_case_types)] + struct ListOperationsSvc(pub Arc); + impl< + T: Operations, + > tonic::server::UnaryService + for ListOperationsSvc { + type Response = super::ListOperationsResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::list_operations(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = ListOperationsSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.longrunning.Operations/GetOperation" => { + #[allow(non_camel_case_types)] + struct GetOperationSvc(pub Arc); + impl< + T: Operations, + > tonic::server::UnaryService + for GetOperationSvc { + type Response = super::Operation; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::get_operation(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = GetOperationSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.longrunning.Operations/DeleteOperation" => { + #[allow(non_camel_case_types)] + struct DeleteOperationSvc(pub Arc); + impl< + T: Operations, + > tonic::server::UnaryService + for DeleteOperationSvc { + type Response = (); + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::delete_operation(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = DeleteOperationSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.longrunning.Operations/CancelOperation" => { + #[allow(non_camel_case_types)] + struct CancelOperationSvc(pub Arc); + impl< + T: Operations, + > tonic::server::UnaryService + for CancelOperationSvc { + type Response = (); + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::cancel_operation(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = CancelOperationSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.longrunning.Operations/WaitOperation" => { + #[allow(non_camel_case_types)] + struct WaitOperationSvc(pub Arc); + impl< + T: Operations, + > tonic::server::UnaryService + for WaitOperationSvc { + type Response = super::Operation; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::wait_operation(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = WaitOperationSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + _ => { + Box::pin(async move { + Ok( + http::Response::builder() + .status(200) + .header("grpc-status", "12") + .header("content-type", "application/grpc") + .body(empty_body()) + .unwrap(), + ) + }) + } + } + } + } + impl Clone for OperationsServer { + fn clone(&self) -> Self { + let inner = self.inner.clone(); + Self { + inner, + accept_compression_encodings: self.accept_compression_encodings, + send_compression_encodings: self.send_compression_encodings, + max_decoding_message_size: self.max_decoding_message_size, + max_encoding_message_size: self.max_encoding_message_size, + } + } + } + impl Clone for _Inner { + fn clone(&self) -> Self { + Self(Arc::clone(&self.0)) + } + } + impl std::fmt::Debug for _Inner { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self.0) + } + } + impl tonic::server::NamedService for OperationsServer { + const NAME: &'static str = "google.longrunning.Operations"; + } +} diff --git a/src/generated/google.pubsub.v1.rs b/src/generated/google.pubsub.v1.rs index 109e8f5..80e9838 100644 --- a/src/generated/google.pubsub.v1.rs +++ b/src/generated/google.pubsub.v1.rs @@ -716,6 +716,684 @@ pub mod schema_service_client { } } } +/// Generated server implementations. +#[cfg(test)] +pub mod schema_service_server { + #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] + use tonic::codegen::*; + /// Generated trait containing gRPC methods that should be implemented for use with SchemaServiceServer. + #[async_trait] + pub trait SchemaService: Send + Sync + 'static { + /// Creates a schema. + async fn create_schema( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status> { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Gets a schema. + async fn get_schema( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status> { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Lists schemas in a project. + async fn list_schemas( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Lists all schema revisions for the named schema. + async fn list_schema_revisions( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Commits a new schema revision to an existing schema. + async fn commit_schema( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status> { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Creates a new schema revision that is a copy of the provided revision_id. + async fn rollback_schema( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status> { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Deletes a specific schema revision. + async fn delete_schema_revision( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status> { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Deletes a schema. + async fn delete_schema( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status> { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Validates a schema. + async fn validate_schema( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Validates a message against a schema. + async fn validate_message( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + } + /// Service for doing schema-related operations. + #[derive(Debug)] + pub struct SchemaServiceServer { + inner: _Inner, + accept_compression_encodings: EnabledCompressionEncodings, + send_compression_encodings: EnabledCompressionEncodings, + max_decoding_message_size: Option, + max_encoding_message_size: Option, + } + struct _Inner(Arc); + impl SchemaServiceServer { + pub fn new(inner: T) -> Self { + Self::from_arc(Arc::new(inner)) + } + pub fn from_arc(inner: Arc) -> Self { + let inner = _Inner(inner); + Self { + inner, + accept_compression_encodings: Default::default(), + send_compression_encodings: Default::default(), + max_decoding_message_size: None, + max_encoding_message_size: None, + } + } + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> InterceptedService + where + F: tonic::service::Interceptor, + { + InterceptedService::new(Self::new(inner), interceptor) + } + /// Enable decompressing requests with the given encoding. + #[must_use] + pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.accept_compression_encodings.enable(encoding); + self + } + /// Compress responses with the given encoding, if the client supports it. + #[must_use] + pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.send_compression_encodings.enable(encoding); + self + } + /// Limits the maximum size of a decoded message. + /// + /// Default: `4MB` + #[must_use] + pub fn max_decoding_message_size(mut self, limit: usize) -> Self { + self.max_decoding_message_size = Some(limit); + self + } + /// Limits the maximum size of an encoded message. + /// + /// Default: `usize::MAX` + #[must_use] + pub fn max_encoding_message_size(mut self, limit: usize) -> Self { + self.max_encoding_message_size = Some(limit); + self + } + } + impl tonic::codegen::Service> for SchemaServiceServer + where + T: SchemaService, + B: Body + Send + 'static, + B::Error: Into + Send + 'static, + { + type Response = http::Response; + type Error = std::convert::Infallible; + type Future = BoxFuture; + fn poll_ready( + &mut self, + _cx: &mut Context<'_>, + ) -> Poll> { + Poll::Ready(Ok(())) + } + fn call(&mut self, req: http::Request) -> Self::Future { + let inner = self.inner.clone(); + match req.uri().path() { + "/google.pubsub.v1.SchemaService/CreateSchema" => { + #[allow(non_camel_case_types)] + struct CreateSchemaSvc(pub Arc); + impl< + T: SchemaService, + > tonic::server::UnaryService + for CreateSchemaSvc { + type Response = super::Schema; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::create_schema(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = CreateSchemaSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.pubsub.v1.SchemaService/GetSchema" => { + #[allow(non_camel_case_types)] + struct GetSchemaSvc(pub Arc); + impl< + T: SchemaService, + > tonic::server::UnaryService + for GetSchemaSvc { + type Response = super::Schema; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::get_schema(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = GetSchemaSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.pubsub.v1.SchemaService/ListSchemas" => { + #[allow(non_camel_case_types)] + struct ListSchemasSvc(pub Arc); + impl< + T: SchemaService, + > tonic::server::UnaryService + for ListSchemasSvc { + type Response = super::ListSchemasResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::list_schemas(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = ListSchemasSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.pubsub.v1.SchemaService/ListSchemaRevisions" => { + #[allow(non_camel_case_types)] + struct ListSchemaRevisionsSvc(pub Arc); + impl< + T: SchemaService, + > tonic::server::UnaryService + for ListSchemaRevisionsSvc { + type Response = super::ListSchemaRevisionsResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::list_schema_revisions(&inner, request) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = ListSchemaRevisionsSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.pubsub.v1.SchemaService/CommitSchema" => { + #[allow(non_camel_case_types)] + struct CommitSchemaSvc(pub Arc); + impl< + T: SchemaService, + > tonic::server::UnaryService + for CommitSchemaSvc { + type Response = super::Schema; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::commit_schema(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = CommitSchemaSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.pubsub.v1.SchemaService/RollbackSchema" => { + #[allow(non_camel_case_types)] + struct RollbackSchemaSvc(pub Arc); + impl< + T: SchemaService, + > tonic::server::UnaryService + for RollbackSchemaSvc { + type Response = super::Schema; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::rollback_schema(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = RollbackSchemaSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.pubsub.v1.SchemaService/DeleteSchemaRevision" => { + #[allow(non_camel_case_types)] + struct DeleteSchemaRevisionSvc(pub Arc); + impl< + T: SchemaService, + > tonic::server::UnaryService + for DeleteSchemaRevisionSvc { + type Response = super::Schema; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::delete_schema_revision( + &inner, + request, + ) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = DeleteSchemaRevisionSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.pubsub.v1.SchemaService/DeleteSchema" => { + #[allow(non_camel_case_types)] + struct DeleteSchemaSvc(pub Arc); + impl< + T: SchemaService, + > tonic::server::UnaryService + for DeleteSchemaSvc { + type Response = (); + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::delete_schema(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = DeleteSchemaSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.pubsub.v1.SchemaService/ValidateSchema" => { + #[allow(non_camel_case_types)] + struct ValidateSchemaSvc(pub Arc); + impl< + T: SchemaService, + > tonic::server::UnaryService + for ValidateSchemaSvc { + type Response = super::ValidateSchemaResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::validate_schema(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = ValidateSchemaSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.pubsub.v1.SchemaService/ValidateMessage" => { + #[allow(non_camel_case_types)] + struct ValidateMessageSvc(pub Arc); + impl< + T: SchemaService, + > tonic::server::UnaryService + for ValidateMessageSvc { + type Response = super::ValidateMessageResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::validate_message(&inner, request) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = ValidateMessageSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + _ => { + Box::pin(async move { + Ok( + http::Response::builder() + .status(200) + .header("grpc-status", "12") + .header("content-type", "application/grpc") + .body(empty_body()) + .unwrap(), + ) + }) + } + } + } + } + impl Clone for SchemaServiceServer { + fn clone(&self) -> Self { + let inner = self.inner.clone(); + Self { + inner, + accept_compression_encodings: self.accept_compression_encodings, + send_compression_encodings: self.send_compression_encodings, + max_decoding_message_size: self.max_decoding_message_size, + max_encoding_message_size: self.max_encoding_message_size, + } + } + } + impl Clone for _Inner { + fn clone(&self) -> Self { + Self(Arc::clone(&self.0)) + } + } + impl std::fmt::Debug for _Inner { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self.0) + } + } + impl tonic::server::NamedService for SchemaServiceServer { + const NAME: &'static str = "google.pubsub.v1.SchemaService"; + } +} /// A policy constraining the storage of messages published to the topic. #[non_exhaustive] #[allow(clippy::derive_partial_eq_without_eq)] @@ -3060,3 +3738,1705 @@ pub mod subscriber_client { } } } +/// Generated server implementations. +#[cfg(test)] +pub mod publisher_server { + #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] + use tonic::codegen::*; + /// Generated trait containing gRPC methods that should be implemented for use with PublisherServer. + #[async_trait] + pub trait Publisher: Send + Sync + 'static { + /// Creates the given topic with the given name. See the [resource name rules] + /// (https://cloud.google.com/pubsub/docs/pubsub-basics#resource_names). + async fn create_topic( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status> { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Updates an existing topic. Note that certain properties of a + /// topic are not modifiable. + async fn update_topic( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status> { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Adds one or more messages to the topic. Returns `NOT_FOUND` if the topic + /// does not exist. + async fn publish( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Gets the configuration of a topic. + async fn get_topic( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status> { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Lists matching topics. + async fn list_topics( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Lists the names of the attached subscriptions on this topic. + async fn list_topic_subscriptions( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Lists the names of the snapshots on this topic. Snapshots are used in + /// [Seek](https://cloud.google.com/pubsub/docs/replay-overview) operations, + /// which allow you to manage message acknowledgments in bulk. That is, you can + /// set the acknowledgment state of messages in an existing subscription to the + /// state captured by a snapshot. + async fn list_topic_snapshots( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Deletes the topic with the given name. Returns `NOT_FOUND` if the topic + /// does not exist. After a topic is deleted, a new topic may be created with + /// the same name; this is an entirely new topic with none of the old + /// configuration or subscriptions. Existing subscriptions to this topic are + /// not deleted, but their `topic` field is set to `_deleted-topic_`. + async fn delete_topic( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status> { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Detaches a subscription from this topic. All messages retained in the + /// subscription are dropped. Subsequent `Pull` and `StreamingPull` requests + /// will return FAILED_PRECONDITION. If the subscription is a push + /// subscription, pushes to the endpoint will stop. + async fn detach_subscription( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + } + /// The service that an application uses to manipulate topics, and to send + /// messages to a topic. + #[derive(Debug)] + pub struct PublisherServer { + inner: _Inner, + accept_compression_encodings: EnabledCompressionEncodings, + send_compression_encodings: EnabledCompressionEncodings, + max_decoding_message_size: Option, + max_encoding_message_size: Option, + } + struct _Inner(Arc); + impl PublisherServer { + pub fn new(inner: T) -> Self { + Self::from_arc(Arc::new(inner)) + } + pub fn from_arc(inner: Arc) -> Self { + let inner = _Inner(inner); + Self { + inner, + accept_compression_encodings: Default::default(), + send_compression_encodings: Default::default(), + max_decoding_message_size: None, + max_encoding_message_size: None, + } + } + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> InterceptedService + where + F: tonic::service::Interceptor, + { + InterceptedService::new(Self::new(inner), interceptor) + } + /// Enable decompressing requests with the given encoding. + #[must_use] + pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.accept_compression_encodings.enable(encoding); + self + } + /// Compress responses with the given encoding, if the client supports it. + #[must_use] + pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.send_compression_encodings.enable(encoding); + self + } + /// Limits the maximum size of a decoded message. + /// + /// Default: `4MB` + #[must_use] + pub fn max_decoding_message_size(mut self, limit: usize) -> Self { + self.max_decoding_message_size = Some(limit); + self + } + /// Limits the maximum size of an encoded message. + /// + /// Default: `usize::MAX` + #[must_use] + pub fn max_encoding_message_size(mut self, limit: usize) -> Self { + self.max_encoding_message_size = Some(limit); + self + } + } + impl tonic::codegen::Service> for PublisherServer + where + T: Publisher, + B: Body + Send + 'static, + B::Error: Into + Send + 'static, + { + type Response = http::Response; + type Error = std::convert::Infallible; + type Future = BoxFuture; + fn poll_ready( + &mut self, + _cx: &mut Context<'_>, + ) -> Poll> { + Poll::Ready(Ok(())) + } + fn call(&mut self, req: http::Request) -> Self::Future { + let inner = self.inner.clone(); + match req.uri().path() { + "/google.pubsub.v1.Publisher/CreateTopic" => { + #[allow(non_camel_case_types)] + struct CreateTopicSvc(pub Arc); + impl tonic::server::UnaryService + for CreateTopicSvc { + type Response = super::Topic; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::create_topic(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = CreateTopicSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.pubsub.v1.Publisher/UpdateTopic" => { + #[allow(non_camel_case_types)] + struct UpdateTopicSvc(pub Arc); + impl< + T: Publisher, + > tonic::server::UnaryService + for UpdateTopicSvc { + type Response = super::Topic; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::update_topic(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = UpdateTopicSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.pubsub.v1.Publisher/Publish" => { + #[allow(non_camel_case_types)] + struct PublishSvc(pub Arc); + impl tonic::server::UnaryService + for PublishSvc { + type Response = super::PublishResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::publish(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = PublishSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.pubsub.v1.Publisher/GetTopic" => { + #[allow(non_camel_case_types)] + struct GetTopicSvc(pub Arc); + impl< + T: Publisher, + > tonic::server::UnaryService + for GetTopicSvc { + type Response = super::Topic; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::get_topic(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = GetTopicSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.pubsub.v1.Publisher/ListTopics" => { + #[allow(non_camel_case_types)] + struct ListTopicsSvc(pub Arc); + impl< + T: Publisher, + > tonic::server::UnaryService + for ListTopicsSvc { + type Response = super::ListTopicsResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::list_topics(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = ListTopicsSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.pubsub.v1.Publisher/ListTopicSubscriptions" => { + #[allow(non_camel_case_types)] + struct ListTopicSubscriptionsSvc(pub Arc); + impl< + T: Publisher, + > tonic::server::UnaryService + for ListTopicSubscriptionsSvc { + type Response = super::ListTopicSubscriptionsResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::list_topic_subscriptions(&inner, request) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = ListTopicSubscriptionsSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.pubsub.v1.Publisher/ListTopicSnapshots" => { + #[allow(non_camel_case_types)] + struct ListTopicSnapshotsSvc(pub Arc); + impl< + T: Publisher, + > tonic::server::UnaryService + for ListTopicSnapshotsSvc { + type Response = super::ListTopicSnapshotsResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::list_topic_snapshots(&inner, request) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = ListTopicSnapshotsSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.pubsub.v1.Publisher/DeleteTopic" => { + #[allow(non_camel_case_types)] + struct DeleteTopicSvc(pub Arc); + impl< + T: Publisher, + > tonic::server::UnaryService + for DeleteTopicSvc { + type Response = (); + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::delete_topic(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = DeleteTopicSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.pubsub.v1.Publisher/DetachSubscription" => { + #[allow(non_camel_case_types)] + struct DetachSubscriptionSvc(pub Arc); + impl< + T: Publisher, + > tonic::server::UnaryService + for DetachSubscriptionSvc { + type Response = super::DetachSubscriptionResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::detach_subscription(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = DetachSubscriptionSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + _ => { + Box::pin(async move { + Ok( + http::Response::builder() + .status(200) + .header("grpc-status", "12") + .header("content-type", "application/grpc") + .body(empty_body()) + .unwrap(), + ) + }) + } + } + } + } + impl Clone for PublisherServer { + fn clone(&self) -> Self { + let inner = self.inner.clone(); + Self { + inner, + accept_compression_encodings: self.accept_compression_encodings, + send_compression_encodings: self.send_compression_encodings, + max_decoding_message_size: self.max_decoding_message_size, + max_encoding_message_size: self.max_encoding_message_size, + } + } + } + impl Clone for _Inner { + fn clone(&self) -> Self { + Self(Arc::clone(&self.0)) + } + } + impl std::fmt::Debug for _Inner { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self.0) + } + } + impl tonic::server::NamedService for PublisherServer { + const NAME: &'static str = "google.pubsub.v1.Publisher"; + } +} +/// Generated server implementations. +#[cfg(test)] +pub mod subscriber_server { + #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] + use tonic::codegen::*; + /// Generated trait containing gRPC methods that should be implemented for use with SubscriberServer. + #[async_trait] + pub trait Subscriber: Send + Sync + 'static { + /// Creates a subscription to a given topic. See the [resource name rules] + /// (https://cloud.google.com/pubsub/docs/pubsub-basics#resource_names). + /// If the subscription already exists, returns `ALREADY_EXISTS`. + /// If the corresponding topic doesn't exist, returns `NOT_FOUND`. + /// + /// If the name is not provided in the request, the server will assign a random + /// name for this subscription on the same project as the topic, conforming + /// to the [resource name format] + /// (https://cloud.google.com/pubsub/docs/pubsub-basics#resource_names). The + /// generated name is populated in the returned Subscription object. Note that + /// for REST API requests, you must specify a name in the request. + async fn create_subscription( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status> { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Gets the configuration details of a subscription. + async fn get_subscription( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status> { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Updates an existing subscription. Note that certain properties of a + /// subscription, such as its topic, are not modifiable. + async fn update_subscription( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status> { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Lists matching subscriptions. + async fn list_subscriptions( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Deletes an existing subscription. All messages retained in the subscription + /// are immediately dropped. Calls to `Pull` after deletion will return + /// `NOT_FOUND`. After a subscription is deleted, a new one may be created with + /// the same name, but the new one has no association with the old + /// subscription or its topic unless the same topic is specified. + async fn delete_subscription( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status> { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Modifies the ack deadline for a specific message. This method is useful + /// to indicate that more time is needed to process a message by the + /// subscriber, or to make the message available for redelivery if the + /// processing was interrupted. Note that this does not modify the + /// subscription-level `ackDeadlineSeconds` used for subsequent messages. + async fn modify_ack_deadline( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status> { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Acknowledges the messages associated with the `ack_ids` in the + /// `AcknowledgeRequest`. The Pub/Sub system can remove the relevant messages + /// from the subscription. + /// + /// Acknowledging a message whose ack deadline has expired may succeed, + /// but such a message may be redelivered later. Acknowledging a message more + /// than once will not result in an error. + async fn acknowledge( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status> { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Pulls messages from the server. + async fn pull( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status> { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Establishes a stream with the server, which sends messages down to the + /// client. The client streams acknowledgements and ack deadline modifications + /// back to the server. The server will close the stream and return the status + /// on any error. The server may close the stream with status `UNAVAILABLE` to + /// reassign server-side resources, in which case, the client should + /// re-establish the stream. Flow control can be achieved by configuring the + /// underlying RPC channel. + async fn streaming_pull( + &self, + request: tonic::Request>, + ) -> std::result::Result< + tonic::Response>, + tonic::Status, + > { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Modifies the `PushConfig` for a specified subscription. + /// + /// This may be used to change a push subscription to a pull one (signified by + /// an empty `PushConfig`) or vice versa, or change the endpoint URL and other + /// attributes of a push subscription. Messages will accumulate for delivery + /// continuously through the call regardless of changes to the `PushConfig`. + async fn modify_push_config( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status> { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Gets the configuration details of a snapshot. Snapshots are used in + /// [Seek](https://cloud.google.com/pubsub/docs/replay-overview) operations, + /// which allow you to manage message acknowledgments in bulk. That is, you can + /// set the acknowledgment state of messages in an existing subscription to the + /// state captured by a snapshot. + async fn get_snapshot( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status> { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Lists the existing snapshots. Snapshots are used in [Seek]( + /// https://cloud.google.com/pubsub/docs/replay-overview) operations, which + /// allow you to manage message acknowledgments in bulk. That is, you can set + /// the acknowledgment state of messages in an existing subscription to the + /// state captured by a snapshot. + async fn list_snapshots( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Creates a snapshot from the requested subscription. Snapshots are used in + /// [Seek](https://cloud.google.com/pubsub/docs/replay-overview) operations, + /// which allow you to manage message acknowledgments in bulk. That is, you can + /// set the acknowledgment state of messages in an existing subscription to the + /// state captured by a snapshot. + /// If the snapshot already exists, returns `ALREADY_EXISTS`. + /// If the requested subscription doesn't exist, returns `NOT_FOUND`. + /// If the backlog in the subscription is too old -- and the resulting snapshot + /// would expire in less than 1 hour -- then `FAILED_PRECONDITION` is returned. + /// See also the `Snapshot.expire_time` field. If the name is not provided in + /// the request, the server will assign a random + /// name for this snapshot on the same project as the subscription, conforming + /// to the [resource name format] + /// (https://cloud.google.com/pubsub/docs/pubsub-basics#resource_names). The + /// generated name is populated in the returned Snapshot object. Note that for + /// REST API requests, you must specify a name in the request. + async fn create_snapshot( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status> { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Updates an existing snapshot. Snapshots are used in + /// [Seek](https://cloud.google.com/pubsub/docs/replay-overview) operations, + /// which allow you to manage message acknowledgments in bulk. That is, you can + /// set the acknowledgment state of messages in an existing subscription to the + /// state captured by a snapshot. + async fn update_snapshot( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status> { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Removes an existing snapshot. Snapshots are used in [Seek] + /// (https://cloud.google.com/pubsub/docs/replay-overview) operations, which + /// allow you to manage message acknowledgments in bulk. That is, you can set + /// the acknowledgment state of messages in an existing subscription to the + /// state captured by a snapshot. + /// When the snapshot is deleted, all messages retained in the snapshot + /// are immediately dropped. After a snapshot is deleted, a new one may be + /// created with the same name, but the new one has no association with the old + /// snapshot or its subscription, unless the same subscription is specified. + async fn delete_snapshot( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status> { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + /// Seeks an existing subscription to a point in time or to a given snapshot, + /// whichever is provided in the request. Snapshots are used in [Seek] + /// (https://cloud.google.com/pubsub/docs/replay-overview) operations, which + /// allow you to manage message acknowledgments in bulk. That is, you can set + /// the acknowledgment state of messages in an existing subscription to the + /// state captured by a snapshot. Note that both the subscription and the + /// snapshot must be on the same topic. + async fn seek( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status> { + Err(tonic::Status::unimplemented("Not yet implemented")) + } + } + /// The service that an application uses to manipulate subscriptions and to + /// consume messages from a subscription via the `Pull` method or by + /// establishing a bi-directional stream using the `StreamingPull` method. + #[derive(Debug)] + pub struct SubscriberServer { + inner: _Inner, + accept_compression_encodings: EnabledCompressionEncodings, + send_compression_encodings: EnabledCompressionEncodings, + max_decoding_message_size: Option, + max_encoding_message_size: Option, + } + struct _Inner(Arc); + impl SubscriberServer { + pub fn new(inner: T) -> Self { + Self::from_arc(Arc::new(inner)) + } + pub fn from_arc(inner: Arc) -> Self { + let inner = _Inner(inner); + Self { + inner, + accept_compression_encodings: Default::default(), + send_compression_encodings: Default::default(), + max_decoding_message_size: None, + max_encoding_message_size: None, + } + } + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> InterceptedService + where + F: tonic::service::Interceptor, + { + InterceptedService::new(Self::new(inner), interceptor) + } + /// Enable decompressing requests with the given encoding. + #[must_use] + pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.accept_compression_encodings.enable(encoding); + self + } + /// Compress responses with the given encoding, if the client supports it. + #[must_use] + pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.send_compression_encodings.enable(encoding); + self + } + /// Limits the maximum size of a decoded message. + /// + /// Default: `4MB` + #[must_use] + pub fn max_decoding_message_size(mut self, limit: usize) -> Self { + self.max_decoding_message_size = Some(limit); + self + } + /// Limits the maximum size of an encoded message. + /// + /// Default: `usize::MAX` + #[must_use] + pub fn max_encoding_message_size(mut self, limit: usize) -> Self { + self.max_encoding_message_size = Some(limit); + self + } + } + impl tonic::codegen::Service> for SubscriberServer + where + T: Subscriber, + B: Body + Send + 'static, + B::Error: Into + Send + 'static, + { + type Response = http::Response; + type Error = std::convert::Infallible; + type Future = BoxFuture; + fn poll_ready( + &mut self, + _cx: &mut Context<'_>, + ) -> Poll> { + Poll::Ready(Ok(())) + } + fn call(&mut self, req: http::Request) -> Self::Future { + let inner = self.inner.clone(); + match req.uri().path() { + "/google.pubsub.v1.Subscriber/CreateSubscription" => { + #[allow(non_camel_case_types)] + struct CreateSubscriptionSvc(pub Arc); + impl tonic::server::UnaryService + for CreateSubscriptionSvc { + type Response = super::Subscription; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::create_subscription(&inner, request) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = CreateSubscriptionSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.pubsub.v1.Subscriber/GetSubscription" => { + #[allow(non_camel_case_types)] + struct GetSubscriptionSvc(pub Arc); + impl< + T: Subscriber, + > tonic::server::UnaryService + for GetSubscriptionSvc { + type Response = super::Subscription; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::get_subscription(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = GetSubscriptionSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.pubsub.v1.Subscriber/UpdateSubscription" => { + #[allow(non_camel_case_types)] + struct UpdateSubscriptionSvc(pub Arc); + impl< + T: Subscriber, + > tonic::server::UnaryService + for UpdateSubscriptionSvc { + type Response = super::Subscription; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::update_subscription(&inner, request) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = UpdateSubscriptionSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.pubsub.v1.Subscriber/ListSubscriptions" => { + #[allow(non_camel_case_types)] + struct ListSubscriptionsSvc(pub Arc); + impl< + T: Subscriber, + > tonic::server::UnaryService + for ListSubscriptionsSvc { + type Response = super::ListSubscriptionsResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::list_subscriptions(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = ListSubscriptionsSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.pubsub.v1.Subscriber/DeleteSubscription" => { + #[allow(non_camel_case_types)] + struct DeleteSubscriptionSvc(pub Arc); + impl< + T: Subscriber, + > tonic::server::UnaryService + for DeleteSubscriptionSvc { + type Response = (); + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::delete_subscription(&inner, request) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = DeleteSubscriptionSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.pubsub.v1.Subscriber/ModifyAckDeadline" => { + #[allow(non_camel_case_types)] + struct ModifyAckDeadlineSvc(pub Arc); + impl< + T: Subscriber, + > tonic::server::UnaryService + for ModifyAckDeadlineSvc { + type Response = (); + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::modify_ack_deadline(&inner, request) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = ModifyAckDeadlineSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.pubsub.v1.Subscriber/Acknowledge" => { + #[allow(non_camel_case_types)] + struct AcknowledgeSvc(pub Arc); + impl< + T: Subscriber, + > tonic::server::UnaryService + for AcknowledgeSvc { + type Response = (); + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::acknowledge(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = AcknowledgeSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.pubsub.v1.Subscriber/Pull" => { + #[allow(non_camel_case_types)] + struct PullSvc(pub Arc); + impl tonic::server::UnaryService + for PullSvc { + type Response = super::PullResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::pull(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = PullSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.pubsub.v1.Subscriber/StreamingPull" => { + #[allow(non_camel_case_types)] + struct StreamingPullSvc(pub Arc); + impl< + T: Subscriber, + > tonic::server::StreamingService + for StreamingPullSvc { + type Response = super::StreamingPullResponse; + type ResponseStream = BoxStream; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request< + tonic::Streaming, + >, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::streaming_pull(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = StreamingPullSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.streaming(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.pubsub.v1.Subscriber/ModifyPushConfig" => { + #[allow(non_camel_case_types)] + struct ModifyPushConfigSvc(pub Arc); + impl< + T: Subscriber, + > tonic::server::UnaryService + for ModifyPushConfigSvc { + type Response = (); + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::modify_push_config(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = ModifyPushConfigSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.pubsub.v1.Subscriber/GetSnapshot" => { + #[allow(non_camel_case_types)] + struct GetSnapshotSvc(pub Arc); + impl< + T: Subscriber, + > tonic::server::UnaryService + for GetSnapshotSvc { + type Response = super::Snapshot; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::get_snapshot(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = GetSnapshotSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.pubsub.v1.Subscriber/ListSnapshots" => { + #[allow(non_camel_case_types)] + struct ListSnapshotsSvc(pub Arc); + impl< + T: Subscriber, + > tonic::server::UnaryService + for ListSnapshotsSvc { + type Response = super::ListSnapshotsResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::list_snapshots(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = ListSnapshotsSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.pubsub.v1.Subscriber/CreateSnapshot" => { + #[allow(non_camel_case_types)] + struct CreateSnapshotSvc(pub Arc); + impl< + T: Subscriber, + > tonic::server::UnaryService + for CreateSnapshotSvc { + type Response = super::Snapshot; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::create_snapshot(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = CreateSnapshotSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.pubsub.v1.Subscriber/UpdateSnapshot" => { + #[allow(non_camel_case_types)] + struct UpdateSnapshotSvc(pub Arc); + impl< + T: Subscriber, + > tonic::server::UnaryService + for UpdateSnapshotSvc { + type Response = super::Snapshot; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::update_snapshot(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = UpdateSnapshotSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.pubsub.v1.Subscriber/DeleteSnapshot" => { + #[allow(non_camel_case_types)] + struct DeleteSnapshotSvc(pub Arc); + impl< + T: Subscriber, + > tonic::server::UnaryService + for DeleteSnapshotSvc { + type Response = (); + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::delete_snapshot(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = DeleteSnapshotSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/google.pubsub.v1.Subscriber/Seek" => { + #[allow(non_camel_case_types)] + struct SeekSvc(pub Arc); + impl tonic::server::UnaryService + for SeekSvc { + type Response = super::SeekResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::seek(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = SeekSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + _ => { + Box::pin(async move { + Ok( + http::Response::builder() + .status(200) + .header("grpc-status", "12") + .header("content-type", "application/grpc") + .body(empty_body()) + .unwrap(), + ) + }) + } + } + } + } + impl Clone for SubscriberServer { + fn clone(&self) -> Self { + let inner = self.inner.clone(); + Self { + inner, + accept_compression_encodings: self.accept_compression_encodings, + send_compression_encodings: self.send_compression_encodings, + max_decoding_message_size: self.max_decoding_message_size, + max_encoding_message_size: self.max_encoding_message_size, + } + } + } + impl Clone for _Inner { + fn clone(&self) -> Self { + Self(Arc::clone(&self.0)) + } + } + impl std::fmt::Debug for _Inner { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self.0) + } + } + impl tonic::server::NamedService for SubscriberServer { + const NAME: &'static str = "google.pubsub.v1.Subscriber"; + } +} diff --git a/src/pubsub/mod.rs b/src/pubsub/mod.rs index 43ece2c..ecb0bcb 100644 --- a/src/pubsub/mod.rs +++ b/src/pubsub/mod.rs @@ -27,7 +27,12 @@ mod streaming_subscription; pub mod emulator; /// Types and functions generated from PubSub's gRPC schema -#[allow(rustdoc::broken_intra_doc_links, rustdoc::bare_urls, missing_docs)] +#[allow( + rustdoc::broken_intra_doc_links, + rustdoc::bare_urls, + missing_docs, + unreachable_pub +)] pub mod api { include!("../generated/google.pubsub.v1.rs"); @@ -135,7 +140,27 @@ where let sub_name: String = subscription.clone().into(); let span = debug_span!("create_subscription", topic = sub_name); let _guard = span.enter(); - StreamSubscription::new(self.inner.clone(), subscription.into(), config) + StreamSubscription::new( + // As of the ack handler changes, the streaming implementation needs more than one + // client. The obvious approach would be to clone the client as necessary. However + // adding a Clone bound is a major semver change, and for downstream-simplification + // reasons we'd like to avoid that. + // + // Fortunately, there already *was* a clone bound on this `stream_subscription` + // function, which is the only public way to construct the stream. Also fortunately we + // only need a static number of clients and not arbitrary clones, so we can clone here + // and pass an array down there. This isn't *pretty*, but it works + // + // TODO(0.12.0) just add the clone bound + [ + self.inner.clone(), + self.inner.clone(), + self.inner.clone(), + self.inner.clone(), + ], + subscription.into(), + config, + ) } } diff --git a/src/pubsub/streaming_subscription.rs b/src/pubsub/streaming_subscription.rs index 18c4f39..b77d730 100644 --- a/src/pubsub/streaming_subscription.rs +++ b/src/pubsub/streaming_subscription.rs @@ -1,17 +1,24 @@ use std::{ convert::TryFrom, + future::Future, + mem, pin::Pin, + sync::Arc, task::{Context, Poll}, time::Duration, }; -use async_channel as mpmc; use futures::{ + future::{self, Either, FutureExt, TryFutureExt}, pin_mut, stream::{self, Stream}, StreamExt, }; use pin_project::pin_project; +use tokio::{ + sync::{mpsc, oneshot, Notify}, + time::error::Elapsed as TimeoutElapsed, +}; use tonic::metadata::MetadataValue; use tracing::{debug, trace_span, Instrument}; @@ -21,6 +28,14 @@ use crate::{ retry_policy::{exponential_backoff, ExponentialBackoff, RetryOperation, RetryPolicy}, }; +/// The maximum deadline supported by the RPC service +const MAX_DEADLINE_SEC: u32 = 600; + +/// The maximum number of acks or modacks in a single request +// 2500 as used in the go lib +// https://github.com/googleapis/google-cloud-go/blob/94d040898cc9e85fdac76560765b01cfd019d0b4/pubsub/iterator.go#L44-L52 +const MAX_ACK_BATCH_SIZE: usize = 2500; + config_default! { /// Configuration for a [streaming subscription](super::SubscriberClient::stream_subscription) /// request @@ -40,37 +55,41 @@ config_default! { pub max_outstanding_bytes: i64, /// Deprecated, subsumed by `max_outstanding_messages` + //TODO(0.12.0) remove deprecated field #[deprecated] @default(0, "StreamSubscriptionConfig::default_ack_channel_capacity") pub ack_channel_capacity: usize, } } -/// A user-initiated request to acknowledge, reject, or modify the deadline of some message -#[derive(Debug, PartialEq)] -enum UserAck { - Ack { id: String }, - Modify { id: String, seconds: i32 }, -} - /// An error encountered when issuing acks, nacks, or modifications from an /// [`AcknowledgeToken`](AcknowledgeToken) #[derive(Debug, Clone, Eq, PartialEq, thiserror::Error)] -#[error("cannot ack/nack/modify because the stream was dropped")] -pub struct AcknowledgeError { - _private: (), +#[error("failed to ack/nack/modify")] +pub struct AcknowledgeError(#[source] AckErr); + +#[derive(Debug, Clone, thiserror::Error)] +enum AckErr { + #[error("error in background task; check primary pull stream for errors")] + BackgroundTaskPanic, + + #[error(transparent)] + Request(tonic::Status), } -impl AcknowledgeError { - // SendErrors should only happen when the receiver is disconnected, which isn't called manually - // by the stream so should only happen on drop - fn from_send_err(_err: mpmc::SendError) -> Self { - AcknowledgeError { _private: () } +// TODO(0.12.0) remove eq/partialeq, use `matches!` in tests instead +impl PartialEq for AckErr { + fn eq(&self, other: &AckErr) -> bool { + use AckErr::*; + match (self, other) { + (BackgroundTaskPanic, BackgroundTaskPanic) => true, + (Request(status_a), Request(status_b)) => status_a.code() == status_b.code(), + _ => false, + } } } -/// The maximum deadline supported by the RPC service -const MAX_DEADLINE_SEC: u32 = 600; +impl Eq for AckErr {} /// An error encountered when issuing deadline modifications with /// [`AcknowledgeToken::modify_deadline`](AcknowledgeToken::modify_deadline) @@ -88,12 +107,31 @@ pub enum ModifyAcknowledgeError { }, } +#[derive(Debug)] +struct AckRouter { + // These channels are unbounded in a technical sense; however in practice there is a bound to + // the number of outstanding messages which pubsub will issue to a streaming caller. This will + // implicitly limit the ack/nack channel sizes. + // + // However modacks are not limited, as well as acks/nacks if a user sets both + // max_outstanding_bytes and max_outstanding_messages to zero (unbounded). Then it's up to the + // user to impose backpressure by awaiting the returned futures + acks: mpsc::UnboundedSender>, + nacks: mpsc::UnboundedSender>, + modacks: mpsc::UnboundedSender>, +} + +struct ModAck { + id: String, + deadline: i32, +} + /// A token with an associated message produced by the [`StreamSubscription`](StreamSubscription) /// stream, used to control that message's re-delivery within the message queue. #[derive(Debug)] pub struct AcknowledgeToken { id: String, - channel: mpmc::Sender, + router: Arc, delivery_attempt: i32, } @@ -103,12 +141,13 @@ impl AcknowledgeToken { /// /// Note that acknowledgements may not arrive to the service within the deadline (or at all), /// so this is only a best-effort means of preventing re-delivery. - pub async fn ack(self) -> Result<(), AcknowledgeError> { - self.channel - .send(UserAck::Ack { id: self.id }) - .await - .map_err(AcknowledgeError::from_send_err)?; - Ok(()) + /// + /// The returned future will complete once the acknowledge request has been sent. It is not + /// necessary to wait for the future's completion however; calling this function will initiate + /// an acknowledgement, which will finish even without awaiting the future. This can be useful + /// for callers that don't need explicit ack settlement and prefer to save latency. + pub fn ack(self) -> impl Future> + Send { + TokenFeedback::send(&self.router.acks, self.id) } /// Negatively acknowledge the corresponding message, requesting that the message service @@ -116,18 +155,10 @@ impl AcknowledgeToken { /// /// This may be useful if the message consumer encounters an error while processing the /// message. - pub async fn nack(self) -> Result<(), AcknowledgeError> { - self.channel - .send(UserAck::Modify { - id: self.id, - // "A NACK is any call to ModifyAckDeadline with a 0 deadline" - // see ReceivedMessage or ModifyAckDeadlineRequest rpc docs - seconds: 0, - }) - .await - .map_err(AcknowledgeError::from_send_err)?; - - Ok(()) + /// + /// The returned future need not be awaited, see [`AcknowledgeToken::ack`] + pub fn nack(self) -> impl Future> + Send { + TokenFeedback::send(&self.router.nacks, self.id) } /// Modify the acknowledgement deadline of the corresponding message, so that re-delivery is @@ -138,22 +169,30 @@ impl AcknowledgeToken { /// deadline, if for example the [subscription-level /// deadline](StreamSubscriptionConfig::stream_ack_deadline) is longer. A deadline of /// 0 seconds is equivalent to a [`nack`](AcknowledgeToken::nack) and will make the message - /// immediately available for re-delivery. + /// immediately available for re-delivery. The maximum deadline accepted by the service is 600 + /// seconds. /// - /// The maximum deadline accepted by the service is 600 seconds. - pub async fn modify_deadline(&mut self, seconds: u32) -> Result<(), ModifyAcknowledgeError> { + /// The returned future need not be awaited, see [`AcknowledgeToken::ack`] + pub fn modify_deadline( + &mut self, + seconds: u32, + ) -> impl Future> + Send { if seconds > MAX_DEADLINE_SEC { - return Err(ModifyAcknowledgeError::InvalidDeadline { seconds }); + return Either::Left(future::ready(Err( + ModifyAcknowledgeError::InvalidDeadline { seconds }, + ))); } - self.channel - .send(UserAck::Modify { - id: self.id.clone(), - seconds: i32::try_from(seconds).expect("deadline must fit in i32"), - }) - .await - .map_err(|err| ModifyAcknowledgeError::Modify(AcknowledgeError::from_send_err(err)))?; - Ok(()) + Either::Right( + TokenFeedback::send( + &self.router.modacks, + ModAck { + id: self.id.clone(), + deadline: seconds as i32, + }, + ) + .map_err(ModifyAcknowledgeError::Modify), + ) } /// The approximate number of times that Cloud Pub/Sub has attempted to deliver the associated @@ -165,6 +204,209 @@ impl AcknowledgeToken { } } +// Each ack token's ack/nack/modack calls are carried by this struct to the background task that +// polls the mpsc channels. That task will then notify the token of the request's completion by sending +// a result back over the given oneshot channel +struct TokenFeedback { + payload: T, + completion: oneshot::Sender>, +} + +impl TokenFeedback { + fn send( + channel: &mpsc::UnboundedSender>, + payload: T, + ) -> impl Future> + Send { + let (completion, listener) = oneshot::channel(); + + // send the payload over the channel synchronously. After this, the caller could drop the + // returned future and the work would still happen (barring errors/panics) + let send_result = channel.send(Self { + completion, + payload, + }); + + // now create the future to actually wait on the outcome + async move { + match send_result { + Ok(()) => match listener.await { + // if the background task completed a request with our payload, then + // we're ready to return a normal case to the user. Note this might still be a + // failed response, but the request completed a trip to the pubsub service + Ok(server_response) => return server_response, + + Err(oneshot::error::RecvError { .. }) => {} + }, + Err(mpsc::error::SendError { .. }) => {} + } + // Either SendError or RecvError imply that the other end of the channel was dropped. + // This means the background handler has stopped; if there are still senders open, that + // should only happen if a panic happened in that task + Err(AcknowledgeError(AckErr::BackgroundTaskPanic)) + } + } +} + +/// Wait for acks to arrive over the given channel, and send them to the server via the acknowledge +/// grpc method. +async fn handle_acks( + mut client: api::subscriber_client::SubscriberClient, + subscription: String, + mut acks: mpsc::UnboundedReceiver>, +) where + S: GrpcService + Send + 'static, + S::Future: Send + 'static, + S::Error: Into, + S::ResponseBody: Body + Send + 'static, + ::Error: Into + Send, +{ + let mut batch = Vec::new(); + loop { + let fetch_count = acks.recv_many(&mut batch, MAX_ACK_BATCH_SIZE).await; + if fetch_count == 0 { + // all senders dropped, pull stream must have closed + break; + } + let request = api::AcknowledgeRequest { + subscription: subscription.clone(), + ack_ids: batch + .iter_mut() + .map(|TokenFeedback { payload, .. }| mem::take(payload)) + .collect(), + }; + + let response = client + .acknowledge(request) + .await + .map(tonic::Response::into_inner) + .map_err(|err| AcknowledgeError(AckErr::Request(err))); + + let mut listeners = batch + .drain(..) + .map(|TokenFeedback { completion, .. }| completion); + + // peel off the first to avoid cloning in the common single-message case + let first = listeners.next().expect("fetched > 0"); + + // Send failures can only happen if the receiver was dropped. + // That's benign, the user isn't listening for ack responses. Ignore such failures + for listener in listeners { + let _ = listener.send(response.clone()); + } + let _ = first.send(response); + } +} + +/// much like handle_acks except includes ack_deadline_seconds and calls modify_ack_deadline. +// unfortunately hard to unify the two without proper async closures (or macros i guess) +async fn handle_nacks( + mut client: api::subscriber_client::SubscriberClient, + subscription: String, + mut nacks: mpsc::UnboundedReceiver>, +) where + S: GrpcService + Send + 'static, + S::Future: Send + 'static, + S::Error: Into, + S::ResponseBody: Body + Send + 'static, + ::Error: Into + Send, +{ + let mut batch = Vec::new(); + loop { + let fetch_count = nacks.recv_many(&mut batch, MAX_ACK_BATCH_SIZE).await; + if fetch_count == 0 { + break; + } + let request = api::ModifyAckDeadlineRequest { + subscription: subscription.clone(), + // zero seconds implies nack + ack_deadline_seconds: 0, + ack_ids: batch + .iter_mut() + .map(|TokenFeedback { payload, .. }| mem::take(payload)) + .collect(), + }; + + let response = client + .modify_ack_deadline(request) + .await + .map(tonic::Response::into_inner) + .map_err(|err| AcknowledgeError(AckErr::Request(err))); + + let mut listeners = batch + .drain(..) + .map(|TokenFeedback { completion, .. }| completion); + + let first = listeners.next().expect("fetched > 0"); + for listener in listeners { + let _ = listener.send(response.clone()); + } + let _ = first.send(response); + } +} + +async fn handle_modacks( + mut client: api::subscriber_client::SubscriberClient, + subscription: String, + mut modacks: mpsc::UnboundedReceiver>, +) where + S: GrpcService + Send + 'static, + S::Future: Send + 'static, + S::Error: Into, + S::ResponseBody: Body + Send + 'static, + ::Error: Into + Send, +{ + let mut batch = Vec::new(); + loop { + let fetch_count = modacks.recv_many(&mut batch, MAX_ACK_BATCH_SIZE).await; + if fetch_count == 0 { + break; + } + + // Unlike acks and nacks, each modack can have a different deadline. The request schema + // specifies a single deadline for all tokens in its batch. To resolve these two + // constraints, sort the batch into sections of identical deadlines, and send a batched + // request for each section. + // + // This is sorted in descending order because the below code pulls off the end. We want the + // shortest deadlines sent to the server first, on the theory that shorter deadlines imply + // tighter timing (though maybe it's irrelevant, deadlines are seconds while requests are + // hopefully millis) + batch.sort_by_key(|entry| std::cmp::Reverse(entry.payload.deadline)); + + // take sections off the end to avoid shifting values down as they're drained + while let Some(last_entry) = batch.last() { + let ack_deadline_seconds = last_entry.payload.deadline; + let section_start = + batch.partition_point(|entry| entry.payload.deadline > ack_deadline_seconds); + + let request = api::ModifyAckDeadlineRequest { + subscription: subscription.clone(), + ack_deadline_seconds, + ack_ids: batch[section_start..] + .iter_mut() + .map(|TokenFeedback { payload, .. }| mem::take(&mut payload.id)) + .collect(), + }; + + let response = client + .modify_ack_deadline(request) + .await + .map(tonic::Response::into_inner) + .map_err(|err| AcknowledgeError(AckErr::Request(err))); + + let mut listeners = batch + .drain(section_start..) + .map(|TokenFeedback { completion, .. }| completion); + + let first = listeners.next().expect("fetched > 0"); + for listener in listeners { + let _ = listener.send(response.clone()); + } + let _ = first.send(response); + } + } +} + /// Create the initial StreamingPullRequest which must include a Subscription string and a unique /// client_id. fn create_initial_streaming_pull_request( @@ -178,88 +420,67 @@ fn create_initial_streaming_pull_request( stream_ack_deadline_seconds: i32::try_from(config.stream_ack_deadline.as_secs()) .expect("ack deadline seconds should fit in i32"), max_outstanding_messages: config.max_outstanding_messages, - modify_deadline_seconds: Vec::default(), - modify_deadline_ack_ids: Vec::default(), max_outstanding_bytes: config.max_outstanding_bytes, - ack_ids: Vec::default(), + ..Default::default() } } /// Create the Nth StreamingPullRequest message (i.e. 2nd or later). -/// -/// Message should contain the previous message ids we want to acknowledge, and message ids for -/// messages whose deadlines should be modified. Nack'ing a message is accomplished by setting its -/// deadline to 0. fn create_subsequent_streaming_pull_request( - ack_ids: Vec, - modify_deadline_seconds: Vec, - modify_deadline_ack_ids: Vec, config: &StreamSubscriptionConfig, ) -> api::StreamingPullRequest { api::StreamingPullRequest { - ack_ids, - // subscription was set on the first request and is not set again. - subscription: String::default(), - // client_id was set on the first request and is not set again. - client_id: String::default(), // Even though this was set on the first request, it is reset on every subsequent request. // Here we "reset" it to the same value. stream_ack_deadline_seconds: i32::try_from(config.stream_ack_deadline.as_secs()) .expect("ack deadline seconds should fit in i32"), - // max_outstanding_messages was set on the first request and is not set again. - max_outstanding_messages: 0, - modify_deadline_seconds, - modify_deadline_ack_ids, - // max_outstanding_bytes was set on the first request and is not set again. - max_outstanding_bytes: 0, + ..Default::default() } } -/// Create never ending request stream of StreamingPullRequests. The first request initializes a new -/// gRPC stream and subsequent messages will acknowledge previous messages and request additional -/// ones. +/// Create an indefinite request stream of StreamingPullRequests. The first request initializes a new +/// gRPC stream and subsequent messages will keep the connection alive. +/// +/// The returned pair includes a stop indicator: after the value is dropped, the stream will stop +/// yielding elements and terminate. fn create_streaming_pull_request_stream( subscription: String, client_id: String, - user_acks: impl Stream, config: StreamSubscriptionConfig, -) -> impl Stream { - async_stream::stream! { +) -> (impl Stream, impl Drop) { + let stop_check = Arc::new(Notify::new()); + + struct StopOnDrop(Arc); + impl Drop for StopOnDrop { + fn drop(&mut self) { + self.0.notify_one(); + } + } + let stopper = StopOnDrop(Arc::clone(&stop_check)); + + let stream = async_stream::stream! { // start by issuing the first request yield create_initial_streaming_pull_request(subscription, client_id, &config); - // Subsequent requests come from user acknowledgements. - // Pull multiple acks at once in order to batch acks in the request. - // The size of batches is chosen to not exceed API restrictions - const MAX_PER_REQUEST_CHANGES: usize = 1000; // 1000 as used in the java lib - pin_mut!(user_acks); - let mut user_ack_batches = user_acks.ready_chunks(MAX_PER_REQUEST_CHANGES); - - while let Some(user_ack_batch) = user_ack_batches.next().await { - // collect the single stream of acks into individual lists depending on the ack type. - // pre-alloc only the ack list as the anticipated normal outcome - let mut acks = Vec::with_capacity(user_ack_batch.len()); - let mut deadlines = Vec::new(); - let mut deadline_acks = Vec::new(); - - for user_ack in user_ack_batch { - match user_ack { - UserAck::Ack { id } => acks.push(id), - UserAck::Modify { id, seconds } => { - deadlines.push(seconds); - deadline_acks.push(id); - } - }; - } + let should_stop = stop_check.notified(); + pin_mut!(should_stop); - yield create_subsequent_streaming_pull_request( - acks, - deadlines, - deadline_acks, - &config - ); + // Periodically send requests to keep the grpc connection active. This can help in cases + // where messages aren't being actively read (e.g. processing messages takes a long time). + // + // This does not send acks back over the stream, instead opting for explicit ack requests + // to have better feedback over ack completion/success/failure. + loop { + match tokio::time::timeout(Duration::from_secs(30), should_stop.as_mut()).await { + Ok(()) => break, + Err(TimeoutElapsed { .. }) => { + yield create_subsequent_streaming_pull_request(&config); + } + } } - } + }; + + (stream, stopper) } /// The stream returned by the @@ -283,7 +504,7 @@ pub struct StreamSubscription< /// could be called after streaming enum StreamState { Initialized { - client: api::subscriber_client::SubscriberClient, + client: [api::subscriber_client::SubscriberClient; 4], subscription: String, config: StreamSubscriptionConfig, retry_policy: R, @@ -297,7 +518,7 @@ enum StreamState { impl StreamSubscription { pub(super) fn new( - client: api::subscriber_client::SubscriberClient, + client: [api::subscriber_client::SubscriberClient; 4], subscription: String, config: StreamSubscriptionConfig, ) -> Self { @@ -421,7 +642,7 @@ where /// The stream will internally reconnect on error if the given retry policy indicates the /// error is retriable fn stream_from_client( - mut client: api::subscriber_client::SubscriberClient, + clients: [api::subscriber_client::SubscriberClient; 4], subscription: String, config: StreamSubscriptionConfig, mut retry_policy: R, @@ -442,25 +663,31 @@ where // the client id is used for stream reconnection on error let client_id = uuid::Uuid::new_v4().to_string(); - // Channel to send/receive message ids to ack. - // This needs to be a multi-producer channel as each message will get a sender handle to send - // its ack. In practice the receiver will only ever be polled as single-consumer; however there - // is a period of time during reconnection when two receivers might exist, because the - // disconnected stream is dropped in a background task at an unknown time (unknown to our - // layer anyway), and the new receiver should continue to pull from the existing senders. - let (sender, receiver) = mpmc::bounded( - usize::try_from(config.max_outstanding_messages) - .expect("outstanding messages should fit in usize"), - ); + let [mut client, ack_client, nack_client, modack_client] = clients; async_stream::stream! { let mut retry_op = None; + let (acks, acks_rx) = mpsc::unbounded_channel(); + let (nacks, nacks_rx) = mpsc::unbounded_channel(); + let (modacks, modacks_rx) = mpsc::unbounded_channel(); + let ack_router = Arc::new(AckRouter { acks, nacks, modacks }); + + // spawn the ack processing in the background. These should continue to process even + // when messages are not being pulled. + let ack_processor = tokio::spawn(future::join3( + handle_acks(ack_client, subscription.clone(), acks_rx), + handle_nacks(nack_client, subscription.clone(), nacks_rx), + handle_modacks(modack_client, subscription.clone(), modacks_rx), + )) + .unwrap_or_else(|join_err| std::panic::resume_unwind(join_err.into_panic())) + .map(|((), (), ())| ()); + pin_mut!(ack_processor); + 'reconnect: loop { - let request_stream = create_streaming_pull_request_stream( + let (request_stream, stream_drop_stopper) = create_streaming_pull_request_stream( subscription.clone(), client_id.clone(), - receiver.clone(), config, ); @@ -472,7 +699,15 @@ where { Err(err) => err, Ok(mut message_stream) => 'read: loop { - match message_stream.next().instrument(trace_span!("sub_stream_pull")).await { + // check if the background processor encountered any panics; + // if not, try to read messages from the stream. + let msg = message_stream.next().instrument(trace_span!("sub_stream_pull")); + pin_mut!(msg); + let next = future::poll_fn(|cx| match ack_processor.as_mut().poll(cx) { + Poll::Ready(()) => unreachable!("shouldn't complete while stream is active"), + Poll::Pending => msg.as_mut().poll(cx) + }); + match next.await { // If the stream is out of elements, some connection must have been closed. // However PubSub docs say StreamingPull always terminates with an error, // so this normal end-of-stream shouldn't happen, and instead should fall @@ -495,7 +730,7 @@ where for message in response.received_messages { let ack_token = AcknowledgeToken { id: message.ack_id, - channel: sender.clone(), + router: Arc::clone(&ack_router), delivery_attempt: message.delivery_attempt, }; let message = match message.message { @@ -512,7 +747,8 @@ where } } }; - debug!("Stream ended"); + std::mem::drop(stream_drop_stopper); + debug!(%client_id, "Stream ended"); // if either the streaming connection or a stream element produces an error, // the error will arrive here. @@ -544,204 +780,833 @@ where #[cfg(test)] mod test { - use rand::Rng; - use super::*; + use std::sync::Mutex; + use tonic::Code; #[test] - fn streaming_pull_request_stream() { - let subscription = "test-subscription"; - let client_id = "test-id"; - - let (mut sender, receiver) = futures::channel::mpsc::unbounded(); - - let requests = create_streaming_pull_request_stream( - subscription.into(), - client_id.into(), - receiver, - StreamSubscriptionConfig { - max_outstanding_messages: 2000, - max_outstanding_bytes: 3000, - stream_ack_deadline: Duration::from_secs(20), - ..Default::default() - }, - ); - pin_mut!(requests); + fn token_send() { + let mut cx = Context::from_waker(futures::task::noop_waker_ref()); + let (send, mut recv) = mpsc::unbounded_channel(); + let mut fut = TokenFeedback::send(&send, "hello world").boxed(); + + // without any poll, the item should already be sent over the channel + let TokenFeedback { + payload, + completion, + } = recv.try_recv().expect("send should be synchronous"); + assert_eq!(payload, "hello world"); + + // the future should now be waiting for a response on the completion channel + assert!(matches!(fut.as_mut().poll(&mut cx), Poll::Pending)); + completion.send(Ok(())).expect("oneshot is open"); + assert!(matches!(fut.as_mut().poll(&mut cx), Poll::Ready(Ok(())))); + + // setup another send to witness an error response + let mut fut = TokenFeedback::send(&send, "abc123").boxed(); + let TokenFeedback { completion, .. } = recv.try_recv().expect("send should be synchronous"); + assert!(matches!(fut.as_mut().poll(&mut cx), Poll::Pending)); + completion + .send(Err(AcknowledgeError(AckErr::BackgroundTaskPanic))) + .expect("oneshot is open"); + assert!(matches!( + fut.as_mut().poll(&mut cx), + Poll::Ready(Err(AcknowledgeError(AckErr::BackgroundTaskPanic))) + )); + } + #[test] + fn token_wait() { + let mut cx = Context::from_waker(futures::task::noop_waker_ref()); + let (send, mut recv) = mpsc::unbounded_channel(); + + let mut fut = TokenFeedback::send(&send, "hello world").boxed(); + let TokenFeedback { completion, .. } = recv.try_recv().expect("send should be synchronous"); + + // if the completion gets dropped, the waiting future should resolve with an error + std::mem::drop(completion); + assert!(matches!( + fut.as_mut().poll(&mut cx), + Poll::Ready(Err(AcknowledgeError(AckErr::BackgroundTaskPanic))) + )); + + // that also applies if the future is already polling + let mut fut = TokenFeedback::send(&send, "hello world").boxed(); + let TokenFeedback { completion, .. } = recv.try_recv().expect("send should be synchronous"); + assert!(matches!(fut.as_mut().poll(&mut cx), Poll::Pending)); + std::mem::drop(completion); + assert!(matches!( + fut.as_mut().poll(&mut cx), + Poll::Ready(Err(AcknowledgeError(AckErr::BackgroundTaskPanic))) + )); + + // start a send which will encounter a receiver drop + let mut fut = TokenFeedback::send(&send, "hello world").boxed(); + assert!(matches!(fut.as_mut().poll(&mut cx), Poll::Pending)); + std::mem::drop(recv); + assert!(matches!( + fut.as_mut().poll(&mut cx), + Poll::Ready(Err(AcknowledgeError(AckErr::BackgroundTaskPanic))) + )); + + // a send started after the receiver drop should also fail + let mut fut = TokenFeedback::send(&send, "hello world").boxed(); + assert!(matches!( + fut.as_mut().poll(&mut cx), + Poll::Ready(Err(AcknowledgeError(AckErr::BackgroundTaskPanic))) + )); + } + + #[test] + fn ack_handling() { let mut cx = Context::from_waker(futures::task::noop_waker_ref()); + #[derive(Default, Clone)] + struct MockSubscriberServer { + acks: Arc>>, + injected_errors: Arc>>, + } + + #[tonic::codegen::async_trait] + impl api::subscriber_server::Subscriber for MockSubscriberServer { + async fn acknowledge( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status> { + self.acks.lock().unwrap().push(request.into_inner()); + + let mut errs = self.injected_errors.lock().unwrap(); + if errs.is_empty() { + Ok(tonic::Response::new(())) + } else { + Err(errs.remove(0)) + } + } + } + + let (ack_send, recv) = mpsc::unbounded_channel(); + let server = MockSubscriberServer::default(); + let take_server_acks = || server.acks.lock().unwrap().drain(..).collect::>(); + + let mut ack_handler = handle_acks( + api::subscriber_client::SubscriberClient::new( + api::subscriber_server::SubscriberServer::new(server.clone()), + ), + "test-subscription".into(), + recv, + ) + .boxed(); + + // simple single ack case + let mut ack_fut = TokenFeedback::send(&ack_send, "ack-id1".into()).boxed(); + // drive ack handler + assert!(matches!(ack_handler.as_mut().poll(&mut cx), Poll::Pending)); + + // check that the server got the request + assert_eq!( + take_server_acks(), + vec![api::AcknowledgeRequest { + subscription: "test-subscription".into(), + ack_ids: vec!["ack-id1".into()], + }] + ); + // and that the ack token got its response + assert!(matches!( + ack_fut.as_mut().poll(&mut cx), + Poll::Ready(Ok(())) + )); + + // send multiple acks before the handler polls again + let mut ack_fut = future::join3( + TokenFeedback::send(&ack_send, "ack-id2".into()), + TokenFeedback::send(&ack_send, "ack-id3".into()), + TokenFeedback::send(&ack_send, "ack-id4".into()), + ) + .boxed(); + assert!(matches!(ack_handler.as_mut().poll(&mut cx), Poll::Pending)); + + // check that the server got a single buffered request + assert_eq!( + take_server_acks(), + vec![api::AcknowledgeRequest { + subscription: "test-subscription".into(), + ack_ids: vec!["ack-id2".into(), "ack-id3".into(), "ack-id4".into()], + }] + ); - // the request stream always starts with an initialized first request + // and that all the futures got responses + assert!(matches!( + ack_fut.as_mut().poll(&mut cx), + Poll::Ready((Ok(()), Ok(()), Ok(()))) + )); + + // send multiple acks again + let mut ack_fut = future::join3( + TokenFeedback::send(&ack_send, "ack-id5".into()), + TokenFeedback::send(&ack_send, "ack-id6".into()), + TokenFeedback::send(&ack_send, "ack-id7".into()), + ) + .boxed(); + // however this time inject an error response from the server + server + .injected_errors + .lock() + .unwrap() + .push(tonic::Status::aborted("injected-error")); + + // drive the ack handler + assert!(matches!(ack_handler.as_mut().poll(&mut cx), Poll::Pending)); assert_eq!( - Poll::Ready(Some(api::StreamingPullRequest { - subscription: subscription.into(), - ack_ids: vec![], - modify_deadline_seconds: vec![], - modify_deadline_ack_ids: vec![], - stream_ack_deadline_seconds: 20, - client_id: client_id.into(), - max_outstanding_messages: 2000, - max_outstanding_bytes: 3000, - })), - requests.as_mut().poll_next(&mut cx) + take_server_acks(), + vec![api::AcknowledgeRequest { + subscription: "test-subscription".into(), + ack_ids: vec!["ack-id5".into(), "ack-id6".into(), "ack-id7".into()], + }] ); - // no subsequent requests until a message is available on the channel - assert_eq!(Poll::Pending, requests.as_mut().poll_next(&mut cx)); + // the ack tokens should each get back the error + let ack_responses = ack_fut.as_mut().poll(&mut cx); + match ack_responses { + Poll::Ready(( + Err(AcknowledgeError(AckErr::Request(status1))), + Err(AcknowledgeError(AckErr::Request(status2))), + Err(AcknowledgeError(AckErr::Request(status3))), + )) if ( + (status1.code(), status2.code(), status3.code()), + (status1.message(), status2.message(), status3.message()), + ) == ( + (Code::Aborted, Code::Aborted, Code::Aborted), + ("injected-error", "injected-error", "injected-error"), + ) => {} + _ => panic!("unexpected future output {ack_responses:?}"), + }; + + // if more than the batch limit is submitted, the handler will send multiple requests + let futs = (0..(MAX_ACK_BATCH_SIZE + 2)) + .map(|i| TokenFeedback::send(&ack_send, format!("mass-ack{i}"))) + .collect::>(); - // send 1 message ack + assert!(matches!(ack_handler.as_mut().poll(&mut cx), Poll::Pending)); + let server_acks = take_server_acks(); + assert_eq!(server_acks.len(), 2); assert_eq!( - Ok(()), - sender.unbounded_send(UserAck::Ack { id: "1st".into() }) + server_acks[0].ack_ids, + (0..MAX_ACK_BATCH_SIZE) + .map(|i| format!("mass-ack{i}")) + .collect::>() ); - // the output stream should be eager, and produce a request if only 1 message is available + const _SANITY_CHECK: [(); 2500] = [(); MAX_ACK_BATCH_SIZE]; assert_eq!( - Poll::Ready(Some(api::StreamingPullRequest { - subscription: String::new(), - ack_ids: vec!["1st".into()], - modify_deadline_seconds: vec![], - modify_deadline_ack_ids: vec![], - stream_ack_deadline_seconds: 20, - client_id: String::new(), - max_outstanding_messages: 0, - max_outstanding_bytes: 0, - })), - requests.as_mut().poll_next(&mut cx) + server_acks[1].ack_ids, + vec!["mass-ack2500".to_owned(), "mass-ack2501".to_owned()] ); - assert_eq!(Poll::Pending, requests.as_mut().poll_next(&mut cx)); - // send 3 message acks/modifies + // all the futures should get their success response + for fut in futs { + assert!(matches!( + fut.boxed().as_mut().poll(&mut cx), + Poll::Ready(Ok(())) + )); + } + + // the handler future can complete after the sender side is dropped. + // however it must first flush any acks still in the queue. + let mut ack_fut = TokenFeedback::send(&ack_send, "ack-id99".into()).boxed(); + std::mem::drop(ack_send); + assert!(take_server_acks().is_empty()); // sanity check + assert!(matches!( + ack_handler.as_mut().poll(&mut cx), + Poll::Ready(()) + )); assert_eq!( - Ok(()), - sender.unbounded_send(UserAck::Ack { id: "2nd".into() }) + take_server_acks(), + vec![api::AcknowledgeRequest { + subscription: "test-subscription".into(), + ack_ids: vec!["ack-id99".into()] + }] ); + assert!(matches!( + ack_fut.as_mut().poll(&mut cx), + Poll::Ready(Ok(())) + )); + } + + // copy-paste of ack handler. practically identical functionality + #[test] + fn nack_handling() { + let mut cx = Context::from_waker(futures::task::noop_waker_ref()); + #[derive(Default, Clone)] + struct MockSubscriberServer { + acks: Arc>>, + injected_errors: Arc>>, + } + + #[tonic::codegen::async_trait] + impl api::subscriber_server::Subscriber for MockSubscriberServer { + async fn modify_ack_deadline( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status> { + self.acks.lock().unwrap().push(request.into_inner()); + + let mut errs = self.injected_errors.lock().unwrap(); + if errs.is_empty() { + Ok(tonic::Response::new(())) + } else { + Err(errs.remove(0)) + } + } + } + + let (ack_send, recv) = mpsc::unbounded_channel(); + let server = MockSubscriberServer::default(); + let take_server_acks = || server.acks.lock().unwrap().drain(..).collect::>(); + + let mut ack_handler = handle_nacks( + api::subscriber_client::SubscriberClient::new( + api::subscriber_server::SubscriberServer::new(server.clone()), + ), + "test-subscription".into(), + recv, + ) + .boxed(); + + // simple single ack case + let mut ack_fut = TokenFeedback::send(&ack_send, "ack-id1".into()).boxed(); + // drive ack handler + assert!(matches!(ack_handler.as_mut().poll(&mut cx), Poll::Pending)); + + // check that the server got the request assert_eq!( - Ok(()), - sender.unbounded_send(UserAck::Modify { - id: "3rd".into(), - seconds: 13 - }) + take_server_acks(), + vec![api::ModifyAckDeadlineRequest { + subscription: "test-subscription".into(), + ack_ids: vec!["ack-id1".into()], + ack_deadline_seconds: 0, + }] ); + // and that the ack token got its response + assert!(matches!( + ack_fut.as_mut().poll(&mut cx), + Poll::Ready(Ok(())) + )); + + // send multiple acks before the handler polls again + let mut ack_fut = future::join3( + TokenFeedback::send(&ack_send, "ack-id2".into()), + TokenFeedback::send(&ack_send, "ack-id3".into()), + TokenFeedback::send(&ack_send, "ack-id4".into()), + ) + .boxed(); + assert!(matches!(ack_handler.as_mut().poll(&mut cx), Poll::Pending)); + + // check that the server got a single buffered request assert_eq!( - Ok(()), - sender.unbounded_send(UserAck::Ack { id: "4th".into() }) + take_server_acks(), + vec![api::ModifyAckDeadlineRequest { + subscription: "test-subscription".into(), + ack_ids: vec!["ack-id2".into(), "ack-id3".into(), "ack-id4".into()], + ack_deadline_seconds: 0, + }] ); + + // and that all the futures got responses + assert!(matches!( + ack_fut.as_mut().poll(&mut cx), + Poll::Ready((Ok(()), Ok(()), Ok(()))) + )); + + // send multiple acks again + let mut ack_fut = future::join3( + TokenFeedback::send(&ack_send, "ack-id5".into()), + TokenFeedback::send(&ack_send, "ack-id6".into()), + TokenFeedback::send(&ack_send, "ack-id7".into()), + ) + .boxed(); + // however this time inject an error response from the server + server + .injected_errors + .lock() + .unwrap() + .push(tonic::Status::aborted("injected-error")); + + // drive the ack handler + assert!(matches!(ack_handler.as_mut().poll(&mut cx), Poll::Pending)); assert_eq!( - Ok(()), - sender.unbounded_send(UserAck::Modify { - id: "5th".into(), - seconds: 15 - }) + take_server_acks(), + vec![api::ModifyAckDeadlineRequest { + subscription: "test-subscription".into(), + ack_ids: vec!["ack-id5".into(), "ack-id6".into(), "ack-id7".into()], + ack_deadline_seconds: 0, + }] ); - // the output stream should buffer when many acks/modifies are available + // the ack tokens should each get back the error + let ack_responses = ack_fut.as_mut().poll(&mut cx); + match ack_responses { + Poll::Ready(( + Err(AcknowledgeError(AckErr::Request(status1))), + Err(AcknowledgeError(AckErr::Request(status2))), + Err(AcknowledgeError(AckErr::Request(status3))), + )) if ( + (status1.code(), status2.code(), status3.code()), + (status1.message(), status2.message(), status3.message()), + ) == ( + (Code::Aborted, Code::Aborted, Code::Aborted), + ("injected-error", "injected-error", "injected-error"), + ) => {} + _ => panic!("unexpected future output {ack_responses:?}"), + }; + + // if more than the batch limit is submitted, the handler will send multiple requests + let futs = (0..(MAX_ACK_BATCH_SIZE + 2)) + .map(|i| TokenFeedback::send(&ack_send, format!("mass-ack{i}"))) + .collect::>(); + + assert!(matches!(ack_handler.as_mut().poll(&mut cx), Poll::Pending)); + let server_acks = take_server_acks(); + assert_eq!(server_acks.len(), 2); assert_eq!( - Poll::Ready(Some(api::StreamingPullRequest { - subscription: String::new(), - ack_ids: vec!["2nd".into(), "4th".into()], - modify_deadline_seconds: vec![13, 15], - modify_deadline_ack_ids: vec!["3rd".into(), "5th".into()], - stream_ack_deadline_seconds: 20, - client_id: String::new(), - max_outstanding_messages: 0, - max_outstanding_bytes: 0, - })), - requests.as_mut().poll_next(&mut cx) + server_acks[0].ack_ids, + (0..MAX_ACK_BATCH_SIZE) + .map(|i| format!("mass-ack{i}")) + .collect::>() ); - assert_eq!(Poll::Pending, requests.as_mut().poll_next(&mut cx)); - - // the output buffering has a limit of 1000. if more messages are immediately available, - // they will be sent in multiple requests. - - // generate acks/modifies with random interleaving - let inputs = std::iter::repeat_with(|| rand::thread_rng().gen::()) - .enumerate() - .skip(6) // skip 0th,1st..5th - .map(|(index, is_modify)| { - let id = index.to_string() + "th"; - if is_modify { - UserAck::Modify { - id, - seconds: index as i32, - } + + const _SANITY_CHECK: [(); 2500] = [(); MAX_ACK_BATCH_SIZE]; + assert_eq!( + server_acks[1].ack_ids, + vec!["mass-ack2500".to_owned(), "mass-ack2501".to_owned()] + ); + + // all the futures should get their success response + for fut in futs { + assert!(matches!( + fut.boxed().as_mut().poll(&mut cx), + Poll::Ready(Ok(())) + )); + } + + // the handler future can complete after the sender side is dropped. + // however it must first flush any acks still in the queue. + let mut ack_fut = TokenFeedback::send(&ack_send, "ack-id99".into()).boxed(); + std::mem::drop(ack_send); + assert!(take_server_acks().is_empty()); // sanity check + assert!(matches!( + ack_handler.as_mut().poll(&mut cx), + Poll::Ready(()) + )); + assert_eq!( + take_server_acks(), + vec![api::ModifyAckDeadlineRequest { + subscription: "test-subscription".into(), + ack_ids: vec!["ack-id99".into()], + ack_deadline_seconds: 0, + }] + ); + assert!(matches!( + ack_fut.as_mut().poll(&mut cx), + Poll::Ready(Ok(())) + )); + } + + // *NOT* a (direct) copy-paste of ack handler, accounting for multiple deadlines. still mostly + // a copy though... + #[test] + fn modack_handling() { + let mut cx = Context::from_waker(futures::task::noop_waker_ref()); + #[derive(Default, Clone)] + struct MockSubscriberServer { + acks: Arc>>, + injected_errors: Arc>>, + } + + #[tonic::codegen::async_trait] + impl api::subscriber_server::Subscriber for MockSubscriberServer { + async fn modify_ack_deadline( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status> { + self.acks.lock().unwrap().push(request.into_inner()); + + let mut errs = self.injected_errors.lock().unwrap(); + if errs.is_empty() { + Ok(tonic::Response::new(())) } else { - UserAck::Ack { id } + Err(errs.remove(0)) } - }) - .take(1001) - .collect::>(); + } + } - let filter_acks = |acks: &[UserAck]| { - acks.iter() - .filter_map(|ack| match ack { - UserAck::Ack { id } => Some(id.clone()), - _ => None, - }) - .collect::>() - }; + let (ack_send, recv) = mpsc::unbounded_channel(); + let server = MockSubscriberServer::default(); + let take_server_acks = || server.acks.lock().unwrap().drain(..).collect::>(); + + let mut ack_handler = handle_modacks( + api::subscriber_client::SubscriberClient::new( + api::subscriber_server::SubscriberServer::new(server.clone()), + ), + "test-subscription".into(), + recv, + ) + .boxed(); + + // simple single ack case + let mut ack_fut = TokenFeedback::send( + &ack_send, + ModAck { + id: "ack-id1".into(), + deadline: 1, + }, + ) + .boxed(); + // drive ack handler + assert!(matches!(ack_handler.as_mut().poll(&mut cx), Poll::Pending)); - let filter_modifies = |acks: &[UserAck]| { - acks.iter() - .filter_map(|ack| match ack { - UserAck::Modify { id, seconds } => Some((id.clone(), *seconds)), - _ => None, - }) - .collect::>() - }; + // check that the server got the request + assert_eq!( + take_server_acks(), + vec![api::ModifyAckDeadlineRequest { + subscription: "test-subscription".into(), + ack_ids: vec!["ack-id1".into()], + ack_deadline_seconds: 1, + }] + ); + // and that the ack token got its response + assert!(matches!( + ack_fut.as_mut().poll(&mut cx), + Poll::Ready(Ok(())) + )); + + // send multiple acks before the handler polls again. + // note these have varying deadlines. The handler may only batch together acks with the same + // deadline + let mut ack_fut = future::join5( + TokenFeedback::send( + &ack_send, + ModAck { + id: "ack-id2".into(), + deadline: 2, + }, + ), + TokenFeedback::send( + &ack_send, + ModAck { + id: "ack-id3".into(), + deadline: 5, + }, + ), + TokenFeedback::send( + &ack_send, + ModAck { + id: "ack-id4".into(), + deadline: 2, + }, + ), + TokenFeedback::send( + &ack_send, + ModAck { + id: "ack-id5".into(), + deadline: 5, + }, + ), + TokenFeedback::send( + &ack_send, + ModAck { + id: "ack-id6".into(), + deadline: 1, + }, + ), + ) + .boxed(); + assert!(matches!(ack_handler.as_mut().poll(&mut cx), Poll::Pending)); - let expected_first_batch = api::StreamingPullRequest { - subscription: String::new(), - ack_ids: filter_acks(&inputs[..1000]), - modify_deadline_seconds: filter_modifies(&inputs[..1000]) - .into_iter() - .map(|tup| tup.1) - .collect(), - modify_deadline_ack_ids: filter_modifies(&inputs[..1000]) - .into_iter() - .map(|tup| tup.0) - .collect(), - stream_ack_deadline_seconds: 20, - client_id: String::new(), - max_outstanding_messages: 0, - max_outstanding_bytes: 0, - }; + // the server should have gotten separate requests for each deadline, in increasing + // order by deadline. + assert_eq!( + take_server_acks(), + vec![ + api::ModifyAckDeadlineRequest { + subscription: "test-subscription".into(), + ack_ids: vec!["ack-id6".into()], + ack_deadline_seconds: 1, + }, + api::ModifyAckDeadlineRequest { + subscription: "test-subscription".into(), + ack_ids: vec!["ack-id2".into(), "ack-id4".into()], + ack_deadline_seconds: 2, + }, + api::ModifyAckDeadlineRequest { + subscription: "test-subscription".into(), + ack_ids: vec!["ack-id3".into(), "ack-id5".into()], + ack_deadline_seconds: 5, + }, + ] + ); - let expected_second_batch = api::StreamingPullRequest { - subscription: String::new(), - ack_ids: filter_acks(&inputs[1000..]), - modify_deadline_seconds: filter_modifies(&inputs[1000..]) - .into_iter() - .map(|tup| tup.1) - .collect(), - modify_deadline_ack_ids: filter_modifies(&inputs[1000..]) - .into_iter() - .map(|tup| tup.0) - .collect(), - stream_ack_deadline_seconds: 20, - client_id: String::new(), - max_outstanding_messages: 0, - max_outstanding_bytes: 0, + // check that all the futures got responses + assert!(matches!( + ack_fut.as_mut().poll(&mut cx), + Poll::Ready((Ok(()), Ok(()), Ok(()), Ok(()), Ok(()))) + )); + + // send multiple acks again + let mut ack_fut = future::join3( + TokenFeedback::send( + &ack_send, + ModAck { + id: "ack-id7".into(), + deadline: 1, + }, + ), + TokenFeedback::send( + &ack_send, + ModAck { + id: "ack-id8".into(), + deadline: 1, + }, + ), + TokenFeedback::send( + &ack_send, + ModAck { + id: "ack-id9".into(), + deadline: 1, + }, + ), + ) + .boxed(); + // however this time inject an error response from the server + server + .injected_errors + .lock() + .unwrap() + .push(tonic::Status::aborted("injected-error")); + + // drive the ack handler + assert!(matches!(ack_handler.as_mut().poll(&mut cx), Poll::Pending)); + assert_eq!( + take_server_acks(), + vec![api::ModifyAckDeadlineRequest { + subscription: "test-subscription".into(), + ack_ids: vec!["ack-id7".into(), "ack-id8".into(), "ack-id9".into()], + ack_deadline_seconds: 1, + },] + ); + + // the ack tokens should each get back the error + let ack_responses = ack_fut.as_mut().poll(&mut cx); + match ack_responses { + Poll::Ready(( + Err(AcknowledgeError(AckErr::Request(status1))), + Err(AcknowledgeError(AckErr::Request(status2))), + Err(AcknowledgeError(AckErr::Request(status3))), + )) if ( + (status1.code(), status2.code(), status3.code()), + (status1.message(), status2.message(), status3.message()), + ) == ( + (Code::Aborted, Code::Aborted, Code::Aborted), + ("injected-error", "injected-error", "injected-error"), + ) => {} + _ => panic!("unexpected future output {ack_responses:?}"), }; + // if more than the batch limit is submitted, the handler will send multiple requests. + let futs = (0..(MAX_ACK_BATCH_SIZE + 2)) + .map(|i| { + TokenFeedback::send( + &ack_send, + ModAck { + id: format!("mass-ack{i}"), + deadline: 1, + }, + ) + }) + .collect::>(); + + assert!(matches!(ack_handler.as_mut().poll(&mut cx), Poll::Pending)); + let server_acks = take_server_acks(); + assert_eq!(server_acks.len(), 2); assert_eq!( - Ok(()), - inputs - .into_iter() - .try_for_each(|ack| sender.unbounded_send(ack)) + server_acks[0].ack_ids, + (0..MAX_ACK_BATCH_SIZE) + .map(|i| format!("mass-ack{i}")) + .collect::>() ); + const _SANITY_CHECK: [(); 2500] = [(); MAX_ACK_BATCH_SIZE]; assert_eq!( - Poll::Ready(Some(expected_first_batch)), - requests.as_mut().poll_next(&mut cx) + server_acks[1].ack_ids, + vec!["mass-ack2500".to_owned(), "mass-ack2501".to_owned()] ); + + // all the futures should get their success response + for fut in futs { + assert!(matches!( + fut.boxed().as_mut().poll(&mut cx), + Poll::Ready(Ok(())) + )); + } + + // the handler future can complete after the sender side is dropped. + // however it must first flush any acks still in the queue. + let mut ack_fut = TokenFeedback::send( + &ack_send, + ModAck { + id: "ack-id99".into(), + deadline: 2, + }, + ) + .boxed(); + std::mem::drop(ack_send); + assert!(take_server_acks().is_empty()); // sanity check + assert!(matches!( + ack_handler.as_mut().poll(&mut cx), + Poll::Ready(()) + )); assert_eq!( - Poll::Ready(Some(expected_second_batch)), - requests.as_mut().poll_next(&mut cx) + take_server_acks(), + vec![api::ModifyAckDeadlineRequest { + subscription: "test-subscription".into(), + ack_ids: vec!["ack-id99".into()], + ack_deadline_seconds: 2, + }] ); - assert_eq!(Poll::Pending, requests.as_mut().poll_next(&mut cx)); + assert!(matches!( + ack_fut.as_mut().poll(&mut cx), + Poll::Ready(Ok(())) + )); + } + + #[tokio::test] + async fn streaming_reqs_stop_drop() { + let mut cx = Context::from_waker(futures::task::noop_waker_ref()); + let (stream, stop_drop) = create_streaming_pull_request_stream( + "test-subscription".into(), + "test-client".into(), + StreamSubscriptionConfig::default(), + ); + let mut stream = stream.boxed(); + + // first call always yield the first element + assert!(matches!( + stream.as_mut().poll_next(&mut cx), + Poll::Ready(Some(api::StreamingPullRequest { .. })) + )); + + // then a periodic element waits for time + tokio::time::pause(); + assert!(matches!(stream.as_mut().poll_next(&mut cx), Poll::Pending)); + tokio::time::advance(Duration::from_secs(31)).await; + assert!(matches!( + stream.as_mut().poll_next(&mut cx), + Poll::Ready(Some(api::StreamingPullRequest { .. })) + )); + // pending on the next one + assert!(matches!(stream.as_mut().poll_next(&mut cx), Poll::Pending)); + + // however dropping the notifier should wake the stream and end it + std::mem::drop(stop_drop); + assert!(matches!( + stream.as_mut().poll_next(&mut cx), + Poll::Ready(None) + )); + } + + // panics in the background ack-handler task should be forwarded _somewhere_ + // ideally it would be to the issuing ack tokens, but it's much easier to pass to the message + // streamer + #[tokio::test] + async fn background_panic_forwarded() { + use std::panic; + let mut cx = Context::from_waker(futures::task::noop_waker_ref()); + + #[derive(Default, Clone)] + struct MockSubscriberServer {} + + #[tonic::codegen::async_trait] + impl api::subscriber_server::Subscriber for MockSubscriberServer { + async fn acknowledge( + &self, + _request: tonic::Request, + ) -> std::result::Result, tonic::Status> { + panic!("injected test panic"); + } + + async fn streaming_pull( + &self, + _request: tonic::Request>, + ) -> std::result::Result< + tonic::Response>, + tonic::Status, + > { + // send one message in order to provide an ack token. after that, produce no + // additional messages but don't end the stream, just hang basically + Ok(tonic::Response::new( + async_stream::stream! { + yield Ok(api::StreamingPullResponse { + received_messages: vec![api::ReceivedMessage { + ack_id: "ack1".into(), + delivery_attempt: 1, + message: Some(api::PubsubMessage { + data: vec![0u8; 16].into(), + ..Default::default() + }) + }], + ..Default::default() + }); + + future::pending::<()>().await; + } + .boxed(), + )) + } + } + + let server = MockSubscriberServer::default(); + + let mut stream = stream_from_client( + std::array::from_fn(|_| { + api::subscriber_client::SubscriberClient::new( + api::subscriber_server::SubscriberServer::new(server.clone()), + ) + }), + "test-subscription".into(), + StreamSubscriptionConfig::default(), + ExponentialBackoff::new(PubSubRetryCheck::default(), Default::default()), + ) + .boxed(); + + // pull the first message out to get an ack token + let ack_token = match stream.as_mut().poll_next(&mut cx) { + Poll::Ready(Some(Ok((ack_token, _message)))) => ack_token, + other => panic!("unexpected stream value {other:?}"), + }; + + // the stream is otherwise empty for now + assert!(matches!(stream.as_mut().poll_next(&mut cx), Poll::Pending)); + + // start an ack from the first message's token. this should trigger a panic in the + // background ack-handler task once it calls the mocked `acknowledge` function + let mut ack_fut = ack_token.ack().boxed(); + + // give the tokio test runtime an opportunity to run background tasks + tokio::task::yield_now().await; + + // the stream's poll should now forward that panic to the caller + match panic::catch_unwind(panic::AssertUnwindSafe(|| { + stream.as_mut().poll_next(&mut cx) + })) { + Ok(poll) => panic!("stream did not panic when expected, instead produced {poll:?}"), + Err(panic_cause) => match panic_cause.downcast::<&'static str>() { + Ok(text) => assert_eq!(*text, "injected test panic"), + Err(_) => panic!("unexpected panic contents"), + }, + } - // the request stream should end when the input stream ends - sender.disconnect(); - assert_eq!(Poll::Ready(None), requests.as_mut().poll_next(&mut cx)); + // and the ack future is informed that an error occurred + assert!(matches!( + ack_fut.as_mut().poll(&mut cx), + Poll::Ready(Err(AcknowledgeError(AckErr::BackgroundTaskPanic))) + )); } } diff --git a/tests/pubsub_client.rs b/tests/pubsub_client.rs index a293c54..3cd3cd8 100644 --- a/tests/pubsub_client.rs +++ b/tests/pubsub_client.rs @@ -949,78 +949,68 @@ mod pubsub_client_tests { } /// 1) Modifying a deadline with too great a deadline should be an error - /// 2) Ack'ing a message should error after the request stream is dropped - #[test] - fn ack_errors() { - let runtime = tokio::runtime::Runtime::new().unwrap(); - - // Interestingly, it appears that tonic spawns the request stream onto the runtime in some - // way, and that dropping the output stream or even the subscriber client does not drop - // the request stream. Instead, we have to drop the entire runtime in order to drop the - // background task which holds the ack request stream - - let mut ack_token = runtime.block_on(async { - let topic_name = "test-topic"; - let subscription_name = "test-subscription"; - - let emulator = Emulator::new().project("test-project").await.unwrap(); - let config = pubsub::PubSubConfig::new().endpoint(emulator.endpoint()); - - let mut publish_client = emulator - .builder() - .build_pubsub_publisher(config.clone()) - .await - .unwrap(); + /// 2) Ack'ing a message still works after the request stream is dropped + #[tokio::test] + async fn ack_errors() { + let topic_name = "test-topic"; + let subscription_name = "test-subscription"; - // Create a topic to query. - let topic = create_dummy_topic(&mut publish_client, emulator.project(), topic_name) - .await - .unwrap() - .into_inner(); + let emulator = Emulator::new().project("test-project").await.unwrap(); + let config = pubsub::PubSubConfig::new().endpoint(emulator.endpoint()); - let mut subscription_client = emulator - .builder() - .build_pubsub_subscriber(config.clone()) - .await - .unwrap(); + let mut publish_client = emulator + .builder() + .build_pubsub_publisher(config.clone()) + .await + .unwrap(); - // Create a subscription to query. Must be created before messages are published. - let _subscription = create_dummy_subscription( - &mut subscription_client, - emulator.project(), - subscription_name, - topic_name, - ) + // Create a topic to query. + let topic = create_dummy_topic(&mut publish_client, emulator.project(), topic_name) .await .unwrap() .into_inner(); - // send 1 message to the publisher to get an ack token - publish_client - .raw_api_mut() - .publish({ - let mut p = pubsub::api::PublishRequest::default(); - p.topic = topic.name; - p.messages = vec![{ - let mut m = pubsub::api::PubsubMessage::default(); - m.data = "foobar".into(); - m - }]; - p - }) - .await - .unwrap(); + let mut subscription_client = emulator + .builder() + .build_pubsub_subscriber(config.clone()) + .await + .unwrap(); - let subscription_stream = subscription_client.stream_subscription( - ProjectSubscriptionName::new(emulator.project(), subscription_name), - StreamSubscriptionConfig::default(), - ); + // Create a subscription to query. Must be created before messages are published. + let _subscription = create_dummy_subscription( + &mut subscription_client, + emulator.project(), + subscription_name, + topic_name, + ) + .await + .unwrap() + .into_inner(); + + // send 1 message to the publisher to get an ack token + publish_client + .raw_api_mut() + .publish({ + let mut p = pubsub::api::PublishRequest::default(); + p.topic = topic.name; + p.messages = vec![{ + let mut m = pubsub::api::PubsubMessage::default(); + m.data = "foobar".into(); + m + }]; + p + }) + .await + .unwrap(); - pin_mut!(subscription_stream); + let subscription_stream = subscription_client.stream_subscription( + ProjectSubscriptionName::new(emulator.project(), subscription_name), + StreamSubscriptionConfig::default(), + ); - let (ack_token, _message) = subscription_stream.next().await.unwrap().unwrap(); - ack_token - }); + pin_mut!(subscription_stream); + + let (mut ack_token, _message) = subscription_stream.next().await.unwrap().unwrap(); let mut cx = Context::from_waker(futures::task::noop_waker_ref()); @@ -1034,30 +1024,13 @@ mod pubsub_client_tests { })) ); - // invariant check that ack tokens still work while the runtime is alive - assert_eq!( - Box::pin(ack_token.modify_deadline(599)) - .as_mut() - .poll(&mut cx), - Poll::Ready(Ok(())) - ); - - std::mem::drop(runtime); + // sanity check that ack tokens work while the stream is alive + assert!(matches!(ack_token.modify_deadline(599).await, Ok(()))); - // now modifications or acks/nacks will fail + std::mem::drop(subscription_stream); - assert!(matches!( - Box::pin(ack_token.modify_deadline(599)) - .as_mut() - .poll(&mut cx), - Poll::Ready(Err(pubsub::ModifyAcknowledgeError::Modify( - pubsub::AcknowledgeError { .. } - ))) - )); - - assert!(matches!( - Box::pin(ack_token.ack()).as_mut().poll(&mut cx), - Poll::Ready(Err(pubsub::AcknowledgeError { .. })) - )); + // now modifications or acks/nacks can still proceed + assert!(matches!(ack_token.modify_deadline(599).await, Ok(()))); + assert!(matches!(ack_token.ack().await, Ok(()))); } }