Skip to content

Commit daf5222

Browse files
authored
[Rust Server] Refactor Mustache templates for request/response body handling (#19347)
* [Rust Server] Refactor Mustache templates for request/response body handling * Update samples
1 parent 8f7354a commit daf5222

27 files changed

Lines changed: 1644 additions & 1143 deletions

File tree

modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustServerCodegen.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -821,12 +821,15 @@ private void postProcessOperationWithModels(CodegenOperation op, List<ModelMap>
821821
} else if (isMimetypePlain(mediaType)) {
822822
consumesPlainText = true;
823823
} else if (isMimetypeWwwFormUrlEncoded(mediaType)) {
824+
op.vendorExtensions.put("x-consumes-form", true);
824825
additionalProperties.put("usesUrlEncodedForm", true);
825826
} else if (isMimetypeMultipartFormData(mediaType)) {
826827
op.vendorExtensions.put("x-consumes-multipart", true);
828+
op.vendorExtensions.put("x-consumes-multipart-form", true);
827829
additionalProperties.put("apiUsesMultipartFormData", true);
828830
additionalProperties.put("apiUsesMultipart", true);
829831
} else if (isMimetypeMultipartRelated(mediaType)) {
832+
op.vendorExtensions.put("x-consumes-multipart", true);
830833
op.vendorExtensions.put("x-consumes-multipart-related", true);
831834
additionalProperties.put("apiUsesMultipartRelated", true);
832835
additionalProperties.put("apiUsesMultipart", true);
@@ -835,15 +838,23 @@ private void postProcessOperationWithModels(CodegenOperation op, List<ModelMap>
835838
}
836839
}
837840

841+
if (op.bodyParams.size() > 0 || op.formParams.size() > 0){
842+
op.vendorExtensions.put("x-has-request-body", true);
843+
}
844+
838845
String underscoredOperationId = underscore(op.operationId).toUpperCase(Locale.ROOT);
846+
839847
if (op.bodyParam != null) {
840848
// Default to consuming json
841849
op.bodyParam.vendorExtensions.put("x-uppercase-operation-id", underscoredOperationId);
842850
if (consumesXml) {
851+
op.vendorExtensions.put("x-consumes-basic", true);
843852
op.bodyParam.vendorExtensions.put("x-consumes-xml", true);
844853
} else if (consumesPlainText) {
854+
op.vendorExtensions.put("x-consumes-basic", true);
845855
op.bodyParam.vendorExtensions.put("x-consumes-plain-text", true);
846856
} else {
857+
op.vendorExtensions.put("x-consumes-basic", true);
847858
op.bodyParam.vendorExtensions.put("x-consumes-json", true);
848859
}
849860
}
@@ -855,10 +866,13 @@ private void postProcessOperationWithModels(CodegenOperation op, List<ModelMap>
855866

856867
// Default to producing json if nothing else is specified
857868
if (consumesXml) {
869+
op.vendorExtensions.put("x-consumes-basic", true);
858870
param.vendorExtensions.put("x-consumes-xml", true);
859871
} else if (consumesPlainText) {
872+
op.vendorExtensions.put("x-consumes-basic", true);
860873
param.vendorExtensions.put("x-consumes-plain-text", true);
861874
} else {
875+
op.vendorExtensions.put("x-consumes-basic", true);
862876
param.vendorExtensions.put("x-consumes-json", true);
863877
}
864878
}

modules/openapi-generator/src/main/resources/rust-server/client-operation.mustache

Lines changed: 4 additions & 234 deletions
Original file line numberDiff line numberDiff line change
@@ -85,217 +85,7 @@
8585
Ok(req) => req,
8686
Err(e) => return Err(ApiError(format!("Unable to create request: {}", e)))
8787
};
88-
89-
{{#vendorExtensions}}
90-
{{#x-consumes-multipart}}
91-
let (body_string, multipart_header) = {
92-
let mut multipart = Multipart::new();
93-
94-
{{#vendorExtensions}}
95-
{{#formParams}}
96-
{{#-first}}
97-
// For each parameter, encode as appropriate and add to the multipart body as a stream.
98-
{{/-first}}
99-
100-
{{^isByteArray}}
101-
{{#jsonSchema}}
102-
let {{{paramName}}}_str = match serde_json::to_string(&param_{{{paramName}}}) {
103-
Ok(str) => str,
104-
Err(e) => return Err(ApiError(format!("Unable to serialize {{{paramName}}} to string: {}", e))),
105-
};
106-
107-
let {{{paramName}}}_vec = {{{paramName}}}_str.as_bytes().to_vec();
108-
let {{{paramName}}}_mime = mime_0_2::Mime::from_str("application/json").expect("impossible to fail to parse");
109-
let {{{paramName}}}_cursor = Cursor::new({{{paramName}}}_vec);
110-
111-
multipart.add_stream("{{{paramName}}}", {{{paramName}}}_cursor, None as Option<&str>, Some({{{paramName}}}_mime));
112-
{{/jsonSchema}}
113-
{{/isByteArray}}
114-
115-
{{#isByteArray}}
116-
let {{{paramName}}}_vec = param_{{{paramName}}}.to_vec();
117-
118-
let {{{paramName}}}_mime = match mime_0_2::Mime::from_str("application/octet-stream") {
119-
Ok(mime) => mime,
120-
Err(err) => return Err(ApiError(format!("Unable to get mime type: {:?}", err))),
121-
};
122-
123-
let {{{paramName}}}_cursor = Cursor::new({{{paramName}}}_vec);
124-
125-
let filename = None as Option<&str> ;
126-
multipart.add_stream("{{{paramName}}}", {{{paramName}}}_cursor, filename, Some({{{paramName}}}_mime));
127-
{{/isByteArray}}
128-
{{/formParams}}
129-
{{/vendorExtensions}}
130-
131-
let mut fields = match multipart.prepare() {
132-
Ok(fields) => fields,
133-
Err(err) => return Err(ApiError(format!("Unable to build request: {}", err))),
134-
};
135-
136-
let mut body_string = String::new();
137-
138-
match fields.read_to_string(&mut body_string) {
139-
Ok(_) => (),
140-
Err(err) => return Err(ApiError(format!("Unable to build body: {}", err))),
141-
}
142-
143-
let boundary = fields.boundary();
144-
145-
let multipart_header = format!("multipart/form-data;boundary={}", boundary);
146-
147-
(body_string, multipart_header)
148-
};
149-
150-
*request.body_mut() = Body::from(body_string);
151-
152-
request.headers_mut().insert(CONTENT_TYPE, match HeaderValue::from_str(&multipart_header) {
153-
Ok(h) => h,
154-
Err(e) => return Err(ApiError(format!("Unable to create header: {} - {}", multipart_header, e)))
155-
});
156-
157-
{{/x-consumes-multipart}}
158-
{{^x-consumes-multipart}}
159-
{{#vendorExtensions}}
160-
{{^x-consumes-multipart-related}}
161-
{{#formParams}}
162-
{{#-first}}
163-
let params = &[
164-
{{/-first}}
165-
("{{{baseName}}}", {{#vendorExtensions}}{{#required}}Some({{#isString}}param_{{{paramName}}}{{/isString}}{{^isString}}format!("{:?}", param_{{{paramName}}}){{/isString}}){{/required}}{{^required}}{{#isString}}param_{{{paramName}}}{{/isString}}{{^isString}}param_{{{paramName}}}.map(|param| format!("{:?}", param)){{/isString}}{{/required}}{{/vendorExtensions}}),
166-
{{#-last}}
167-
];
168-
let body = serde_urlencoded::to_string(params).expect("impossible to fail to serialize");
169-
170-
let header = "{{#consumes}}{{#-first}}{{{mediaType}}}{{/-first}}{{/consumes}}{{^consumes}}application/json{{/consumes}}";
171-
request.headers_mut().insert(CONTENT_TYPE, match HeaderValue::from_str(header) {
172-
Ok(h) => h,
173-
Err(e) => return Err(ApiError(format!("Unable to create header: {} - {}", header, e)))
174-
});
175-
*request.body_mut() = Body::from(body.into_bytes());
176-
{{/-last}}
177-
{{/formParams}}
178-
{{/x-consumes-multipart-related}}
179-
{{#x-consumes-multipart-related}}
180-
{{#formParams}}
181-
{{#-first}}
182-
// Construct the Body for a multipart/related request. The mime 0.2.6 library
183-
// does not parse quoted-string parameters correctly. The boundary doesn't
184-
// need to be a quoted string if it does not contain a '/', hence ensure
185-
// no such boundary is used.
186-
let mut boundary = generate_boundary();
187-
for b in boundary.iter_mut() {
188-
if b == &(b'/') {
189-
*b = b'=';
190-
}
191-
}
192-
193-
let mut body_parts = vec![];
194-
{{/-first}}
195-
196-
{{#required}}
197-
{
198-
{{/required}}
199-
{{^required}}
200-
if let Some({{{paramName}}}) = param_{{{paramName}}} {
201-
{{/required}}
202-
let part = Node::Part(Part {
203-
headers: {
204-
let mut h = Headers::new();
205-
h.set(ContentType("{{{contentType}}}".parse().unwrap()));
206-
h.set_raw("Content-ID", vec![b"{{{baseName}}}".to_vec()]);
207-
h
208-
},
209-
{{#isBinary}}
210-
body: {{#required}}param_{{/required}}{{{paramName}}}.0,
211-
{{/isBinary}}
212-
{{^isBinary}}
213-
body: serde_json::to_string(&{{{paramName}}})
214-
.expect("Impossible to fail to serialize")
215-
.into_bytes(),
216-
{{/isBinary}}
217-
});
218-
body_parts.push(part);
219-
}
220-
{{#-last}}
221-
222-
// Write the body into a vec.
223-
let mut body: Vec<u8> = vec![];
224-
write_multipart(&mut body, &boundary, &body_parts)
225-
.expect("Failed to write multipart body");
226-
227-
// Add the message body to the request object.
228-
*request.body_mut() = Body::from(body);
229-
230-
let header = "{{#consumes}}{{#-first}}{{{mediaType}}}{{/-first}}{{/consumes}}{{^consumes}}application/json{{/consumes}}";
231-
request.headers_mut().insert(CONTENT_TYPE,
232-
match HeaderValue::from_bytes(
233-
&[header.as_bytes(), "; boundary=".as_bytes(), &boundary, "; type=\"application/json\"".as_bytes()].concat()
234-
) {
235-
Ok(h) => h,
236-
Err(e) => return Err(ApiError(format!("Unable to create header: {} - {}", header, e)))
237-
});
238-
239-
{{/-last}}
240-
{{/formParams}}
241-
{{/x-consumes-multipart-related}}
242-
{{/vendorExtensions}}
243-
{{#bodyParam}}
244-
{{#-first}}
245-
// Body parameter
246-
{{/-first}}
247-
{{#vendorExtensions}}
248-
{{#x-consumes-plain-text}}
249-
{{#isByteArray}}
250-
let body = param_{{{paramName}}}.0;
251-
{{/isByteArray}}
252-
{{^isByteArray}}
253-
let body = param_{{{paramName}}};
254-
{{/isByteArray}}
255-
{{/x-consumes-plain-text}}
256-
{{#required}}
257-
{{#x-consumes-xml}}
258-
let body = param_{{{paramName}}}.as_xml();
259-
{{/x-consumes-xml}}
260-
{{#x-consumes-json}}
261-
let body = serde_json::to_string(&param_{{{paramName}}}).expect("impossible to fail to serialize");
262-
{{/x-consumes-json}}
263-
{{/required}}
264-
{{^required}}
265-
let body = param_{{{paramName}}}.map(|ref body| {
266-
{{#x-consumes-xml}}
267-
body.as_xml()
268-
{{/x-consumes-xml}}
269-
{{#x-consumes-json}}
270-
serde_json::to_string(body).expect("impossible to fail to serialize")
271-
{{/x-consumes-json}}
272-
});
273-
{{/required}}
274-
{{/vendorExtensions}}
275-
{{#-last}}
276-
277-
{{/-last}}
278-
{{/bodyParam}}
279-
{{#bodyParam}}
280-
{{^required}}
281-
if let Some(body) = body {
282-
{{/required}}
283-
*request.body_mut() = Body::from(body);
284-
{{^required}}
285-
}
286-
{{/required}}
287-
288-
let header = "{{#consumes}}{{#-first}}{{{mediaType}}}{{/-first}}{{/consumes}}{{^consumes}}application/json{{/consumes}}";
289-
request.headers_mut().insert(CONTENT_TYPE, match HeaderValue::from_str(header) {
290-
Ok(h) => h,
291-
Err(e) => return Err(ApiError(format!("Unable to create header: {} - {}", header, e)))
292-
});
293-
{{#-last}}
294-
295-
{{/-last}}
296-
{{/bodyParam}}
297-
{{/x-consumes-multipart}}
298-
{{/vendorExtensions}}
88+
{{>client-request-body-instance}}
29989
let header = HeaderValue::from_str(Has::<XSpanIdString>::get(context).0.as_str());
30090
request.headers_mut().insert(HeaderName::from_static("x-span-id"), match header {
30191
Ok(h) => h,
@@ -421,29 +211,9 @@
421211
let body = body
422212
.into_raw()
423213
.map_err(|e| ApiError(format!("Failed to read response: {}", e))).await?;
424-
{{#vendorExtensions}}
425-
{{#x-produces-bytes}}
426-
let body = swagger::ByteArray(body.to_vec());
427-
{{/x-produces-bytes}}
428-
{{^x-produces-bytes}}
429-
let body = str::from_utf8(&body)
430-
.map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?;
431-
{{#x-produces-xml}}
432-
// ToDo: this will move to swagger-rs and become a standard From conversion trait
433-
// once https://github.com/RReverser/serde-xml-rs/pull/45 is accepted upstream
434-
let body = serde_xml_rs::from_str::<{{{dataType}}}>(body)
435-
.map_err(|e| ApiError(format!("Response body did not match the schema: {}", e)))?;
436-
{{/x-produces-xml}}
437-
{{#x-produces-json}}
438-
let body = serde_json::from_str::<{{{dataType}}}>(body).map_err(|e| {
439-
ApiError(format!("Response body did not match the schema: {}", e))
440-
})?;
441-
{{/x-produces-json}}
442-
{{#x-produces-plain-text}}
443-
let body = body.to_string();
444-
{{/x-produces-plain-text}}
445-
{{/x-produces-bytes}}
446-
{{/vendorExtensions}}
214+
215+
{{>client-response-body-instance}}
216+
447217
Ok({{{operationId}}}Response::{{#vendorExtensions}}{{x-response-id}}{{/vendorExtensions}}
448218
{{^headers}}
449219
(body)
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
{{#vendorExtensions}}
2+
{{#x-consumes-multipart-form}}
3+
4+
// Consumes multipart/form body
5+
{{>client-request-body-multipart-form}}
6+
{{/x-consumes-multipart-form}}
7+
{{#x-consumes-multipart-related}}
8+
9+
// Consumes multipart/related body
10+
{{#formParams}}
11+
{{>generate-multipart-related}}
12+
{{/formParams}}
13+
14+
let header = "multipart/related";
15+
request.headers_mut().insert(CONTENT_TYPE,
16+
match HeaderValue::from_bytes(
17+
&[header.as_bytes(), "; boundary=".as_bytes(), &boundary, "; type=\"application/json\"".as_bytes()].concat()
18+
) {
19+
Ok(h) => h,
20+
Err(e) => return Err(ApiError(format!("Unable to create header: {} - {}", header, e)))
21+
});
22+
23+
// Add the message body to the request object.
24+
*request.body_mut() = Body::from(body);
25+
{{/x-consumes-multipart-related}}
26+
{{#x-consumes-form}}
27+
28+
// Consumes form body
29+
{{#formParams}}
30+
{{#-first}}
31+
let params = &[
32+
{{/-first}}
33+
("{{{baseName}}}", {{#vendorExtensions}}{{#required}}Some({{#isString}}param_{{{paramName}}}{{/isString}}{{^isString}}format!("{:?}", param_{{{paramName}}}){{/isString}}){{/required}}{{^required}}{{#isString}}param_{{{paramName}}}{{/isString}}{{^isString}}param_{{{paramName}}}.map(|param| format!("{:?}", param)){{/isString}}{{/required}}{{/vendorExtensions}}),
34+
{{#-last}}
35+
];
36+
let body = serde_urlencoded::to_string(params).expect("impossible to fail to serialize");
37+
38+
*request.body_mut() = Body::from(body.into_bytes());
39+
40+
let header = "{{#consumes}}{{#-first}}{{{mediaType}}}{{/-first}}{{/consumes}}{{^consumes}}application/json{{/consumes}}";
41+
request.headers_mut().insert(CONTENT_TYPE, match HeaderValue::from_str(header) {
42+
Ok(h) => h,
43+
Err(e) => return Err(ApiError(format!("Unable to create header: {} - {}", header, e)))
44+
});
45+
{{/-last}}
46+
{{/formParams}}
47+
{{/x-consumes-form}}
48+
{{#x-consumes-basic}}
49+
50+
// Consumes basic body
51+
{{#bodyParam}}
52+
// Body parameter
53+
{{^required}}
54+
if let Some(param_{{{paramName}}}) = param_{{{paramName}}} {
55+
{{/required}}
56+
{{#vendorExtensions}}
57+
{{#x-consumes-plain-text}}
58+
{{#isByteArray}}
59+
let body = param_{{{paramName}}}.0;
60+
{{/isByteArray}}
61+
{{^isByteArray}}
62+
let body = param_{{{paramName}}};
63+
{{/isByteArray}}
64+
{{/x-consumes-plain-text}}
65+
{{#x-consumes-xml}}
66+
let body = param_{{{paramName}}}.as_xml();
67+
{{/x-consumes-xml}}
68+
{{#x-consumes-json}}
69+
let body = serde_json::to_string(&param_{{{paramName}}}).expect("impossible to fail to serialize");
70+
{{/x-consumes-json}}
71+
{{/vendorExtensions}}
72+
*request.body_mut() = Body::from(body);
73+
{{^required}}
74+
}
75+
{{/required}}
76+
77+
let header = "{{#consumes}}{{#-first}}{{{mediaType}}}{{/-first}}{{/consumes}}{{^consumes}}application/json{{/consumes}}";
78+
request.headers_mut().insert(CONTENT_TYPE, match HeaderValue::from_str(header) {
79+
Ok(h) => h,
80+
Err(e) => return Err(ApiError(format!("Unable to create header: {} - {}", header, e)))
81+
});
82+
{{/bodyParam}}
83+
{{/x-consumes-basic}}
84+
{{/vendorExtensions}}

0 commit comments

Comments
 (0)