Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Rust Server] Refactor Mustache templates for request/response body handling #19347

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -821,12 +821,15 @@ private void postProcessOperationWithModels(CodegenOperation op, List<ModelMap>
} else if (isMimetypePlain(mediaType)) {
consumesPlainText = true;
} else if (isMimetypeWwwFormUrlEncoded(mediaType)) {
op.vendorExtensions.put("x-consumes-form", true);
additionalProperties.put("usesUrlEncodedForm", true);
} else if (isMimetypeMultipartFormData(mediaType)) {
op.vendorExtensions.put("x-consumes-multipart", true);
op.vendorExtensions.put("x-consumes-multipart-form", true);
additionalProperties.put("apiUsesMultipartFormData", true);
additionalProperties.put("apiUsesMultipart", true);
} else if (isMimetypeMultipartRelated(mediaType)) {
op.vendorExtensions.put("x-consumes-multipart", true);
op.vendorExtensions.put("x-consumes-multipart-related", true);
additionalProperties.put("apiUsesMultipartRelated", true);
additionalProperties.put("apiUsesMultipart", true);
Expand All @@ -835,15 +838,23 @@ private void postProcessOperationWithModels(CodegenOperation op, List<ModelMap>
}
}

if (op.bodyParams.size() > 0 || op.formParams.size() > 0){
op.vendorExtensions.put("x-has-request-body", true);
}

String underscoredOperationId = underscore(op.operationId).toUpperCase(Locale.ROOT);

