Skip to content

Commit

Permalink
refactor: Pass protobuf message descriptor to DynamicMessage::new #73
Browse files Browse the repository at this point in the history
  • Loading branch information
rholshausen committed Oct 31, 2024
1 parent a7b1218 commit 8452e25
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 33 deletions.
46 changes: 30 additions & 16 deletions src/dynamic_message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use prost_types::{DescriptorProto, FileDescriptorSet};
use serde_json::Value;
use tonic::codec::{Codec, DecodeBuf, Decoder, EncodeBuf, Encoder};
use tonic::Status;
use tracing::{debug, error, instrument, trace};
use tracing::{debug, error, instrument, trace, warn};

use crate::message_decoder::{decode_message, ProtobufField, ProtobufFieldData};

Expand Down Expand Up @@ -75,9 +75,13 @@ pub struct DynamicMessage {

impl DynamicMessage {
/// Create a new message from the slice of fields
pub fn new(fields: &[ProtobufField], descriptors: &FileDescriptorSet) -> DynamicMessage {
pub fn new(
message_descriptor: &DescriptorProto,
field_data: &[ProtobufField],
descriptors: &FileDescriptorSet
) -> DynamicMessage {
DynamicMessage {
fields: fields.to_vec(),
fields: field_data.to_vec(),
descriptors: descriptors.clone()
}
}
Expand Down Expand Up @@ -206,7 +210,7 @@ impl DynamicMessage {
let mut buffer = Bytes::copy_from_slice(data);
match decode_message(&mut buffer, descriptor, &descriptors) {
Ok(fields) => {
let mut message = DynamicMessage::new(fields.as_slice(), &descriptors);
let mut message = DynamicMessage::new(descriptor, fields.as_slice(), &descriptors);
message.match_path(path_tokens, callback);
data.clear();
if let Err(err) = message.write_to(data) {
Expand All @@ -229,7 +233,7 @@ impl DynamicMessage {
}

/// Mutates the message by applying the generators to any matching message fields
#[instrument(ret, skip(self))]
#[instrument(ret, skip(self, generators))]
pub fn apply_generators(
&mut self,
generators: Option<&HashMap<DocPath, Generator>>,
Expand All @@ -246,6 +250,8 @@ impl DynamicMessage {
let generated_value = generator.generate_value(&value.data, &context, &vm_boxed)?;
self.set_value(&path, generated_value)?;
}
} else {
warn!("No matching field found for generator '{}'", path);
}
}
}
Expand Down Expand Up @@ -303,7 +309,7 @@ impl Decoder for DynamicMessageDecoder {
#[instrument]
fn decode(&mut self, src: &mut DecodeBuf<'_>) -> Result<Option<Self::Item>, Self::Error> {
match decode_message(src, &self.descriptor, &self.file_descriptor_set) {
Ok(fields) => Ok(Some(DynamicMessage::new(&fields, &self.file_descriptor_set))),
Ok(fields) => Ok(Some(DynamicMessage::new(&self.descriptor, fields.as_slice(), &self.file_descriptor_set))),
Err(err) => {
error!("Failed to decode the message - {err}");
Err(Status::invalid_argument(format!("Failed to decode the message - {err}")))
Expand Down Expand Up @@ -332,7 +338,8 @@ mod tests {
let descriptors = FileDescriptorSet {
file: vec![]
};
let mut message = DynamicMessage::new(fields.as_slice(), &descriptors);
let descriptor = DescriptorProto::default();
let mut message = DynamicMessage::new(&descriptor, fields.as_slice(), &descriptors);
let path = DocPath::new("$.one.two.three").unwrap();
expect!(message.fetch_value(&path)).to(be_none());
}
Expand All @@ -349,7 +356,8 @@ mod tests {
file: vec![]
};
let fields = vec![ field.clone() ];
let mut message = DynamicMessage::new(fields.as_slice(), &descriptors);
let descriptor = DescriptorProto::default();
let mut message = DynamicMessage::new(&descriptor, fields.as_slice(), &descriptors);
let path = DocPath::new("one").unwrap();
expect!(message.fetch_value(&path)).to(be_some().value(field));
}
Expand All @@ -365,8 +373,9 @@ mod tests {
let descriptors = FileDescriptorSet {
file: vec![]
};
let descriptor = DescriptorProto::default();
let fields = vec![ field.clone() ];
let mut message = DynamicMessage::new(fields.as_slice(), &descriptors);
let mut message = DynamicMessage::new(&descriptor, fields.as_slice(), &descriptors);
let path = DocPath::new("$.one").unwrap();
expect!(message.fetch_value(&path)).to(be_some().value(field));
}
Expand Down Expand Up @@ -427,7 +436,8 @@ mod tests {
let descriptors = FileDescriptorSet {
file: vec![]
};
let child_message = DynamicMessage::new(&[child_field.clone(), child_field2], &descriptors);
let descriptor = DescriptorProto::default();
let child_message = DynamicMessage::new(&child_descriptor, &[child_field.clone(), child_field2], &descriptors);
let mut buffer = BytesMut::new();
child_message.write_to(&mut buffer).unwrap();
let field = ProtobufField {
Expand All @@ -437,7 +447,7 @@ mod tests {
data: ProtobufFieldData::Message(buffer.to_vec(), child_descriptor)
};
let fields = vec![ field.clone() ];
let mut message = DynamicMessage::new(fields.as_slice(), &descriptors);
let mut message = DynamicMessage::new(&descriptor, fields.as_slice(), &descriptors);
let path = DocPath::new("$.one.two").unwrap();
expect!(message.fetch_value(&path)).to(be_some().value(child_field));
}
Expand All @@ -448,7 +458,8 @@ mod tests {
let descriptors = FileDescriptorSet {
file: vec![]
};
let mut message = DynamicMessage::new(fields.as_slice(), &descriptors);
let descriptor = DescriptorProto::default();
let mut message = DynamicMessage::new(&descriptor, fields.as_slice(), &descriptors);
let path = DocPath::new_unwrap("$.one.two.three");
let generators = hashmap!{
path.clone() => RandomInt(1, 10)
Expand All @@ -469,7 +480,8 @@ mod tests {
file: vec![]
};
let fields = vec![ field.clone() ];
let mut message = DynamicMessage::new(fields.as_slice(), &descriptors);
let descriptor = DescriptorProto::default();
let mut message = DynamicMessage::new(&descriptor, fields.as_slice(), &descriptors);
let generators = hashmap!{
DocPath::new_unwrap("$.two") => RandomInt(1, 10)
};
Expand All @@ -489,7 +501,8 @@ mod tests {
file: vec![]
};
let fields = vec![ field.clone() ];
let mut message = DynamicMessage::new(fields.as_slice(), &descriptors);
let descriptor = DescriptorProto::default();
let mut message = DynamicMessage::new(&descriptor, fields.as_slice(), &descriptors, );
let generators = hashmap!{
DocPath::new_unwrap("$.one") => RandomInt(1, 10)
};
Expand Down Expand Up @@ -533,7 +546,7 @@ mod tests {
let descriptors = FileDescriptorSet {
file: vec![]
};
let child_message = DynamicMessage::new(&[child_field.clone(), child_field2], &descriptors);
let child_message = DynamicMessage::new(&child_descriptor, &[child_field.clone(), child_field2], &descriptors);
let mut buffer = BytesMut::new();
child_message.write_to(&mut buffer).unwrap();
let field = ProtobufField {
Expand All @@ -543,7 +556,8 @@ mod tests {
data: ProtobufFieldData::Message(buffer.to_vec(), child_descriptor)
};
let fields = vec![ field.clone() ];
let mut message = DynamicMessage::new(fields.as_slice(), &descriptors);
let descriptor = DescriptorProto::default();
let mut message = DynamicMessage::new(&descriptor, fields.as_slice(), &descriptors);
let path = DocPath::new_unwrap("$.one.two");
let generators = hashmap!{
path.clone() => RandomInt(1, 10)
Expand Down
6 changes: 3 additions & 3 deletions src/mock_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,12 +120,12 @@ impl MockService {
let mut response_bytes = response_contents.contents.value()
.unwrap_or_default();
trace!("Response message has {} bytes", response_bytes.len());
let response_message = decode_message(&mut response_bytes, &response_descriptor, &self.file_descriptor_set)
let response_message_fields = decode_message(&mut response_bytes, &response_descriptor, &self.file_descriptor_set)
.map_err(|err| {
error!("Failed to encode response message - {}", err);
Status::invalid_argument(err.to_string())
})?;
let mut message = DynamicMessage::new(&response_message, &self.file_descriptor_set);
let mut message = DynamicMessage::new(&response_descriptor, &response_message_fields, &self.file_descriptor_set);
self.apply_generators(&mut message, &response_contents).map_err(|err| {
error!("Failed to generate response message - {}", err);
Status::invalid_argument(err.to_string())
Expand Down Expand Up @@ -374,7 +374,7 @@ mod tests {
let bytes = BASE64.decode("EgoNAABAQBUAAIBA").unwrap();
let mut bytes2 = BytesMut::from(bytes.as_slice());
let fields = decode_message(&mut bytes2, input_message, fds).unwrap();
let request = DynamicMessage::new(fields.as_slice(), &file_descriptor_set);
let request = DynamicMessage::new(input_message, fields.as_slice(), &file_descriptor_set);

let mock_service = MockService {
file_descriptor_set: file_descriptor_set.clone(),
Expand Down
18 changes: 10 additions & 8 deletions src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ use pact_plugin_driver::utils::{
to_proto_value
};
use pact_verifier::verification_result::VerificationMismatchResult;
use prost_types::{FileDescriptorProto, FileDescriptorSet, MethodDescriptorProto, ServiceDescriptorProto};
use prost_types::{DescriptorProto, FileDescriptorProto, FileDescriptorSet, MethodDescriptorProto, ServiceDescriptorProto};
use prost_types::value::Kind;
use serde_json::Value;
use tonic::{Request, Response, Status};
Expand Down Expand Up @@ -462,9 +462,9 @@ impl ProtobufPactPlugin {
if body.is_empty() {
Ok(GenerateContentResponse::default())
} else {
let message = decode_message(&mut body, &message_descriptor, &descriptors)?;
debug!("message to generate = {:?}", message);
let generated_message = generate_protobuf_contents(&message, &content_type, &request.generators, &descriptors, request.test_mode())?;
let field_data = decode_message(&mut body, &message_descriptor, &descriptors)?;
debug!("message to generate = {:?}", field_data);
let generated_message = generate_protobuf_contents(&message_descriptor, &field_data, &content_type, &request.generators, &descriptors, request.test_mode())?;
Ok(GenerateContentResponse {
contents: Some(generated_message),
})
Expand Down Expand Up @@ -593,6 +593,7 @@ impl ProtobufPactPlugin {
/// Generate contents for the interaction
///
/// # Arguments:
/// * `message_descriptor` - Descriptor for the message
/// * `fields` - all fields in the message to generate contents for
/// * `content_type` - content type of the message, comes from generation request
/// * `generators` - map of generators, comes from generation request
Expand All @@ -606,13 +607,14 @@ impl ProtobufPactPlugin {
/// * `content_type_hint` - always `ContentTypeHint::Binary`
#[instrument(level = "trace")]
fn generate_protobuf_contents(
message_descriptor: &DescriptorProto,
fields: &Vec<ProtobufField>,
content_type: &ContentType,
generators: &HashMap<String, proto::Generator>,
all_descriptors: &FileDescriptorSet,
mode: TestMode
) -> anyhow::Result<Body> {
let mut message: DynamicMessage = DynamicMessage::new(fields, all_descriptors, );
let mut message: DynamicMessage = DynamicMessage::new(message_descriptor, fields, all_descriptors);
let variant_matcher = NoopVariantMatcher {};
let vm_boxed = variant_matcher.boxed();
let context = hashmap!{};
Expand Down Expand Up @@ -957,8 +959,8 @@ impl PactPlugin for ProtobufPactPlugin {
let config = proto_struct_to_map(&request.config.clone().unwrap_or_default());
let test_context = config.iter().map(|(k, v)| (k.as_str(), v.clone())).collect();
let decoded_body = match decode_message(&mut raw_request_body, &input_message, &all_file_desc) {
Ok(message) => {
let mut message = DynamicMessage::new(&message, &all_file_desc);
Ok(field_values) => {
let mut message = DynamicMessage::new(&input_message, &field_values, &all_file_desc);
if let Err(err) = message.apply_generators(
interaction.request.generators.categories.get(&GeneratorCategory::BODY),
&GeneratorTestMode::Provider,
Expand All @@ -967,7 +969,7 @@ impl PactPlugin for ProtobufPactPlugin {
return Ok(Self::verification_preparation_error_response(err.to_string()));
}
message
},
}
Err(err) => {
return Ok(Self::verification_preparation_error_response(err.to_string()));
}
Expand Down
2 changes: 1 addition & 1 deletion src/verification.rs
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ fn build_grpc_request(
trace!(?body, ?metadata, ?file_desc, ?input_desc, ">> build_grpc_request");
let mut bytes = body.value().unwrap_or_default();
let message_fields = decode_message(&mut bytes, input_desc, file_desc)?;
let mut request = Request::new(DynamicMessage::new(&message_fields, file_desc));
let mut request = Request::new(DynamicMessage::new(input_desc, &message_fields, file_desc));
let request_metadata = request.metadata_mut();
for (key, md) in metadata {
if key != "request-path" {
Expand Down
7 changes: 2 additions & 5 deletions tests/mock_server_tests.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use std::panic::catch_unwind;
use std::path::Path;
use base64::Engine;
use bytes::{BufMut, BytesMut};

use expectest::prelude::*;
use pact_consumer::mock_server::StartMockServerAsync;
Expand Down Expand Up @@ -116,10 +115,6 @@ async fn each_value_matcher() {
data: ProtobufFieldData::String("value2".to_string())
};
let fields = vec![ field, field2 ];
let message = DynamicMessage::new(fields.as_slice(), &fds);
let mut buffer = BytesMut::new();
buffer.put_u8(0);
message.write_to(&mut buffer).unwrap();

let mut conn = tonic::transport::Endpoint::from_shared(url.to_string())
.unwrap()
Expand All @@ -139,6 +134,8 @@ async fn each_value_matcher() {
let codec = PactCodec::new(&fds, &input_message, &output_message, &interaction);
let mut grpc = tonic::client::Grpc::new(conn);
let path = http::uri::PathAndQuery::try_from("/com.pact.protobuf.example.Test/GetValues").unwrap();

let message = DynamicMessage::new(&input_message, fields.as_slice(), &fds);
grpc.unary(Request::new(message), path, codec).await.unwrap();
}

0 comments on commit 8452e25

Please sign in to comment.