-
Notifications
You must be signed in to change notification settings - Fork 3.9k
feat(gRPC): build gRPC client interface to initiate communication with recovery-decider service #8178
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(gRPC): build gRPC client interface to initiate communication with recovery-decider service #8178
Changes from 17 commits
809da65
8f160b0
bd411f5
2e02b84
fd47398
4a82642
dd531f9
01d98a0
653cd5d
6cae8e1
787912a
a5c5d9a
da18869
f5d5294
826202a
d03c564
24dac12
a4262c7
71ec2bc
3b04911
44e5a18
75ef438
c53b310
0479693
4227dfb
69d5d24
6764550
2fe86f9
3142502
49f7a22
b4c7e71
a9899b5
e93d5a7
5aaaea6
da5a505
d75b207
c9f7aa6
9bebc57
86c9715
272c416
7378787
23ed82c
23e13fa
9e30a2a
eaf1e40
b154311
d2e3caa
1110062
7170f7a
79d8e64
964fd78
fda03ea
3e619b7
054bf7f
f916cb5
f0fe9d5
1fdab57
e1483a3
264766f
738037d
167dccd
720b422
86a63b6
0a516f6
4c0503d
48214ee
1b619b3
ae153fb
8d78311
0177ba4
fe2af2e
77b750d
32a74f9
4386261
a4aac2a
1bdedf7
98aeceb
f478efb
38378f9
d4b6025
b7fc94e
ad3beaa
c6ed016
49e68b5
35eae6a
9770ff6
60d745a
bd1913e
f89bf01
88671ca
576b4ab
5e7297d
4c50e55
71e0b4e
3d21400
ffd75fe
2b1d402
401c7b5
aa325bd
feb3f88
89405fb
5cead6b
d62ba13
5de0d97
1b768bf
0c01259
4edb072
f66a288
dfdb3e3
51deffb
c26bded
77bcbd5
3aed65b
dea1639
fa5f1ce
9ecd485
3eae00b
a9d90a1
060a33a
273acd1
0536d0e
abf8d50
8dd5919
baeb245
7e81472
920f211
20a864f
2bb5234
4b269f1
26fee44
ff53383
f791524
3818225
5da1854
01aaa59
553f094
da3304a
e810940
baee0b3
99daa29
560a72b
7bd8144
45435cc
59f5a77
bdddba9
4e19f8f
d973789
bbdc2a6
c9031ce
ee1735d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -1145,6 +1145,14 @@ host = "localhost" | |||||||||||||
port = 8000 | ||||||||||||||
service = "dynamo" | ||||||||||||||
|
||||||||||||||
[grpc_client.recovery_decider_client] | ||||||||||||||
host = "127.0.0.1" | ||||||||||||||
port = 8000 | ||||||||||||||
AdityaKumaar21 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||
|
||||||||||||||
[grpc_client.trainer_client] | ||||||||||||||
host = "0.0.0.0" | ||||||||||||||
port = 50051 | ||||||||||||||
|
||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Invalid host for trainer client Apply this diff: -[grpc_client.trainer_client]
-host = "0.0.0.0"
+[grpc_client.trainer_client]
+host = "127.0.0.1" 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
|
||||||||||||||
[theme.storage] | ||||||||||||||
file_storage_backend = "file_system" # Theme storage backend to be used | ||||||||||||||
|
||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -43,6 +43,7 @@ nutype = { version = "0.4.3", features = ["serde"] } | |
once_cell = "1.21.3" | ||
openssl = {version = "0.10.72", optional = true} | ||
phonenumber = "0.3.7" | ||
prost-types = { version = "0.13" } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chain
This crate is pulled in by almost every binary in the workspace. Unconditionally compiling Consider: -prost-types = { version = "0.13" }
+prost-types = { version = "0.13", optional = true } and then exposing it under an explicit feature (e.g. Also double-check that every crate depending on 🏁 Script executed: #!/usr/bin/env bash
# 1. Check if common_utils has any features defined around prost-types
echo "=== Features in crates/common_utils/Cargo.toml ==="
rg -n '^\[features\]' crates/common_utils/Cargo.toml -A 20
# 2. List all workspace crates that depend on common_utils
echo -e "\n=== Crates depending on common_utils ==="
rg -l 'common_utils *= ' -t toml
# 3. Find all instances of prost-types in Cargo.toml files
echo -e "\n=== prost-types usages ==="
rg -n 'prost-types' -t toml
# 4. Find all instances of prost in Cargo.toml files
echo -e "\n=== prost = usages ==="
rg -n 'prost *= ' -t toml Length of output: 2455 prost-types should be feature-gated in common_utils To avoid dragging in the full Protobuf stack for every build, mark • crates/common_utils/Cargo.toml [features]
default = []
keymanager = ["dep:router_env"]
…
tokenization_v2 = []
+ grpc = ["dep:prost-types"]
[dependencies]
-prost-types = { version = "0.13" }
+prost-types = { version = "0.13", optional = true } • crates/router/Cargo.toml common_utils = { path = "../common_utils", features = ["grpc"] } • crates/external_services/Cargo.toml Finally, confirm every 🤖 Prompt for AI Agents
AdityaKumaar21 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
quick-xml = { version = "0.31.0", features = ["serialize"] } | ||
rand = "0.8.5" | ||
regex = "1.11.1" | ||
|
Original file line number | Diff line number | Diff line change | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -1,4 +1,6 @@ | ||||||||||||
//! Custom serialization/deserialization implementations. | ||||||||||||
/// Serde helpers for `prost_types::Timestamp` and `Option<prost_types::Timestamp>`. | ||||||||||||
pub mod prost_timestamp; | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Missing cfg-gate mirrors unconditional dep The new -/// Serde helpers for `prost_types::Timestamp` and `Option<prost_types::Timestamp>`.
-pub mod prost_timestamp;
+/// Serde helpers for `prost_types::Timestamp` and `Option<prost_types::Timestamp>`.
+#[cfg(feature = "prost")]
+pub mod prost_timestamp; (Or re-use whatever feature name you decide on.) 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
|
||||||||||||
|
||||||||||||
/// Use the well-known ISO 8601 format when serializing and deserializing an | ||||||||||||
/// [`PrimitiveDateTime`][PrimitiveDateTime]. | ||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
use prost_types::Timestamp; | ||
use serde::{Deserialize, Deserializer, Serialize, Serializer}; | ||
|
||
/// A wrapper around `prost_types::Timestamp` to enable custom Serde implementations. | ||
#[derive(Debug, Clone, PartialEq)] | ||
pub struct SerializableTimestamp(pub Timestamp); | ||
|
||
impl From<Timestamp> for SerializableTimestamp { | ||
fn from(ts: Timestamp) -> Self { | ||
Self(ts) | ||
} | ||
} | ||
|
||
impl From<SerializableTimestamp> for Timestamp { | ||
fn from(sts: SerializableTimestamp) -> Self { | ||
sts.0 | ||
} | ||
} | ||
|
||
// Helper struct for serializing/deserializing the fields of Timestamp | ||
#[derive(Serialize, Deserialize)] | ||
struct TimestampFields { | ||
seconds: i64, | ||
nanos: i32, | ||
} | ||
|
||
impl Serialize for SerializableTimestamp { | ||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> | ||
where | ||
S: Serializer, | ||
{ | ||
let fields = TimestampFields { | ||
seconds: self.0.seconds, | ||
nanos: self.0.nanos, | ||
}; | ||
fields.serialize(serializer) | ||
} | ||
} | ||
|
||
impl<'de> Deserialize<'de> for SerializableTimestamp { | ||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> | ||
where | ||
D: Deserializer<'de>, | ||
{ | ||
let fields = TimestampFields::deserialize(deserializer)?; | ||
Ok(Self(Timestamp { | ||
seconds: fields.seconds, | ||
nanos: fields.nanos, | ||
})) | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add validation for the nanos field during deserialization. The Apply this diff to add validation: impl<'de> Deserialize<'de> for SerializableTimestamp {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let fields = TimestampFields::deserialize(deserializer)?;
+ if fields.nanos < 0 || fields.nanos > 999_999_999 {
+ return Err(serde::de::Error::custom(format!(
+ "nanos field must be between 0 and 999,999,999, got {}",
+ fields.nanos
+ )));
+ }
Ok(Self(Timestamp {
seconds: fields.seconds,
nanos: fields.nanos,
}))
}
} 🤖 Prompt for AI Agents
|
||
|
||
/// Serde module for `Option<SerializableTimestamp>`. | ||
pub mod optional_prost_timestamp { | ||
use serde::{Deserialize, Deserializer, Serialize, Serializer}; | ||
|
||
use super::SerializableTimestamp; | ||
|
||
/// Serializes `Option<SerializableTimestamp>`. | ||
pub fn serialize<S>( | ||
option_timestamp: &Option<SerializableTimestamp>, | ||
serializer: S, | ||
) -> Result<S::Ok, S::Error> | ||
where | ||
S: Serializer, | ||
{ | ||
match option_timestamp { | ||
Some(timestamp) => timestamp.serialize(serializer), // Directly use SerializableTimestamp's Serialize impl | ||
None => serializer.serialize_none(), | ||
} | ||
} | ||
|
||
/// Deserializes `Option<SerializableTimestamp>`. | ||
pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<SerializableTimestamp>, D::Error> | ||
where | ||
D: Deserializer<'de>, | ||
{ | ||
Option::<SerializableTimestamp>::deserialize(deserializer) // Directly use SerializableTimestamp's Deserialize impl | ||
} | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -13,6 +13,17 @@ email = ["dep:aws-config"] | |||||||||||||||||||||||||||||||||||||||||||
aws_s3 = ["dep:aws-config", "dep:aws-sdk-s3"] | ||||||||||||||||||||||||||||||||||||||||||||
hashicorp-vault = ["dep:vaultrs"] | ||||||||||||||||||||||||||||||||||||||||||||
v1 = ["hyperswitch_interfaces/v1", "common_utils/v1"] | ||||||||||||||||||||||||||||||||||||||||||||
v2 = [ | ||||||||||||||||||||||||||||||||||||||||||||
"dep:prost", | ||||||||||||||||||||||||||||||||||||||||||||
"dep:prost-types", | ||||||||||||||||||||||||||||||||||||||||||||
"dep:tonic", | ||||||||||||||||||||||||||||||||||||||||||||
"dep:tonic-build", | ||||||||||||||||||||||||||||||||||||||||||||
"dep:router_env", | ||||||||||||||||||||||||||||||||||||||||||||
"tokio/macros", | ||||||||||||||||||||||||||||||||||||||||||||
"tokio/rt-multi-thread", | ||||||||||||||||||||||||||||||||||||||||||||
"dep:hyper-util", | ||||||||||||||||||||||||||||||||||||||||||||
"dep:http-body-util" | ||||||||||||||||||||||||||||||||||||||||||||
] | ||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion
Consider moving only - "dep:tonic-build", Same applies to any other build-only crates you might add later. 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
|
||||||||||||||||||||||||||||||||||||||||||||
dynamic_routing = [ | ||||||||||||||||||||||||||||||||||||||||||||
"dep:prost", | ||||||||||||||||||||||||||||||||||||||||||||
"dep:tonic", | ||||||||||||||||||||||||||||||||||||||||||||
|
@@ -47,6 +58,7 @@ serde = { version = "1.0.219", features = ["derive"] } | |||||||||||||||||||||||||||||||||||||||||||
thiserror = "1.0.69" | ||||||||||||||||||||||||||||||||||||||||||||
vaultrs = { version = "0.7.4", optional = true } | ||||||||||||||||||||||||||||||||||||||||||||
prost = { version = "0.13", optional = true } | ||||||||||||||||||||||||||||||||||||||||||||
prost-types = { version = "0.13" , optional = true} | ||||||||||||||||||||||||||||||||||||||||||||
tokio = "1.45.1" | ||||||||||||||||||||||||||||||||||||||||||||
tonic = { version = "0.12.3", optional = true } | ||||||||||||||||||||||||||||||||||||||||||||
tonic-reflection = { version = "0.12.3", optional = true } | ||||||||||||||||||||||||||||||||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,29 +1,60 @@ | ||
#[allow(clippy::expect_used)] | ||
fn main() -> Result<(), Box<dyn std::error::Error>> { | ||
#[cfg(any(feature = "dynamic_routing", feature = "v2"))] | ||
compile_protos()?; | ||
|
||
Ok(()) | ||
} | ||
|
||
#[cfg(any(feature = "dynamic_routing", feature = "v2"))] | ||
fn compile_protos() -> Result<(), Box<dyn std::error::Error>> { | ||
let mut proto_files_to_compile = Vec::new(); | ||
let proto_base_path = router_env::workspace_path().join("proto"); | ||
|
||
#[cfg(feature = "dynamic_routing")] | ||
{ | ||
// Get the directory of the current crate | ||
proto_files_to_compile.push(proto_base_path.join("success_rate.proto")); | ||
proto_files_to_compile.push(proto_base_path.join("contract_routing.proto")); | ||
proto_files_to_compile.push(proto_base_path.join("elimination_rate.proto")); | ||
proto_files_to_compile.push(proto_base_path.join("health_check.proto")); | ||
} | ||
|
||
#[cfg(feature = "v2")] | ||
{ | ||
proto_files_to_compile.push(proto_base_path.join("recovery_decider.proto")); | ||
proto_files_to_compile.push(proto_base_path.join("trainer_client.proto")); | ||
} | ||
|
||
if !proto_files_to_compile.is_empty() { | ||
AdityaKumaar21 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// Ensure proto files are unique in case a file is needed by multiple features | ||
proto_files_to_compile.sort(); | ||
proto_files_to_compile.dedup(); | ||
|
||
let proto_path = router_env::workspace_path().join("proto"); | ||
let success_rate_proto_file = proto_path.join("success_rate.proto"); | ||
let contract_routing_proto_file = proto_path.join("contract_routing.proto"); | ||
let elimination_proto_file = proto_path.join("elimination_rate.proto"); | ||
let health_check_proto_file = proto_path.join("health_check.proto"); | ||
let out_dir = std::path::PathBuf::from(std::env::var("OUT_DIR")?); | ||
|
||
// Compile the .proto file | ||
tonic_build::configure() | ||
.out_dir(out_dir) | ||
.compile( | ||
&[ | ||
success_rate_proto_file, | ||
health_check_proto_file, | ||
elimination_proto_file, | ||
contract_routing_proto_file, | ||
], | ||
&[proto_path], | ||
.compile_well_known_types(true) | ||
.extern_path(".google.protobuf.Timestamp", "::prost_types::Timestamp") | ||
.type_attribute( | ||
"trainer.TriggerTrainingRequest", | ||
"#[derive(masking::Deserialize, masking::Serialize)]", | ||
) | ||
.type_attribute( | ||
"trainer.TriggerTrainingResponse", | ||
"#[derive(serde::Serialize)]", | ||
) | ||
.type_attribute( | ||
"trainer.GetTrainingJobStatusResponse", | ||
"#[derive(serde::Serialize)]", | ||
) | ||
.type_attribute( | ||
"google.protobuf.Timestamp", | ||
"#[derive(serde::Serialize, serde::Deserialize)]", | ||
SanchithHegde marked this conversation as resolved.
Show resolved
Hide resolved
|
||
) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Because Instead, enable the prost-types = { version = "0.12", features = ["serde"] } and delete the redundant attribute: - .type_attribute(
- "google.protobuf.Timestamp",
- "#[derive(serde::Serialize, serde::Deserialize)]",
- ) This keeps the build script leaner and future-proof. 🤖 Prompt for AI Agents
|
||
.expect("Failed to compile proto files"); | ||
.compile(&proto_files_to_compile, &[proto_base_path])?; | ||
} | ||
Ok(()) | ||
} |
Uh oh!
There was an error while loading. Please reload this page.