if (op.bodyParam != null) {
// Default to consuming json
op.bodyParam.vendorExtensions.put("x-uppercase-operation-id", underscoredOperationId);
if (consumesXml) {
op.vendorExtensions.put("x-consumes-basic", true);
op.bodyParam.vendorExtensions.put("x-consumes-xml", true);
} else if (consumesPlainText) {
op.vendorExtensions.put("x-consumes-basic", true);
op.bodyParam.vendorExtensions.put("x-consumes-plain-text", true);
} else {
op.vendorExtensions.put("x-consumes-basic", true);
op.bodyParam.vendorExtensions.put("x-consumes-json", true);
}
}
Expand All @@ -855,10 +866,13 @@ private void postProcessOperationWithModels(CodegenOperation op, List<ModelMap>

// Default to producing json if nothing else is specified
if (consumesXml) {
op.vendorExtensions.put("x-consumes-basic", true);
param.vendorExtensions.put("x-consumes-xml", true);
} else if (consumesPlainText) {
op.vendorExtensions.put("x-consumes-basic", true);
param.vendorExtensions.put("x-consumes-plain-text", true);
} else {
op.vendorExtensions.put("x-consumes-basic", true);
param.vendorExtensions.put("x-consumes-json", true);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,217 +85,7 @@
Ok(req) => req,
Err(e) => return Err(ApiError(format!("Unable to create request: {}", e)))
};

{{#vendorExtensions}}
{{#x-consumes-multipart}}
let (body_string, multipart_header) = {
let mut multipart = Multipart::new();

{{#vendorExtensions}}
{{#formParams}}
{{#-first}}
// For each parameter, encode as appropriate and add to the multipart body as a stream.
{{/-first}}

{{^isByteArray}}
{{#jsonSchema}}
let {{{paramName}}}_str = match serde_json::to_string(&param_{{{paramName}}}) {
Ok(str) => str,
Err(e) => return Err(ApiError(format!("Unable to serialize {{{paramName}}} to string: {}", e))),
};

let {{{paramName}}}_vec = {{{paramName}}}_str.as_bytes().to_vec();
let {{{paramName}}}_mime = mime_0_2::Mime::from_str("application/json").expect("impossible to fail to parse");
let {{{paramName}}}_cursor = Cursor::new({{{paramName}}}_vec);

multipart.add_stream("{{{paramName}}}", {{{paramName}}}_cursor, None as Option<&str>, Some({{{paramName}}}_mime));
{{/jsonSchema}}
{{/isByteArray}}

{{#isByteArray}}
let {{{paramName}}}_vec = param_{{{paramName}}}.to_vec();

let {{{paramName}}}_mime = match mime_0_2::Mime::from_str("application/octet-stream") {
Ok(mime) => mime,
Err(err) => return Err(ApiError(format!("Unable to get mime type: {:?}", err))),
};

let {{{paramName}}}_cursor = Cursor::new({{{paramName}}}_vec);

let filename = None as Option<&str> ;
multipart.add_stream("{{{paramName}}}", {{{paramName}}}_cursor, filename, Some({{{paramName}}}_mime));
{{/isByteArray}}
{{/formParams}}
{{/vendorExtensions}}

let mut fields = match multipart.prepare() {
Ok(fields) => fields,
Err(err) => return Err(ApiError(format!("Unable to build request: {}", err))),
};

let mut body_string = String::new();

match fields.read_to_string(&mut body_string) {
Ok(_) => (),
Err(err) => return Err(ApiError(format!("Unable to build body: {}", err))),
}

let boundary = fields.boundary();

let multipart_header = format!("multipart/form-data;boundary={}", boundary);

(body_string, multipart_header)
};

*request.body_mut() = Body::from(body_string);

request.headers_mut().insert(CONTENT_TYPE, match HeaderValue::from_str(&multipart_header) {
Ok(h) => h,
Err(e) => return Err(ApiError(format!("Unable to create header: {} - {}", multipart_header, e)))
});

{{/x-consumes-multipart}}
{{^x-consumes-multipart}}
{{#vendorExtensions}}
{{^x-consumes-multipart-related}}
{{#formParams}}
{{#-first}}
let params = &[
{{/-first}}
("{{{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}}),
{{#-last}}
];
let body = serde_urlencoded::to_string(params).expect("impossible to fail to serialize");

let header = "{{#consumes}}{{#-first}}{{{mediaType}}}{{/-first}}{{/consumes}}{{^consumes}}application/json{{/consumes}}";
request.headers_mut().insert(CONTENT_TYPE, match HeaderValue::from_str(header) {
Ok(h) => h,
Err(e) => return Err(ApiError(format!("Unable to create header: {} - {}", header, e)))
});
*request.body_mut() = Body::from(body.into_bytes());
{{/-last}}
{{/formParams}}
{{/x-consumes-multipart-related}}
{{#x-consumes-multipart-related}}
{{#formParams}}
{{#-first}}
// Construct the Body for a multipart/related request. The mime 0.2.6 library
// does not parse quoted-string parameters correctly. The boundary doesn't
// need to be a quoted string if it does not contain a '/', hence ensure
// no such boundary is used.
let mut boundary = generate_boundary();
for b in boundary.iter_mut() {
if b == &(b'/') {
*b = b'=';
}
}

let mut body_parts = vec![];
{{/-first}}

{{#required}}
{
{{/required}}
{{^required}}
if let Some({{{paramName}}}) = param_{{{paramName}}} {
{{/required}}
let part = Node::Part(Part {
headers: {
let mut h = Headers::new();
h.set(ContentType("{{{contentType}}}".parse().unwrap()));
h.set_raw("Content-ID", vec![b"{{{baseName}}}".to_vec()]);
h
},
{{#isBinary}}
body: {{#required}}param_{{/required}}{{{paramName}}}.0,
{{/isBinary}}
{{^isBinary}}
body: serde_json::to_string(&{{{paramName}}})
.expect("Impossible to fail to serialize")
.into_bytes(),
{{/isBinary}}
});
body_parts.push(part);
}
{{#-last}}

// Write the body into a vec.
let mut body: Vec<u8> = vec![];
write_multipart(&mut body, &boundary, &body_parts)
.expect("Failed to write multipart body");

// Add the message body to the request object.
*request.body_mut() = Body::from(body);

let header = "{{#consumes}}{{#-first}}{{{mediaType}}}{{/-first}}{{/consumes}}{{^consumes}}application/json{{/consumes}}";
request.headers_mut().insert(CONTENT_TYPE,
match HeaderValue::from_bytes(
&[header.as_bytes(), "; boundary=".as_bytes(), &boundary, "; type=\"application/json\"".as_bytes()].concat()
) {
Ok(h) => h,
Err(e) => return Err(ApiError(format!("Unable to create header: {} - {}", header, e)))
});

{{/-last}}
{{/formParams}}
{{/x-consumes-multipart-related}}
{{/vendorExtensions}}
{{#bodyParam}}
{{#-first}}
// Body parameter
{{/-first}}
{{#vendorExtensions}}
{{#x-consumes-plain-text}}
{{#isByteArray}}
let body = param_{{{paramName}}}.0;
{{/isByteArray}}
{{^isByteArray}}
let body = param_{{{paramName}}};
{{/isByteArray}}
{{/x-consumes-plain-text}}
{{#required}}
{{#x-consumes-xml}}
let body = param_{{{paramName}}}.as_xml();
{{/x-consumes-xml}}
{{#x-consumes-json}}
let body = serde_json::to_string(&param_{{{paramName}}}).expect("impossible to fail to serialize");
{{/x-consumes-json}}
{{/required}}
{{^required}}
let body = param_{{{paramName}}}.map(|ref body| {
{{#x-consumes-xml}}
body.as_xml()
{{/x-consumes-xml}}
{{#x-consumes-json}}
serde_json::to_string(body).expect("impossible to fail to serialize")
{{/x-consumes-json}}
});
{{/required}}
{{/vendorExtensions}}
{{#-last}}

{{/-last}}
{{/bodyParam}}
{{#bodyParam}}
{{^required}}
if let Some(body) = body {
{{/required}}
*request.body_mut() = Body::from(body);
{{^required}}
}
{{/required}}

let header = "{{#consumes}}{{#-first}}{{{mediaType}}}{{/-first}}{{/consumes}}{{^consumes}}application/json{{/consumes}}";
request.headers_mut().insert(CONTENT_TYPE, match HeaderValue::from_str(header) {
Ok(h) => h,
Err(e) => return Err(ApiError(format!("Unable to create header: {} - {}", header, e)))
});
{{#-last}}

{{/-last}}
{{/bodyParam}}
{{/x-consumes-multipart}}
{{/vendorExtensions}}
{{>client-request-body-instance}}
let header = HeaderValue::from_str(Has::<XSpanIdString>::get(context).0.as_str());
request.headers_mut().insert(HeaderName::from_static("x-span-id"), match header {
Ok(h) => h,
Expand Down Expand Up @@ -421,29 +211,9 @@
let body = body
.into_raw()
.map_err(|e| ApiError(format!("Failed to read response: {}", e))).await?;
{{#vendorExtensions}}
{{#x-produces-bytes}}
let body = swagger::ByteArray(body.to_vec());
{{/x-produces-bytes}}
{{^x-produces-bytes}}
let body = str::from_utf8(&body)
.map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?;
{{#x-produces-xml}}
// ToDo: this will move to swagger-rs and become a standard From conversion trait
// once https://github.com/RReverser/serde-xml-rs/pull/45 is accepted upstream
let body = serde_xml_rs::from_str::<{{{dataType}}}>(body)
.map_err(|e| ApiError(format!("Response body did not match the schema: {}", e)))?;
{{/x-produces-xml}}
{{#x-produces-json}}
let body = serde_json::from_str::<{{{dataType}}}>(body).map_err(|e| {
ApiError(format!("Response body did not match the schema: {}", e))
})?;
{{/x-produces-json}}
{{#x-produces-plain-text}}
let body = body.to_string();
{{/x-produces-plain-text}}
{{/x-produces-bytes}}
{{/vendorExtensions}}

{{>client-response-body-instance}}

Ok({{{operationId}}}Response::{{#vendorExtensions}}{{x-response-id}}{{/vendorExtensions}}
{{^headers}}
(body)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
{{#vendorExtensions}}
{{#x-consumes-multipart-form}}

// Consumes multipart/form body
{{>client-request-body-multipart-form}}
{{/x-consumes-multipart-form}}
{{#x-consumes-multipart-related}}

// Consumes multipart/related body
{{#formParams}}
{{>generate-multipart-related}}
{{/formParams}}

let header = "multipart/related";
request.headers_mut().insert(CONTENT_TYPE,
match HeaderValue::from_bytes(
&[header.as_bytes(), "; boundary=".as_bytes(), &boundary, "; type=\"application/json\"".as_bytes()].concat()
) {
Ok(h) => h,
Err(e) => return Err(ApiError(format!("Unable to create header: {} - {}", header, e)))
});

// Add the message body to the request object.
*request.body_mut() = Body::from(body);
{{/x-consumes-multipart-related}}
{{#x-consumes-form}}

// Consumes form body
{{#formParams}}
{{#-first}}
let params = &[
{{/-first}}
("{{{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}}),
{{#-last}}
];
let body = serde_urlencoded::to_string(params).expect("impossible to fail to serialize");

*request.body_mut() = Body::from(body.into_bytes());

let header = "{{#consumes}}{{#-first}}{{{mediaType}}}{{/-first}}{{/consumes}}{{^consumes}}application/json{{/consumes}}";
request.headers_mut().insert(CONTENT_TYPE, match HeaderValue::from_str(header) {
Ok(h) => h,
Err(e) => return Err(ApiError(format!("Unable to create header: {} - {}", header, e)))
});
{{/-last}}
{{/formParams}}
{{/x-consumes-form}}
{{#x-consumes-basic}}

// Consumes basic body
{{#bodyParam}}
// Body parameter
{{^required}}
if let Some(param_{{{paramName}}}) = param_{{{paramName}}} {
{{/required}}
{{#vendorExtensions}}
{{#x-consumes-plain-text}}
{{#isByteArray}}
let body = param_{{{paramName}}}.0;
{{/isByteArray}}
{{^isByteArray}}
let body = param_{{{paramName}}};
{{/isByteArray}}
{{/x-consumes-plain-text}}
{{#x-consumes-xml}}
let body = param_{{{paramName}}}.as_xml();
{{/x-consumes-xml}}
{{#x-consumes-json}}
let body = serde_json::to_string(&param_{{{paramName}}}).expect("impossible to fail to serialize");
{{/x-consumes-json}}
{{/vendorExtensions}}
*request.body_mut() = Body::from(body);
{{^required}}
}
{{/required}}

let header = "{{#consumes}}{{#-first}}{{{mediaType}}}{{/-first}}{{/consumes}}{{^consumes}}application/json{{/consumes}}";
request.headers_mut().insert(CONTENT_TYPE, match HeaderValue::from_str(header) {
Ok(h) => h,
Err(e) => return Err(ApiError(format!("Unable to create header: {} - {}", header, e)))
});
{{/bodyParam}}
{{/x-consumes-basic}}
{{/vendorExtensions}}
Loading
Loading