Skip to main content

fakecloud_cloudformation/
xml_responses.rs

1use 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    format!(
112        r#"      <member>
113        <StackName>{name}</StackName>
114        <StackId>{id}</StackId>
115        <StackStatus>{status}</StackStatus>
116        <CreationTime>{created}</CreationTime>{description_xml}{tags_xml}{params_xml}
117      </member>"#,
118        name = xml_escape(&stack.name),
119        id = xml_escape(&stack.stack_id),
120        status = xml_escape(&stack.status),
121        created = stack.created_at.format("%Y-%m-%dT%H:%M:%SZ"),
122    )
123}
124
125pub fn list_stacks_response(stacks: &[Stack], request_id: &str) -> String {
126    let summaries: String = stacks
127        .iter()
128        .map(|s| {
129            format!(
130                r#"      <member>
131        <StackName>{name}</StackName>
132        <StackId>{id}</StackId>
133        <StackStatus>{status}</StackStatus>
134        <CreationTime>{created}</CreationTime>
135      </member>"#,
136                name = xml_escape(&s.name),
137                id = xml_escape(&s.stack_id),
138                status = xml_escape(&s.status),
139                created = s.created_at.format("%Y-%m-%dT%H:%M:%SZ"),
140            )
141        })
142        .collect::<Vec<_>>()
143        .join("\n");
144
145    format!(
146        r#"<ListStacksResponse xmlns="http://cloudformation.amazonaws.com/doc/2010-05-15/">
147  <ListStacksResult>
148    <StackSummaries>
149{summaries}
150    </StackSummaries>
151  </ListStacksResult>
152  <ResponseMetadata>
153    <RequestId>{request_id}</RequestId>
154  </ResponseMetadata>
155</ListStacksResponse>"#,
156        request_id = xml_escape(request_id),
157    )
158}
159
160pub fn list_stack_resources_response(resources: &[StackResource], request_id: &str) -> String {
161    let summaries: String = resources
162        .iter()
163        .map(stack_resource_summary_xml)
164        .collect::<Vec<_>>()
165        .join("\n");
166
167    format!(
168        r#"<ListStackResourcesResponse xmlns="http://cloudformation.amazonaws.com/doc/2010-05-15/">
169  <ListStackResourcesResult>
170    <StackResourceSummaries>
171{summaries}
172    </StackResourceSummaries>
173  </ListStackResourcesResult>
174  <ResponseMetadata>
175    <RequestId>{request_id}</RequestId>
176  </ResponseMetadata>
177</ListStackResourcesResponse>"#,
178        request_id = xml_escape(request_id),
179    )
180}
181
182fn stack_resource_summary_xml(resource: &StackResource) -> String {
183    format!(
184        r#"      <member>
185        <LogicalResourceId>{logical_id}</LogicalResourceId>
186        <PhysicalResourceId>{physical_id}</PhysicalResourceId>
187        <ResourceType>{resource_type}</ResourceType>
188        <ResourceStatus>{status}</ResourceStatus>
189      </member>"#,
190        logical_id = xml_escape(&resource.logical_id),
191        physical_id = xml_escape(&resource.physical_id),
192        resource_type = xml_escape(&resource.resource_type),
193        status = xml_escape(&resource.status),
194    )
195}
196
197pub fn describe_stack_resources_response(
198    resources: &[StackResource],
199    stack_name: &str,
200    request_id: &str,
201) -> String {
202    let members: String = resources
203        .iter()
204        .map(|r| {
205            format!(
206                r#"      <member>
207        <StackName>{stack_name}</StackName>
208        <LogicalResourceId>{logical_id}</LogicalResourceId>
209        <PhysicalResourceId>{physical_id}</PhysicalResourceId>
210        <ResourceType>{resource_type}</ResourceType>
211        <ResourceStatus>{status}</ResourceStatus>
212      </member>"#,
213                stack_name = xml_escape(stack_name),
214                logical_id = xml_escape(&r.logical_id),
215                physical_id = xml_escape(&r.physical_id),
216                resource_type = xml_escape(&r.resource_type),
217                status = xml_escape(&r.status),
218            )
219        })
220        .collect::<Vec<_>>()
221        .join("\n");
222
223    format!(
224        r#"<DescribeStackResourcesResponse xmlns="http://cloudformation.amazonaws.com/doc/2010-05-15/">
225  <DescribeStackResourcesResult>
226    <StackResources>
227{members}
228    </StackResources>
229  </DescribeStackResourcesResult>
230  <ResponseMetadata>
231    <RequestId>{request_id}</RequestId>
232  </ResponseMetadata>
233</DescribeStackResourcesResponse>"#,
234        request_id = xml_escape(request_id),
235    )
236}
237
238pub fn get_template_response(template_body: &str, request_id: &str) -> String {
239    format!(
240        r#"<GetTemplateResponse xmlns="http://cloudformation.amazonaws.com/doc/2010-05-15/">
241  <GetTemplateResult>
242    <TemplateBody>{template_body}</TemplateBody>
243  </GetTemplateResult>
244  <ResponseMetadata>
245    <RequestId>{request_id}</RequestId>
246  </ResponseMetadata>
247</GetTemplateResponse>"#,
248        template_body = xml_escape(template_body),
249        request_id = xml_escape(request_id),
250    )
251}