aws_smithy_json/protocol/
aws_json_rpc.rs1use crate::codec::{JsonCodec, JsonCodecSettings};
23use aws_smithy_runtime_api::client::orchestrator::Metadata;
24use aws_smithy_schema::http_protocol::HttpRpcProtocol;
25use aws_smithy_schema::{shape_id, Schema, ShapeId};
26use aws_smithy_types::config_bag::ConfigBag;
27
28#[derive(Debug)]
30pub struct AwsJsonRpcProtocol {
31 inner: HttpRpcProtocol<JsonCodec>,
32 target_prefix: String,
33}
34
35impl AwsJsonRpcProtocol {
36 pub fn aws_json_1_0(target_prefix: impl Into<String>) -> Self {
41 Self::new(
42 shape_id!("aws.protocols", "awsJson1_0"),
43 "application/x-amz-json-1.0",
44 target_prefix.into(),
45 )
46 }
47
48 pub fn aws_json_1_1(target_prefix: impl Into<String>) -> Self {
52 Self::new(
53 shape_id!("aws.protocols", "awsJson1_1"),
54 "application/x-amz-json-1.1",
55 target_prefix.into(),
56 )
57 }
58
59 fn new(protocol_id: ShapeId, content_type: &'static str, target_prefix: String) -> Self {
60 let codec = JsonCodec::new(
61 JsonCodecSettings::builder()
62 .use_json_name(false)
63 .default_timestamp_format(aws_smithy_types::date_time::Format::EpochSeconds)
64 .build(),
65 );
66 Self {
67 inner: HttpRpcProtocol::new(protocol_id, codec, content_type),
68 target_prefix,
69 }
70 }
71}
72
73impl aws_smithy_schema::protocol::ClientProtocol for AwsJsonRpcProtocol {
74 fn protocol_id(&self) -> &ShapeId {
75 self.inner.protocol_id()
76 }
77
78 fn serialize_request(
79 &self,
80 input: &dyn aws_smithy_schema::serde::SerializableStruct,
81 input_schema: &Schema,
82 endpoint: &str,
83 cfg: &ConfigBag,
84 ) -> Result<aws_smithy_runtime_api::http::Request, aws_smithy_schema::serde::SerdeError> {
85 let mut request = self
86 .inner
87 .serialize_request(input, input_schema, endpoint, cfg)?;
88 if let Some(metadata) = cfg.load::<Metadata>() {
89 request.headers_mut().insert(
90 "X-Amz-Target",
91 format!("{}.{}", self.target_prefix, metadata.name()),
92 );
93 }
94 Ok(request)
95 }
96
97 fn deserialize_response<'a>(
98 &self,
99 response: &'a aws_smithy_runtime_api::http::Response,
100 output_schema: &Schema,
101 cfg: &ConfigBag,
102 ) -> Result<
103 Box<dyn aws_smithy_schema::serde::ShapeDeserializer + 'a>,
104 aws_smithy_schema::serde::SerdeError,
105 > {
106 self.inner
107 .deserialize_response(response, output_schema, cfg)
108 }
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114 use aws_smithy_schema::protocol::ClientProtocol;
115 use aws_smithy_schema::serde::{SerdeError, SerializableStruct, ShapeSerializer};
116 use aws_smithy_schema::ShapeType;
117 use aws_smithy_types::config_bag::Layer;
118
119 struct EmptyStruct;
120 impl SerializableStruct for EmptyStruct {
121 fn serialize_members(&self, _: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
122 Ok(())
123 }
124 }
125
126 static TEST_SCHEMA: aws_smithy_schema::Schema =
127 aws_smithy_schema::Schema::new(shape_id!("test", "Input"), ShapeType::Structure);
128
129 fn cfg_with_metadata(service: &str, operation: &str) -> ConfigBag {
130 let mut layer = Layer::new("test");
131 layer.store_put(Metadata::new(operation.to_string(), service.to_string()));
132 ConfigBag::of_layers(vec![layer])
133 }
134
135 #[test]
136 fn json_1_0_content_type() {
137 let request = AwsJsonRpcProtocol::aws_json_1_0("TestService")
138 .serialize_request(
139 &EmptyStruct,
140 &TEST_SCHEMA,
141 "https://example.com",
142 &ConfigBag::base(),
143 )
144 .unwrap();
145 assert_eq!(
146 request.headers().get("Content-Type").unwrap(),
147 "application/x-amz-json-1.0"
148 );
149 }
150
151 #[test]
152 fn json_1_1_content_type() {
153 let request = AwsJsonRpcProtocol::aws_json_1_1("TestService")
154 .serialize_request(
155 &EmptyStruct,
156 &TEST_SCHEMA,
157 "https://example.com",
158 &ConfigBag::base(),
159 )
160 .unwrap();
161 assert_eq!(
162 request.headers().get("Content-Type").unwrap(),
163 "application/x-amz-json-1.1"
164 );
165 }
166
167 #[test]
168 fn sets_x_amz_target() {
169 let cfg = cfg_with_metadata("MyService", "DoThing");
170 let request = AwsJsonRpcProtocol::aws_json_1_0("MyService")
171 .serialize_request(&EmptyStruct, &TEST_SCHEMA, "https://example.com", &cfg)
172 .unwrap();
173 assert_eq!(
174 request.headers().get("X-Amz-Target").unwrap(),
175 "MyService.DoThing"
176 );
177 }
178
179 #[test]
180 fn json_1_0_protocol_id() {
181 assert_eq!(
182 AwsJsonRpcProtocol::aws_json_1_0("Svc")
183 .protocol_id()
184 .as_str(),
185 "aws.protocols#awsJson1_0"
186 );
187 }
188
189 #[test]
190 fn json_1_1_protocol_id() {
191 assert_eq!(
192 AwsJsonRpcProtocol::aws_json_1_1("Svc")
193 .protocol_id()
194 .as_str(),
195 "aws.protocols#awsJson1_1"
196 );
197 }
198}