Skip to content

Commit

Permalink
[Rust Server] fix handling of special characters in path param names (#…
Browse files Browse the repository at this point in the history
…4897)

* test for hyphen in path parameter name

* fix handling of hyphens in path parameter names
  • Loading branch information
andyleiserson authored and wing328 committed Jan 17, 2020
1 parent f48325a commit 67d23fc
Show file tree
Hide file tree
Showing 12 changed files with 283 additions and 47 deletions.
Expand Up @@ -552,6 +552,16 @@ public CodegenOperation fromOperation(String path, String httpMethod, Operation
Map<String, Schema> definitions = ModelUtils.getSchemas(this.openAPI);
CodegenOperation op = super.fromOperation(path, httpMethod, operation, servers);

String pathFormatString = op.path;
for (CodegenParameter param : op.pathParams) {
// Replace {baseName} with {paramName} for format string
String paramSearch = "{" + param.baseName + "}";
String paramReplace = "{" + param.paramName + "}";

pathFormatString = pathFormatString.replace(paramSearch, paramReplace);
}
op.vendorExtensions.put("pathFormatString", pathFormatString);

// The Rust code will need to contain a series of regular expressions.
// For performance, we'll construct these at start-of-day and re-use
// them. That means we need labels for them.
Expand Down Expand Up @@ -584,10 +594,18 @@ public CodegenOperation fromOperation(String path, String httpMethod, Operation
}
// Don't prefix with '^' so that the templates can put the
// basePath on the front.
pathSetEntry.put("pathRegEx", op.path.replace("{", "(?P<").replace("}", ">[^/?#]*)") + "$");
String pathRegEx = op.path;
for (CodegenParameter param : op.pathParams) {
// Replace {baseName} with (?P<paramName>[^/?#]*) for regex
String paramSearch = "{" + param.baseName + "}";
String paramReplace = "(?P<" + param.paramName + ">[^/?#]*)";

pathRegEx = pathRegEx.replace(paramSearch, paramReplace);
}

pathSetEntry.put("pathRegEx", pathRegEx + "$");
pathSetMap.put(pathId, pathSetEntry);
}

