diff --git a/.gitignore b/.gitignore index b2dd75f..1b70d9a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ /target -/vss-accessor/src/proto/ +/src/proto/ diff --git a/Cargo.toml b/Cargo.toml index 858b842..d4ae59e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,4 +1,18 @@ -[workspace] -members = [ - "vss-accessor", -] +[package] +name = "vss-client" +version = "0.1.0" +edition = "2021" +build = "build.rs" + +[dependencies] +prost = "0.11.9" +reqwest = { version = "0.11.13", features = ["rustls-tls"] } + +[dev-dependencies] + +[build-dependencies] +prost-build = { version = "0.11.3" } +reqwest = { version = "0.11.13", features = ["blocking"] } + +[features] +genproto = [] diff --git a/vss-accessor/build.rs b/build.rs similarity index 85% rename from vss-accessor/build.rs rename to build.rs index e0f4233..dd96086 100644 --- a/vss-accessor/build.rs +++ b/build.rs @@ -1,8 +1,7 @@ +#[cfg(feature = "genproto")] extern crate prost_build; - -use std::fs::File; -use std::path::Path; -use std::{env, fs}; +#[cfg(feature = "genproto")] +use std::{env, fs, fs::File, path::Path}; /// To generate updated proto objects: /// 1. Place `vss.proto` file in `src/proto/` @@ -20,8 +19,8 @@ fn generate_protos() { ).unwrap(); prost_build::compile_protos(&["src/proto/vss.proto"], &["src/"]).unwrap(); - let from_path = Path::new(&env::var("OUT_DIR").unwrap()).join("org.vss.rs"); - fs::copy(from_path, "src/generated-src/org.vss.rs").unwrap(); + let from_path = Path::new(&env::var("OUT_DIR").unwrap()).join("vss.rs"); + fs::copy(from_path, "src/types.rs").unwrap(); } #[cfg(feature = "genproto")] diff --git a/src/client.rs b/src/client.rs new file mode 100644 index 0000000..3474574 --- /dev/null +++ b/src/client.rs @@ -0,0 +1,81 @@ +use ::prost::Message; +use reqwest; +use reqwest::Client; + +use crate::error::VssError; +use crate::types::{ + GetObjectRequest, GetObjectResponse, ListKeyVersionsRequest, ListKeyVersionsResponse, PutObjectRequest, + PutObjectResponse, +}; + +/// Thin-client to access a hosted instance of Versioned Storage Service (VSS). +/// The provided [`VssClient`] API is minimalistic and is congruent to the VSS server-side API. +pub struct VssClient { + base_url: String, + client: Client, +} + +impl VssClient { + /// Constructs a [`VssClient`] using `base_url` as the VSS server endpoint. + pub fn new(base_url: &str) -> Self { + let client = Client::new(); + Self { base_url: String::from(base_url), client } + } + + /// Fetches a value against a given `key` in `request`. + /// Makes a service call to the `GetObject` endpoint of the VSS server. + /// For API contract/usage, refer to docs for [`GetObjectRequest`] and [`GetObjectResponse`]. + pub async fn get_object(&self, request: &GetObjectRequest) -> Result { + let url = format!("{}/getObject", self.base_url); + + let raw_response = self.client.post(url).body(request.encode_to_vec()).send().await?; + let status = raw_response.status(); + let payload = raw_response.bytes().await?; + + if status.is_success() { + let response = GetObjectResponse::decode(&payload[..])?; + Ok(response) + } else { + Err(VssError::new(status, payload)) + } + } + + /// Writes multiple [`PutObjectRequest::transaction_items`] as part of a single transaction. + /// Makes a service call to the `PutObject` endpoint of the VSS server, with multiple items. + /// Items in the `request` are written in a single all-or-nothing transaction. + /// For API contract/usage, refer to docs for [`PutObjectRequest`] and [`PutObjectResponse`]. + pub async fn put_object(&self, request: &PutObjectRequest) -> Result { + let url = format!("{}/putObjects", self.base_url); + + let response_raw = self.client.post(url).body(request.encode_to_vec()).send().await?; + let status = response_raw.status(); + let payload = response_raw.bytes().await?; + + if status.is_success() { + let response = PutObjectResponse::decode(&payload[..])?; + Ok(response) + } else { + Err(VssError::new(status, payload)) + } + } + + /// Lists keys and their corresponding version for a given [`ListKeyVersionsRequest::store_id`]. + /// Makes a service call to the `ListKeyVersions` endpoint of the VSS server. + /// For API contract/usage, refer to docs for [`ListKeyVersionsRequest`] and [`ListKeyVersionsResponse`]. + pub async fn list_key_versions( + &self, request: &ListKeyVersionsRequest, + ) -> Result { + let url = format!("{}/listKeyVersions", self.base_url); + + let response_raw = self.client.post(url).body(request.encode_to_vec()).send().await?; + let status = response_raw.status(); + let payload = response_raw.bytes().await?; + + if status.is_success() { + let response = ListKeyVersionsResponse::decode(&payload[..])?; + Ok(response) + } else { + Err(VssError::new(status, payload)) + } + } +} diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..d07aa77 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,78 @@ +use crate::types::{ErrorCode, ErrorResponse}; +use prost::bytes::Bytes; +use prost::{DecodeError, Message}; +use reqwest::StatusCode; +use std::error::Error; +use std::fmt::{Display, Formatter}; + +/// When there is an error while writing to VSS storage, the response contains a relevant error code. +/// A mapping from a VSS server error codes. Refer to [`ErrorResponse`] docs for more +/// information regarding each error code and corresponding use-cases. +#[derive(Debug)] +pub enum VssError { + InvalidRequestError(String), + ConflictError(String), + InternalServerError(String), + InternalError(String), +} + +impl VssError { + /// Create new instance of `VssError` + pub fn new(status: StatusCode, payload: Bytes) -> VssError { + match ErrorResponse::decode(&payload[..]) { + Ok(error_response) => VssError::from(error_response), + Err(e) => { + let message = + format!("Unable to decode ErrorResponse from server, HttpStatusCode: {}, DecodeErr: {}", status, e); + VssError::InternalError(message) + } + } + } +} + +impl Display for VssError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + VssError::InvalidRequestError(message) => { + write!(f, "Request sent to VSS Storage was invalid: {}", message) + } + VssError::ConflictError(message) => { + write!(f, "Potential version conflict in write operation: {}", message) + } + VssError::InternalServerError(message) => { + write!(f, "InternalServerError: {}", message) + } + VssError::InternalError(message) => { + write!(f, "InternalError: {}", message) + } + } + } +} + +impl Error for VssError {} + +impl From for VssError { + fn from(error_response: ErrorResponse) -> Self { + match error_response.error_code() { + ErrorCode::InvalidRequestException => VssError::InvalidRequestError(error_response.message), + ErrorCode::ConflictException => VssError::ConflictError(error_response.message), + ErrorCode::InternalServerException => VssError::InternalServerError(error_response.message), + _ => VssError::InternalError(format!( + "VSS responded with an unknown error code: {}, message: {}", + error_response.error_code, error_response.message + )), + } + } +} + +impl From for VssError { + fn from(err: DecodeError) -> Self { + VssError::InternalError(err.to_string()) + } +} + +impl From for VssError { + fn from(err: reqwest::Error) -> Self { + VssError::InternalError(err.to_string()) + } +} diff --git a/vss-accessor/src/lib.rs b/src/lib.rs similarity index 64% rename from vss-accessor/src/lib.rs rename to src/lib.rs index 800e485..98d1486 100644 --- a/vss-accessor/src/lib.rs +++ b/src/lib.rs @@ -1,2 +1,6 @@ #![deny(rustdoc::broken_intra_doc_links)] #![deny(rustdoc::private_intra_doc_links)] + +pub mod client; +pub mod error; +pub mod types; diff --git a/src/types.rs b/src/types.rs new file mode 100644 index 0000000..142931f --- /dev/null +++ b/src/types.rs @@ -0,0 +1,251 @@ +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GetObjectRequest { + /// store_id is a keyspace identifier. + /// Ref: ) + /// All APIs operate within a single store_id. + /// It is up to clients to use single or multiple stores for their use-case. + /// This can be used for client-isolation/ rate-limiting / throttling on the server-side. + /// Authorization and billing can also be performed at the store_id level. + #[prost(string, tag = "1")] + pub store_id: ::prost::alloc::string::String, + /// Key for which the value is to be fetched. + /// + /// Consistency Guarantee: + /// Get(read) operations against a key are consistent reads and will reflect all previous writes, + /// since Put/Write provides read-after-write and read-after-update consistency guarantees. + /// + /// Read Isolation: + /// Get/Read operations against a key are ensured to have read-committed isolation. + /// Ref: )#Read_committed + #[prost(string, tag = "2")] + pub key: ::prost::alloc::string::String, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GetObjectResponse { + /// Fetched value and version along with the corresponding key in the request. + #[prost(message, optional, tag = "2")] + pub value: ::core::option::Option, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PutObjectRequest { + /// store_id is a keyspace identifier. + /// Ref: ) + /// All APIs operate within a single store_id. + /// It is up to clients to use single or multiple stores for their use-case. + /// This can be used for client-isolation/ rate-limiting / throttling on the server-side. + /// Authorization and billing can also be performed at the store_id level. + #[prost(string, tag = "1")] + pub store_id: ::prost::alloc::string::String, + /// global_version is a sequence-number/version of the whole store. This can be used for versioning + /// and ensures that multiple updates in case of multiple devices can only be done linearly, even + /// if those updates did not directly conflict with each other based on keys/transaction_items. + /// + /// If present, the write will only succeed if the current server-side global_version against + /// the store_id is same as in the request. + /// Clients are expected to store (client-side) the global version against store_id. + /// The request must contain their client-side value of global_version if global versioning and + /// conflict detection is desired. + /// + /// For the first write of the store, global version should be '0'. If the write succeeds, clients + /// must increment their global version (client-side) by 1. + /// The server increments global_version (server-side) for every successful write, hence this + /// client-side increment is required to ensure matching versions. This updated global version + /// should be used in subsequent PutObjectRequests for the store. + /// + /// Requests with a conflicting version will fail with `CONFLICT_EXCEPTION` as ErrorCode. + #[prost(int64, optional, tag = "2")] + pub global_version: ::core::option::Option, + /// Items to be written as a result of this PutObjectRequest. + /// + /// In an item, each key is supplied with its corresponding value and version. + /// Clients can choose to encrypt the keys client-side in order to obfuscate their usage patterns. + /// If the write is successful, the previous value corresponding to the key will be overwritten. + /// + /// Multiple items in transaction_items of a single PutObjectRequest are written in + /// a database-transaction in an all-or-nothing fashion. + /// Items in a single PutObjectRequest must have distinct keys. + /// + /// Clients are expected to store a version against every key. + /// The write will succeed if the current DB version against the key is the same as in the request. + /// When initiating a PutObjectRequest, the request should contain their client-side version for + /// that key-value. + /// + /// For the first write of any key, the version should be '0'. If the write succeeds, the client + /// must increment their corresponding key versions (client-side) by 1. + /// The server increments key versions (server-side) for every successful write, hence this + /// client-side increment is required to ensure matching versions. These updated key versions should + /// be used in subsequent PutObjectRequests for the keys. + /// + /// Requests with a conflicting version will fail with `CONFLICT_EXCEPTION` as ErrorCode. + /// + /// Considerations for transactions: + /// Transaction writes of multiple items have a performance overhead, hence it is recommended to use + /// them only if required by the client application to ensure logic/code correctness. + /// That is, transaction_items are not a substitute for batch-write of multiple unrelated items. + /// When a write of multiple unrelated items is desired, it is recommended to use separate + /// PutObjectRequests. + /// + /// Consistency guarantee: + /// All PutObjectRequests are strongly consistent i.e. they provide read-after-write and + /// read-after-update consistency guarantees. + #[prost(message, repeated, tag = "3")] + pub transaction_items: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PutObjectResponse {} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ListKeyVersionsRequest { + /// store_id is a keyspace identifier. + /// Ref: ) + /// All APIs operate within a single store_id. + /// It is up to clients to use single or multiple stores for their use-case. + /// This can be used for client-isolation/ rate-limiting / throttling on the server-side. + /// Authorization and billing can also be performed at the store_id level. + #[prost(string, tag = "1")] + pub store_id: ::prost::alloc::string::String, + /// A key_prefix is a string of characters at the beginning of the key. Prefixes can be used as + /// a way to organize key-values in a similar way to directories. + /// + /// If key_prefix is specified, the response results will be limited to those keys that begin with + /// the specified prefix. + /// + /// If no key_prefix is specified or it is empty (""), all the keys are eligible to be returned in + /// the response. + #[prost(string, optional, tag = "2")] + pub key_prefix: ::core::option::Option<::prost::alloc::string::String>, + /// page_size is used by clients to specify the maximum number of results that can be returned by + /// the server. + /// The server may further constrain the maximum number of results returned in a single page. + /// If the page_size is 0 or not set, the server will decide the number of results to be returned. + #[prost(int32, optional, tag = "3")] + pub page_size: ::core::option::Option, + /// page_token is a pagination token. + /// + /// To query for the first page of ListKeyVersions, page_token must not be specified. + /// + /// For subsequent pages, use the value that was returned as `next_page_token` in the previous + /// page's ListKeyVersionsResponse. + #[prost(string, optional, tag = "4")] + pub page_token: ::core::option::Option<::prost::alloc::string::String>, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ListKeyVersionsResponse { + /// Fetched keys and versions. + /// Even though this API reuses KeyValue struct, the value sub-field will not be set by the server. + #[prost(message, repeated, tag = "1")] + pub key_versions: ::prost::alloc::vec::Vec, + /// next_page_token is a pagination token, used to retrieve the next page of results. + /// Use this value to query for next_page of paginated ListKeyVersions operation, by specifying + /// this value as the `page_token` in the next request. + /// + /// If next_page_token is empty (""), then the "last page" of results has been processed and + /// there is no more data to be retrieved. + /// + /// If next_page_token is not empty, it does not necessarily mean that there is more data in the + /// result set. The only way to know when you have reached the end of the result set is when + /// next_page_token is empty. + /// + /// Caution: Clients must not assume a specific number of key_versions to be present in a page for + /// paginated response. + #[prost(string, optional, tag = "2")] + pub next_page_token: ::core::option::Option<::prost::alloc::string::String>, + /// global_version is a sequence-number/version of the whole store. + /// + /// global_version is only returned in response for the first page of the ListKeyVersionsResponse + /// and is guaranteed to be read before reading any key-versions. + /// + /// In case of refreshing the complete key-version view on the client-side, correct usage for + /// the returned global_version is as following: + /// 1. Read global_version from the first page of paginated response and save it as local variable. + /// 2. Update all the key_versions on client-side from all the pages of paginated response. + /// 3. Update global_version on client_side from the local variable saved in step-1. + /// This ensures that on client-side, all current key_versions were stored at global_version or later. + /// This guarantee is helpful for ensuring the versioning correctness if using the global_version + /// in PutObject API and can help avoid the race conditions related to it. + #[prost(int64, optional, tag = "3")] + pub global_version: ::core::option::Option, +} +/// When HttpStatusCode is not ok (200), the response `content` contains a serialized ErrorResponse +/// with the relevant ErrorCode and message +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ErrorResponse { + /// The error code uniquely identifying an error condition. + /// It is meant to be read and understood programmatically by code that detects/handles errors by + /// type. + #[prost(enumeration = "ErrorCode", tag = "1")] + pub error_code: i32, + /// The error message containing a generic description of the error condition in English. + /// It is intended for a human audience only and should not be parsed to extract any information + /// programmatically. Client-side code may use it for logging only. + #[prost(string, tag = "2")] + pub message: ::prost::alloc::string::String, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct KeyValue { + /// Key against which the value is stored. + #[prost(string, tag = "1")] + pub key: ::prost::alloc::string::String, + /// Version field is used for key-level versioning. + /// For first write of key, version should be '0'. If the write succeeds, clients must increment + /// their corresponding key version (client-side) by 1. + /// The server increments key version (server-side) for every successful write, hence this + /// client-side increment is required to ensure matching versions. These updated key versions should + /// be used in subsequent PutObjectRequests for the keys. + #[prost(int64, tag = "2")] + pub version: i64, + /// Object value in bytes which is stored (in put) and fetched (in get). + /// Clients must encrypt this blob client-side before sending it over the wire to server in order + /// to preserve privacy and security. + #[prost(bytes = "vec", tag = "3")] + pub value: ::prost::alloc::vec::Vec, +} +/// ErrorCodes to be used in ErrorResponse +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum ErrorCode { + /// Default protobuf Enum value. Will not be used as ErrorCode by server. + Unknown = 0, + /// CONFLICT_EXCEPTION is used when the request contains mismatched version (either key or global) + /// in PutObjectRequest. For more info refer PutObjectRequest. + ConflictException = 1, + /// INVALID_REQUEST_EXCEPTION is used in the following cases: + /// - The request was missing a required argument. + /// - The specified argument was invalid, incomplete or in the wrong format. + /// - The request body of api cannot be deserialized into corresponding protobuf object. + InvalidRequestException = 2, + /// An internal server error occurred, client is probably at no fault and can safely retry this + /// error with exponential backoff. + InternalServerException = 3, +} +impl ErrorCode { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + ErrorCode::Unknown => "UNKNOWN", + ErrorCode::ConflictException => "CONFLICT_EXCEPTION", + ErrorCode::InvalidRequestException => "INVALID_REQUEST_EXCEPTION", + ErrorCode::InternalServerException => "INTERNAL_SERVER_EXCEPTION", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "UNKNOWN" => Some(Self::Unknown), + "CONFLICT_EXCEPTION" => Some(Self::ConflictException), + "INVALID_REQUEST_EXCEPTION" => Some(Self::InvalidRequestException), + "INTERNAL_SERVER_EXCEPTION" => Some(Self::InternalServerException), + _ => None, + } + } +} diff --git a/vss-accessor/Cargo.toml b/vss-accessor/Cargo.toml deleted file mode 100644 index c2a7c64..0000000 --- a/vss-accessor/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -name = "vss-accessor" -version = "0.1.0" -edition = "2021" -build = "build.rs" - -[dependencies] - -[dev-dependencies] - -[build-dependencies] -prost-build = { version = "0.11.3" } -reqwest = { version = "0.11.13", features = ["blocking"] } - -[features] -genproto = [] \ No newline at end of file diff --git a/vss-accessor/src/generated-src/org.vss.rs b/vss-accessor/src/generated-src/org.vss.rs deleted file mode 100644 index acc4f69..0000000 --- a/vss-accessor/src/generated-src/org.vss.rs +++ /dev/null @@ -1,251 +0,0 @@ -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct GetObjectRequest { - /// store_id is a keyspace identifier. - /// Ref: ) - /// All APIs operate within a single store_id. - /// It is up to clients to use single or multiple stores for their use-case. - /// This can be used for client-isolation/ rate-limiting / throttling on the server-side. - /// Authorization and billing can also be performed at the store_id level. - #[prost(string, tag = "1")] - pub store_id: ::prost::alloc::string::String, - /// Key for which the value is to be fetched. - /// - /// Consistency Guarantee: - /// Get(read) operations against a key are consistent reads and will reflect all previous writes, - /// since Put/Write provides read-after-write and read-after-update consistency guarantees. - /// - /// Read Isolation: - /// Get/Read operations against a key are ensured to have read-committed isolation. - /// Ref: )#Read_committed - #[prost(string, tag = "2")] - pub key: ::prost::alloc::string::String, -} -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct GetObjectResponse { - /// Fetched value and version along with the corresponding key in the request. - #[prost(message, optional, tag = "2")] - pub value: ::core::option::Option, -} -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct PutObjectRequest { - /// store_id is a keyspace identifier. - /// Ref: ) - /// All APIs operate within a single store_id. - /// It is up to clients to use single or multiple stores for their use-case. - /// This can be used for client-isolation/ rate-limiting / throttling on the server-side. - /// Authorization and billing can also be performed at the store_id level. - #[prost(string, tag = "1")] - pub store_id: ::prost::alloc::string::String, - /// global_version is a sequence-number/version of the whole store. This can be used for versioning - /// and ensures that multiple updates in case of multiple devices can only be done linearly, even - /// if those updates did not directly conflict with each other based on keys/transaction_items. - /// - /// If present, the write will only succeed if the current server-side global_version against - /// the store_id is same as in the request. - /// Clients are expected to store (client-side) the global version against store_id. - /// The request must contain their client-side value of global_version if global versioning and - /// conflict detection is desired. - /// - /// For the first write of the store, global version should be '0'. If the write succeeds, clients - /// must increment their global version (client-side) by 1. - /// The server increments global_version (server-side) for every successful write, hence this - /// client-side increment is required to ensure matching versions. This updated global version - /// should be used in subsequent PutObjectRequests for the store. - /// - /// Requests with a conflicting version will fail with `CONFLICT_EXCEPTION` as ErrorCode. - #[prost(int64, optional, tag = "2")] - pub global_version: ::core::option::Option, - /// Items to be written as a result of this PutObjectRequest. - /// - /// In an item, each key is supplied with its corresponding value and version. - /// Clients can choose to encrypt the keys client-side in order to obfuscate their usage patterns. - /// If the write is successful, the previous value corresponding to the key will be overwritten. - /// - /// Multiple items in transaction_items of a single PutObjectRequest are written in - /// a database-transaction in an all-or-nothing fashion. - /// Items in a single PutObjectRequest must have distinct keys. - /// - /// Clients are expected to store a version against every key. - /// The write will succeed if the current DB version against the key is the same as in the request. - /// When initiating a PutObjectRequest, the request should contain their client-side version for - /// that key-value. - /// - /// For the first write of any key, the version should be '0'. If the write succeeds, the client - /// must increment their corresponding key versions (client-side) by 1. - /// The server increments key versions (server-side) for every successful write, hence this - /// client-side increment is required to ensure matching versions. These updated key versions should - /// be used in subsequent PutObjectRequests for the keys. - /// - /// Requests with a conflicting version will fail with `CONFLICT_EXCEPTION` as ErrorCode. - /// - /// Considerations for transactions: - /// Transaction writes of multiple items have a performance overhead, hence it is recommended to use - /// them only if required by the client application to ensure logic/code correctness. - /// That is, transaction_items are not a substitute for batch-write of multiple unrelated items. - /// When a write of multiple unrelated items is desired, it is recommended to use separate - /// PutObjectRequests. - /// - /// Consistency guarantee: - /// All PutObjectRequests are strongly consistent i.e. they provide read-after-write and - /// read-after-update consistency guarantees. - #[prost(message, repeated, tag = "3")] - pub transaction_items: ::prost::alloc::vec::Vec, -} -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct PutObjectResponse {} -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct ListKeyVersionsRequest { - /// store_id is a keyspace identifier. - /// Ref: ) - /// All APIs operate within a single store_id. - /// It is up to clients to use single or multiple stores for their use-case. - /// This can be used for client-isolation/ rate-limiting / throttling on the server-side. - /// Authorization and billing can also be performed at the store_id level. - #[prost(string, tag = "1")] - pub store_id: ::prost::alloc::string::String, - /// A key_prefix is a string of characters at the beginning of the key. Prefixes can be used as - /// a way to organize key-values in a similar way to directories. - /// - /// If key_prefix is specified, the response results will be limited to those keys that begin with - /// the specified prefix. - /// - /// If no key_prefix is specified or it is empty (""), all the keys are eligible to be returned in - /// the response. - #[prost(string, optional, tag = "2")] - pub key_prefix: ::core::option::Option<::prost::alloc::string::String>, - /// page_size is used by clients to specify the maximum number of results that can be returned by - /// the server. - /// The server may further constrain the maximum number of results returned in a single page. - /// If the page_size is 0 or not set, the server will decide the number of results to be returned. - #[prost(int32, optional, tag = "3")] - pub page_size: ::core::option::Option, - /// page_token is a pagination token. - /// - /// To query for the first page of ListKeyVersions, page_token must not be specified. - /// - /// For subsequent pages, use the value that was returned as `next_page_token` in the previous - /// page's ListKeyVersionsResponse. - #[prost(string, optional, tag = "4")] - pub page_token: ::core::option::Option<::prost::alloc::string::String>, -} -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct ListKeyVersionsResponse { - /// Fetched keys and versions. - /// Even though this API reuses KeyValue struct, the value sub-field will not be set by the server. - #[prost(message, repeated, tag = "1")] - pub key_versions: ::prost::alloc::vec::Vec, - /// next_page_token is a pagination token, used to retrieve the next page of results. - /// Use this value to query for next_page of paginated ListKeyVersions operation, by specifying - /// this value as the `page_token` in the next request. - /// - /// If next_page_token is empty (""), then the "last page" of results has been processed and - /// there is no more data to be retrieved. - /// - /// If next_page_token is not empty, it does not necessarily mean that there is more data in the - /// result set. The only way to know when you have reached the end of the result set is when - /// next_page_token is empty. - /// - /// Caution: Clients must not assume a specific number of key_versions to be present in a page for - /// paginated response. - #[prost(string, optional, tag = "2")] - pub next_page_token: ::core::option::Option<::prost::alloc::string::String>, - /// global_version is a sequence-number/version of the whole store. - /// - /// global_version is only returned in response for the first page of the ListKeyVersionsResponse - /// and is guaranteed to be read before reading any key-versions. - /// - /// In case of refreshing the complete key-version view on the client-side, correct usage for - /// the returned global_version is as following: - /// 1. Read global_version from the first page of paginated response and save it as local variable. - /// 2. Update all the key_versions on client-side from all the pages of paginated response. - /// 3. Update global_version on client_side from the local variable saved in step-1. - /// This ensures that on client-side, all current key_versions were stored at global_version or later. - /// This guarantee is helpful for ensuring the versioning correctness if using the global_version - /// in PutObject API and can help avoid the race conditions related to it. - #[prost(int64, optional, tag = "3")] - pub global_version: ::core::option::Option, -} -/// When HttpStatusCode is not ok (200), the response `content` contains a serialized ErrorResponse -/// with the relevant ErrorCode and message -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct ErrorResponse { - /// The error code uniquely identifying an error condition. - /// It is meant to be read and understood programmatically by code that detects/handles errors by - /// type. - #[prost(enumeration = "ErrorCode", tag = "1")] - pub error_code: i32, - /// The error message containing a generic description of the error condition in English. - /// It is intended for a human audience only and should not be parsed to extract any information - /// programmatically. Client-side code may use it for logging only. - #[prost(string, tag = "2")] - pub message: ::prost::alloc::string::String, -} -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct KeyValue { - /// Key against which the value is stored. - #[prost(string, tag = "1")] - pub key: ::prost::alloc::string::String, - /// Version field is used for key-level versioning. - /// For first write of key, version should be '0'. If the write succeeds, clients must increment - /// their corresponding key version (client-side) by 1. - /// The server increments key version (server-side) for every successful write, hence this - /// client-side increment is required to ensure matching versions. These updated key versions should - /// be used in subsequent PutObjectRequests for the keys. - #[prost(int64, tag = "2")] - pub version: i64, - /// Object value in bytes which is stored (in put) and fetched (in get). - /// Clients must encrypt this blob client-side before sending it over the wire to server in order - /// to preserve privacy and security. - #[prost(bytes = "vec", tag = "3")] - pub value: ::prost::alloc::vec::Vec, -} -/// ErrorCodes to be used in ErrorResponse -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] -#[repr(i32)] -pub enum ErrorCode { - /// Default protobuf Enum value. Will not be used as ErrorCode by server. - Unknown = 0, - /// CONFLICT_EXCEPTION is used when the request contains mismatched version (either key or global) - /// in PutObjectRequest. For more info refer PutObjectRequest. - ConflictException = 1, - /// INVALID_REQUEST_EXCEPTION is used in the following cases: - /// - The request was missing a required argument. - /// - The specified argument was invalid, incomplete or in the wrong format. - /// - The request body of api cannot be deserialized into corresponding protobuf object. - InvalidRequestException = 2, - /// An internal server error occurred, client is probably at no fault and can safely retry this - /// error with exponential backoff. - InternalServerException = 3, -} -impl ErrorCode { - /// String value of the enum field names used in the ProtoBuf definition. - /// - /// The values are not transformed in any way and thus are considered stable - /// (if the ProtoBuf definition does not change) and safe for programmatic use. - pub fn as_str_name(&self) -> &'static str { - match self { - ErrorCode::Unknown => "UNKNOWN", - ErrorCode::ConflictException => "CONFLICT_EXCEPTION", - ErrorCode::InvalidRequestException => "INVALID_REQUEST_EXCEPTION", - ErrorCode::InternalServerException => "INTERNAL_SERVER_EXCEPTION", - } - } - /// Creates an enum from field names used in the ProtoBuf definition. - pub fn from_str_name(value: &str) -> ::core::option::Option { - match value { - "UNKNOWN" => Some(Self::Unknown), - "CONFLICT_EXCEPTION" => Some(Self::ConflictException), - "INVALID_REQUEST_EXCEPTION" => Some(Self::InvalidRequestException), - "INTERNAL_SERVER_EXCEPTION" => Some(Self::InternalServerException), - _ => None, - } - } -}