fakecloud_cloudformation/
xml_responses.rs1use crate::state::{Stack, StackResource};
2
3use fakecloud_aws::xml::xml_escape;
4
5pub fn create_stack_response(stack_id: &str, request_id: &str) -> String {
6 format!(
7 r#"<CreateStackResponse xmlns="http://cloudformation.amazonaws.com/doc/2010-05-15/">
8 <CreateStackResult>
9 <StackId>{stack_id}</StackId>
10 </CreateStackResult>
11 <ResponseMetadata>
12 <RequestId>{request_id}</RequestId>
13 </ResponseMetadata>
14</CreateStackResponse>"#,
15 stack_id = xml_escape(stack_id),
16 request_id = xml_escape(request_id),
17 )
18}
19
20pub fn update_stack_response(stack_id: &str, request_id: &str) -> String {
21 format!(
22 r#"<UpdateStackResponse xmlns="http://cloudformation.amazonaws.com/doc/2010-05-15/">
23 <UpdateStackResult>
24 <StackId>{stack_id}</StackId>
25 </UpdateStackResult>
26 <ResponseMetadata>
27 <RequestId>{request_id}</RequestId>
28 </ResponseMetadata>
29</UpdateStackResponse>"#,
30 stack_id = xml_escape(stack_id),
31 request_id = xml_escape(request_id),
32 )
33}
34
35pub fn delete_stack_response(request_id: &str) -> String {
36 format!(
37 r#"<DeleteStackResponse xmlns="http://cloudformation.amazonaws.com/doc/2010-05-15/">
38 <ResponseMetadata>
39 <RequestId>{request_id}</RequestId>
40 </ResponseMetadata>
41</DeleteStackResponse>"#,
42 request_id = xml_escape(request_id),
43 )
44}
45
46pub fn describe_stacks_response(stacks: &[Stack], request_id: &str) -> String {
47 let members: String = stacks
48 .iter()
49 .map(stack_member_xml)
50 .collect::<Vec<_>>()
51 .join("\n");
52
53 format!(
54 r#"<DescribeStacksResponse xmlns="http://cloudformation.amazonaws.com/doc/2010-05-15/">
55 <DescribeStacksResult>
56 <Stacks>
57{members}
58 </Stacks>
59 </DescribeStacksResult>
60 <ResponseMetadata>
61 <RequestId>{request_id}</RequestId>
62 </ResponseMetadata>
63</DescribeStacksResponse>"#,
64 request_id = xml_escape(request_id),
65 )
66}
67
68fn stack_member_xml(stack: &Stack) -> String {
69 let tags_xml = if stack.tags.is_empty() {
70 String::new()
71 } else {
72 let tags: String = stack
73 .tags
74 .iter()
75 .map(|(k, v)| {
76 format!(
77 " <member>\n <Key>{}</Key>\n <Value>{}</Value>\n </member>",
78 xml_escape(k),
79 xml_escape(v),
80 )
81 })
82 .collect::<Vec<_>>()
83 .join("\n");
84 format!("\n <Tags>\n{tags}\n </Tags>")
85 };
86
87 let params_xml = if stack.parameters.is_empty() {
88 String::new()
89 } else {
90 let params: String = stack
91 .parameters
92 .iter()
93 .map(|(k, v)| {
94 format!(
95 " <member>\n <ParameterKey>{}</ParameterKey>\n <ParameterValue>{}</ParameterValue>\n </member>",
96 xml_escape(k),
97 xml_escape(v),
98 )
99 })
100 .collect::<Vec<_>>()
101 .join("\n");
102 format!("\n <Parameters>\n{params}\n </Parameters>")
103 };
104
105 let description_xml = stack
106 .description
107 .as_ref()
108 .map(|d| format!("\n <Description>{}</Description>", xml_escape(d)))
109 .unwrap_or_default();
110
111 let notification_arns_xml = if stack.notification_arns.is_empty() {
112 String::new()
113 } else {
114 let members: String = stack
115 .notification_arns
116 .iter()
117 .map(|arn| format!(" <member>{}</member>", xml_escape(arn)))
118 .collect::<Vec<_>>()
119 .join("\n");
120 format!("\n <NotificationARNs>\n{members}\n </NotificationARNs>")
121 };
122
123 format!(
124 r#" <member>
125 <StackName>{name}</StackName>
126 <StackId>{id}</StackId>
127 <StackStatus>{status}</StackStatus>
128 <CreationTime>{created}</CreationTime>{description_xml}{tags_xml}{params_xml}{notification_arns_xml}
129 </member>"#,
130 name = xml_escape(&stack.name),
131 id = xml_escape(&stack.stack_id),
132 status = xml_escape(&stack.status),
133 created = stack.created_at.format("%Y-%m-%dT%H:%M:%SZ"),
134 )
135}
136
137pub fn list_stacks_response(stacks: &[Stack], request_id: &str) -> String {
138 let summaries: String = stacks
139 .iter()
140 .map(|s| {
141 format!(
142 r#" <member>
143 <StackName>{name}</StackName>
144 <StackId>{id}</StackId>
145 <StackStatus>{status}</StackStatus>
146 <CreationTime>{created}</CreationTime>
147 </member>"#,
148 name = xml_escape(&s.name),
149 id = xml_escape(&s.stack_id),
150 status = xml_escape(&s.status),
151 created = s.created_at.format("%Y-%m-%dT%H:%M:%SZ"),
152 )
153 })
154 .collect::<Vec<_>>()
155 .join("\n");
156
157 format!(
158 r#"<ListStacksResponse xmlns="http://cloudformation.amazonaws.com/doc/2010-05-15/">
159 <ListStacksResult>
160 <StackSummaries>
161{summaries}
162 </StackSummaries>
163 </ListStacksResult>
164 <ResponseMetadata>
165 <RequestId>{request_id}</RequestId>
166 </ResponseMetadata>
167</ListStacksResponse>"#,
168 request_id = xml_escape(request_id),
169 )
170}
171
172pub fn list_stack_resources_response(resources: &[StackResource], request_id: &str) -> String {
173 let summaries: String = resources
174 .iter()
175 .map(stack_resource_summary_xml)
176 .collect::<Vec<_>>()
177 .join("\n");
178
179 format!(
180 r#"<ListStackResourcesResponse xmlns="http://cloudformation.amazonaws.com/doc/2010-05-15/">
181 <ListStackResourcesResult>
182 <StackResourceSummaries>
183{summaries}
184 </StackResourceSummaries>
185 </ListStackResourcesResult>
186 <ResponseMetadata>
187 <RequestId>{request_id}</RequestId>
188 </ResponseMetadata>
189</ListStackResourcesResponse>"#,
190 request_id = xml_escape(request_id),
191 )
192}
193
194fn stack_resource_summary_xml(resource: &StackResource) -> String {
195 format!(
196 r#" <member>
197 <LogicalResourceId>{logical_id}</LogicalResourceId>
198 <PhysicalResourceId>{physical_id}</PhysicalResourceId>
199 <ResourceType>{resource_type}</ResourceType>
200 <ResourceStatus>{status}</ResourceStatus>
201 </member>"#,
202 logical_id = xml_escape(&resource.logical_id),
203 physical_id = xml_escape(&resource.physical_id),
204 resource_type = xml_escape(&resource.resource_type),
205 status = xml_escape(&resource.status),
206 )
207}
208
209pub fn describe_stack_resources_response(
210 resources: &[StackResource],
211 stack_name: &str,
212 request_id: &str,
213) -> String {
214 let members: String = resources
215 .iter()
216 .map(|r| {
217 format!(
218 r#" <member>
219 <StackName>{stack_name}</StackName>
220 <LogicalResourceId>{logical_id}</LogicalResourceId>
221 <PhysicalResourceId>{physical_id}</PhysicalResourceId>
222 <ResourceType>{resource_type}</ResourceType>
223 <ResourceStatus>{status}</ResourceStatus>
224 </member>"#,
225 stack_name = xml_escape(stack_name),
226 logical_id = xml_escape(&r.logical_id),
227 physical_id = xml_escape(&r.physical_id),
228 resource_type = xml_escape(&r.resource_type),
229 status = xml_escape(&r.status),
230 )
231 })
232 .collect::<Vec<_>>()
233 .join("\n");
234
235 format!(
236 r#"<DescribeStackResourcesResponse xmlns="http://cloudformation.amazonaws.com/doc/2010-05-15/">
237 <DescribeStackResourcesResult>
238 <StackResources>
239{members}
240 </StackResources>
241 </DescribeStackResourcesResult>
242 <ResponseMetadata>
243 <RequestId>{request_id}</RequestId>
244 </ResponseMetadata>
245</DescribeStackResourcesResponse>"#,
246 request_id = xml_escape(request_id),
247 )
248}
249
250pub fn get_template_response(template_body: &str, request_id: &str) -> String {
251 format!(
252 r#"<GetTemplateResponse xmlns="http://cloudformation.amazonaws.com/doc/2010-05-15/">
253 <GetTemplateResult>
254 <TemplateBody>{template_body}</TemplateBody>
255 </GetTemplateResult>
256 <ResponseMetadata>
257 <RequestId>{request_id}</RequestId>
258 </ResponseMetadata>
259</GetTemplateResponse>"#,
260 template_body = xml_escape(template_body),
261 request_id = xml_escape(request_id),
262 )
263}