op.vendorExtensions.put("operation_id", underscore(op.operationId));
op.vendorExtensions.put("uppercase_operation_id", underscore(op.operationId).toUpperCase(Locale.ROOT));
op.vendorExtensions.put("path", op.path.replace("{", ":").replace("}", ""));
Expand Down
Expand Up @@ -262,8 +262,8 @@ impl<F, C> Api<C> for Client<F> where
{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}
fn {{#vendorExtensions}}{{{operation_id}}}{{/vendorExtensions}}(&self{{#allParams}}, param_{{{paramName}}}: {{^required}}Option<{{/required}}{{#isListContainer}}&{{/isListContainer}}{{{dataType}}}{{^required}}>{{/required}}{{/allParams}}, context: &C) -> Box<dyn Future<Item={{{operationId}}}Response, Error=ApiError>> {
let mut uri = format!(
"{}{{{basePathWithoutHost}}}{{path}}",
self.base_path{{#pathParams}}, {{{baseName}}}=utf8_percent_encode(&param_{{{paramName}}}.to_string(), ID_ENCODE_SET){{/pathParams}}
"{}{{{basePathWithoutHost}}}{{#vendorExtensions}}{{pathFormatString}}{{/vendorExtensions}}",
self.base_path{{#pathParams}}, {{{paramName}}}=utf8_percent_encode(&param_{{{paramName}}}.to_string(), ID_ENCODE_SET){{/pathParams}}
);

let mut query_string = self::url::form_urlencoded::Serializer::new("".to_owned());
Expand Down
Expand Up @@ -195,12 +195,12 @@ where
{{/hasPathParams}}
{{/vendorExtensions}}
{{#pathParams}}
let param_{{{paramName}}} = match percent_encoding::percent_decode(path_params["{{{baseName}}}"].as_bytes()).decode_utf8() {
let param_{{{paramName}}} = match percent_encoding::percent_decode(path_params["{{{paramName}}}"].as_bytes()).decode_utf8() {
Ok(param_{{{paramName}}}) => match param_{{{paramName}}}.parse::<{{{dataType}}}>() {
Ok(param_{{{paramName}}}) => param_{{{paramName}}},
Err(e) => return Box::new(future::ok(Response::new().with_status(StatusCode::BadRequest).with_body(format!("Couldn't parse path parameter {{{baseName}}}: {:?}", e)))),
},
Err(_) => return Box::new(future::ok(Response::new().with_status(StatusCode::BadRequest).with_body(format!("Couldn't percent-decode path parameter as UTF-8: {}", &path_params["{{{baseName}}}"]))))
Err(_) => return Box::new(future::ok(Response::new().with_status(StatusCode::BadRequest).with_body(format!("Couldn't percent-decode path parameter as UTF-8: {}", &path_params["{{{paramName}}}"]))))
};
{{/pathParams}}
{{#headerParams}}
Expand Down
Expand Up @@ -946,6 +946,25 @@ paths:
description: successful operation
schema:
$ref: '#/definitions/Client'
/fake/hyphenParam/{hyphen-param}:
get:
tags:
- fake
description: To test hyphen in path parameter name
operationId: hyphenParam
consumes:
- application/json
produces:
- application/json
parameters:
- name: hyphen-param
in: path
description: Parameter with hyphen in name
required: true
type: string
responses:
200:
description: Success
securityDefinitions:
petstore_auth:
type: oauth2
Expand Down
Expand Up @@ -66,6 +66,7 @@ cargo run --example client FakeOuterBooleanSerialize
cargo run --example client FakeOuterCompositeSerialize
cargo run --example client FakeOuterNumberSerialize
cargo run --example client FakeOuterStringSerialize
cargo run --example client HyphenParam
cargo run --example client TestBodyWithQueryParams
cargo run --example client TestClientModel
cargo run --example client TestEndpointParameters
Expand Down Expand Up @@ -131,6 +132,7 @@ Method | HTTP request | Description
[**fakeOuterCompositeSerialize**](docs/fake_api.md#fakeOuterCompositeSerialize) | **POST** /fake/outer/composite |
[**fakeOuterNumberSerialize**](docs/fake_api.md#fakeOuterNumberSerialize) | **POST** /fake/outer/number |
[**fakeOuterStringSerialize**](docs/fake_api.md#fakeOuterStringSerialize) | **POST** /fake/outer/string |
[**hyphenParam**](docs/fake_api.md#hyphenParam) | **GET** /fake/hyphenParam/{hyphen-param} |
[**testBodyWithQueryParams**](docs/fake_api.md#testBodyWithQueryParams) | **PUT** /fake/body-with-query-params |
[**testClientModel**](docs/fake_api.md#testClientModel) | **PATCH** /fake | To test \"client\" model
[**testEndpointParameters**](docs/fake_api.md#testEndpointParameters) | **POST** /fake | Fake endpoint for testing various parameters 假端點 偽のエンドポイント 가짜 엔드 포인트
Expand Down
Expand Up @@ -984,6 +984,23 @@ paths:
tags:
- $another-fake?
x-codegen-request-body-name: body
/fake/hyphenParam/{hyphen-param}:
get:
description: To test hyphen in path parameter name
operationId: hyphenParam
parameters:
- description: Parameter with hyphen in name
in: path
name: hyphen-param
required: true
schema:
type: string
responses:
200:
content: {}
description: Success
tags:
- fake
components:
schemas:
Order:
Expand Down
Expand Up @@ -8,6 +8,7 @@ Method | HTTP request | Description
**fakeOuterCompositeSerialize**](fake_api.md#fakeOuterCompositeSerialize) | **POST** /fake/outer/composite |
**fakeOuterNumberSerialize**](fake_api.md#fakeOuterNumberSerialize) | **POST** /fake/outer/number |
**fakeOuterStringSerialize**](fake_api.md#fakeOuterStringSerialize) | **POST** /fake/outer/string |
**hyphenParam**](fake_api.md#hyphenParam) | **GET** /fake/hyphenParam/{hyphen-param} |
**testBodyWithQueryParams**](fake_api.md#testBodyWithQueryParams) | **PUT** /fake/body-with-query-params |
**testClientModel**](fake_api.md#testClientModel) | **PATCH** /fake | To test \"client\" model
**testEndpointParameters**](fake_api.md#testEndpointParameters) | **POST** /fake | Fake endpoint for testing various parameters 假端點 偽のエンドポイント 가짜 엔드 포인트
Expand Down Expand Up @@ -152,6 +153,33 @@ No authorization required

[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)

# **hyphenParam**
> hyphenParam(hyphen_param)

To test hyphen in path parameter name

### Required Parameters

Name | Type | Description | Notes
------------- | ------------- | ------------- | -------------
**hyphen_param** | **String**| Parameter with hyphen in name |

### Return type

(empty response body)

### Authorization

No authorization required

### HTTP request headers

- **Content-Type**: Not defined
- **Accept**: Not defined

[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)

# **testBodyWithQueryParams**
> testBodyWithQueryParams(query, body)
Expand Down
Expand Up @@ -24,6 +24,7 @@ use petstore_with_fake_endpoints_models_for_testing::{ApiNoContext, ContextWrapp
FakeOuterCompositeSerializeResponse,
FakeOuterNumberSerializeResponse,
FakeOuterStringSerializeResponse,
HyphenParamResponse,
TestBodyWithQueryParamsResponse,
TestClientModelResponse,
TestEndpointParametersResponse,
Expand Down Expand Up @@ -63,6 +64,7 @@ fn main() {
"FakeOuterCompositeSerialize",
"FakeOuterNumberSerialize",
"FakeOuterStringSerialize",
"HyphenParam",
"TestEndpointParameters",
"TestEnumParameters",
"TestJsonFormData",
Expand Down Expand Up @@ -147,6 +149,11 @@ fn main() {
println!("{:?} (X-Span-ID: {:?})", result, (client.context() as &dyn Has<XSpanIdString>).get().clone());
},

Some("HyphenParam") => {
let result = core.run(client.hyphen_param("hyphen_param_example".to_string()));
println!("{:?} (X-Span-ID: {:?})", result, (client.context() as &dyn Has<XSpanIdString>).get().clone());
},

// Disabled because there's no example.
// Some("TestBodyWithQueryParams") => {
// let result = core.run(client.test_body_with_query_params("query_example".to_string(), ???));
Expand Down
Expand Up @@ -16,6 +16,7 @@ use petstore_with_fake_endpoints_models_for_testing::{Api, ApiError,
FakeOuterCompositeSerializeResponse,
FakeOuterNumberSerializeResponse,
FakeOuterStringSerializeResponse,
HyphenParamResponse,
TestBodyWithQueryParamsResponse,
TestClientModelResponse,
TestEndpointParametersResponse,
Expand Down Expand Up @@ -95,6 +96,13 @@ impl<C> Api<C> for Server<C> where C: Has<XSpanIdString>{
}


fn hyphen_param(&self, hyphen_param: String, context: &C) -> Box<dyn Future<Item=HyphenParamResponse, Error=ApiError>> {
let context = context.clone();
println!("hyphen_param(\"{}\") - X-Span-ID: {:?}", hyphen_param, context.get().0.clone());
Box::new(futures::failed("Generic failure".into()))
}


fn test_body_with_query_params(&self, query: String, body: models::User, context: &C) -> Box<dyn Future<Item=TestBodyWithQueryParamsResponse, Error=ApiError>> {
let context = context.clone();
println!("test_body_with_query_params(\"{}\", {:?}) - X-Span-ID: {:?}", query, body, context.get().0.clone());
Expand Down
Expand Up @@ -49,6 +49,7 @@ use {Api,
FakeOuterCompositeSerializeResponse,
FakeOuterNumberSerializeResponse,
FakeOuterStringSerializeResponse,
HyphenParamResponse,
TestBodyWithQueryParamsResponse,
TestClientModelResponse,
TestEndpointParametersResponse,
Expand Down Expand Up @@ -678,6 +679,67 @@ impl<F, C> Api<C> for Client<F> where

}

fn hyphen_param(&self, param_hyphen_param: String, context: &C) -> Box<dyn Future<Item=HyphenParamResponse, Error=ApiError>> {
let mut uri = format!(
"{}/v2/fake/hyphenParam/{hyphen_param}",
self.base_path, hyphen_param=utf8_percent_encode(&param_hyphen_param.to_string(), ID_ENCODE_SET)
);

let mut query_string = self::url::form_urlencoded::Serializer::new("".to_owned());


let query_string_str = query_string.finish();
if !query_string_str.is_empty() {
uri += "?";
uri += &query_string_str;
}

let uri = match Uri::from_str(&uri) {
Ok(uri) => uri,
Err(err) => return Box::new(futures::done(Err(ApiError(format!("Unable to build URI: {}", err))))),
};

let mut request = hyper::Request::new(hyper::Method::Get, uri);


request.headers_mut().set(XSpanId((context as &dyn Has<XSpanIdString>).get().0.clone()));
Box::new(self.client_service.call(request)
.map_err(|e| ApiError(format!("No response received: {}", e)))
.and_then(|mut response| {
match response.status().as_u16() {
200 => {
let body = response.body();
Box::new(

future::ok(
HyphenParamResponse::Success
)
) as Box<dyn Future<Item=_, Error=_>>
},
code => {
let headers = response.headers().clone();
Box::new(response.body()
.take(100)
.concat2()
.then(move |body|
future::err(ApiError(format!("Unexpected response code {}:\n{:?}\n\n{}",
code,
headers,
match body {
Ok(ref body) => match str::from_utf8(body) {
Ok(body) => Cow::from(body),
Err(e) => Cow::from(format!("<Body was not UTF8: {:?}>", e)),
},
Err(e) => Cow::from(format!("<Failed to read body: {}>", e)),
})))
)
) as Box<dyn Future<Item=_, Error=_>>
}
}
}))

}

fn test_body_with_query_params(&self, param_query: String, param_body: models::User, context: &C) -> Box<dyn Future<Item=TestBodyWithQueryParamsResponse, Error=ApiError>> {
let mut uri = format!(
"{}/v2/fake/body-with-query-params",
Expand Down Expand Up @@ -1310,8 +1372,8 @@ impl<F, C> Api<C> for Client<F> where

fn delete_pet(&self, param_pet_id: i64, param_api_key: Option<String>, context: &C) -> Box<dyn Future<Item=DeletePetResponse, Error=ApiError>> {
let mut uri = format!(
"{}/v2/pet/{petId}",
self.base_path, petId=utf8_percent_encode(&param_pet_id.to_string(), ID_ENCODE_SET)
"{}/v2/pet/{pet_id}",
self.base_path, pet_id=utf8_percent_encode(&param_pet_id.to_string(), ID_ENCODE_SET)
);

let mut query_string = self::url::form_urlencoded::Serializer::new("".to_owned());
Expand Down Expand Up @@ -1577,8 +1639,8 @@ impl<F, C> Api<C> for Client<F> where

fn get_pet_by_id(&self, param_pet_id: i64, context: &C) -> Box<dyn Future<Item=GetPetByIdResponse, Error=ApiError>> {
let mut uri = format!(
"{}/v2/pet/{petId}",
self.base_path, petId=utf8_percent_encode(&param_pet_id.to_string(), ID_ENCODE_SET)
"{}/v2/pet/{pet_id}",
self.base_path, pet_id=utf8_percent_encode(&param_pet_id.to_string(), ID_ENCODE_SET)
);

let mut query_string = self::url::form_urlencoded::Serializer::new("".to_owned());
Expand Down Expand Up @@ -1776,8 +1838,8 @@ impl<F, C> Api<C> for Client<F> where

fn update_pet_with_form(&self, param_pet_id: i64, param_name: Option<String>, param_status: Option<String>, context: &C) -> Box<dyn Future<Item=UpdatePetWithFormResponse, Error=ApiError>> {
let mut uri = format!(
"{}/v2/pet/{petId}",
self.base_path, petId=utf8_percent_encode(&param_pet_id.to_string(), ID_ENCODE_SET)
"{}/v2/pet/{pet_id}",
self.base_path, pet_id=utf8_percent_encode(&param_pet_id.to_string(), ID_ENCODE_SET)
);

let mut query_string = self::url::form_urlencoded::Serializer::new("".to_owned());
Expand Down Expand Up @@ -1857,8 +1919,8 @@ impl<F, C> Api<C> for Client<F> where

fn upload_file(&self, param_pet_id: i64, param_additional_metadata: Option<String>, param_file: Option<swagger::ByteArray>, context: &C) -> Box<dyn Future<Item=UploadFileResponse, Error=ApiError>> {
let mut uri = format!(
"{}/v2/pet/{petId}/uploadImage",
self.base_path, petId=utf8_percent_encode(&param_pet_id.to_string(), ID_ENCODE_SET)
"{}/v2/pet/{pet_id}/uploadImage",
self.base_path, pet_id=utf8_percent_encode(&param_pet_id.to_string(), ID_ENCODE_SET)
);

let mut query_string = self::url::form_urlencoded::Serializer::new("".to_owned());
Expand Down
Expand Up @@ -86,6 +86,12 @@ pub enum FakeOuterStringSerializeResponse {
(String)
}

#[derive(Debug, PartialEq)]
pub enum HyphenParamResponse {
/// Success
Success
}

#[derive(Debug, PartialEq)]
pub enum TestBodyWithQueryParamsResponse {
/// Success
Expand Down Expand Up @@ -334,6 +340,9 @@ pub trait Api<C> {
fn fake_outer_string_serialize(&self, body: Option<models::OuterString>, context: &C) -> Box<dyn Future<Item=FakeOuterStringSerializeResponse, Error=ApiError>>;


fn hyphen_param(&self, hyphen_param: String, context: &C) -> Box<dyn Future<Item=HyphenParamResponse, Error=ApiError>>;


fn test_body_with_query_params(&self, query: String, body: models::User, context: &C) -> Box<dyn Future<Item=TestBodyWithQueryParamsResponse, Error=ApiError>>;

/// To test \"client\" model
Expand Down Expand Up @@ -435,6 +444,9 @@ pub trait ApiNoContext {
fn fake_outer_string_serialize(&self, body: Option<models::OuterString>) -> Box<dyn Future<Item=FakeOuterStringSerializeResponse, Error=ApiError>>;


fn hyphen_param(&self, hyphen_param: String) -> Box<dyn Future<Item=HyphenParamResponse, Error=ApiError>>;


fn test_body_with_query_params(&self, query: String, body: models::User) -> Box<dyn Future<Item=TestBodyWithQueryParamsResponse, Error=ApiError>>;

/// To test \"client\" model
Expand Down Expand Up @@ -557,6 +569,11 @@ impl<'a, T: Api<C>, C> ApiNoContext for ContextWrapper<'a, T, C> {
}


fn hyphen_param(&self, hyphen_param: String) -> Box<dyn Future<Item=HyphenParamResponse, Error=ApiError>> {
self.api().hyphen_param(hyphen_param, &self.context())
}


fn test_body_with_query_params(&self, query: String, body: models::User) -> Box<dyn Future<Item=TestBodyWithQueryParamsResponse, Error=ApiError>> {
self.api().test_body_with_query_params(query, body, &self.context())
}
Expand Down

0 comments on commit 67d23fc

Please sign in to comment.