-
-
Notifications
You must be signed in to change notification settings - Fork 6.7k
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] Client library choice between hyper and reqwest #1258
Conversation
@bcourtine thanks for the PR. I'd a quick look and overall it looks good. Can you please add Line 1027 in aac88a5
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the PR. Before I give this a more in-depth look, I've got a couple of high-level questions/comments.
@@ -27,6 +27,6 @@ fi | |||
|
|||
# if you've executed sbt assembly previously it will use that instead. | |||
export JAVA_OPTS="${JAVA_OPTS} -XX:MaxPermSize=256M -Xmx1024M -DloggerPath=conf/log4j.properties" | |||
ags="generate -t modules/openapi-generator/src/main/resources/rust -i modules/openapi-generator/src/test/resources/2_0/petstore.yaml -g rust -o samples/client/petstore/rust -DpackageName=petstore_client $@" | |||
ags="generate -t modules/openapi-generator/src/main/resources/rust -i modules/openapi-generator/src/test/resources/2_0/petstore.yaml -g rust -o samples/client/petstore/rust -DpackageName=petstore_client --library=hyper $@" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this is back-compatible. As such, I would suggest creating this against the branch for the next major release - 4.0.x, I believe. See https://github.com/OpenAPITools/openapi-generator/wiki/Git-Branches
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This "library" modification is optional. If no library is chosen for generation, Hyper remains the default library. I may be wrong, but I think it is backward compatible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If hyper
is the default, the change should be backward-compatible.
If reqwest
is the default, the change should target 3.4.x branch as it's a breaking change with fallbacks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From RustClientCodegen.java
:
CliOption libraryOption = new CliOption(CodegenConstants.LIBRARY, "library template (sub-template) to use");
libraryOption.setEnum(supportedLibraries);
// set hyper as the default
libraryOption.setDefault(HYPER_LIBRARY);
cliOptions.add(libraryOption);
setLibrary(HYPER_LIBRARY);
tokio-core = "*" | ||
{{/hyper}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My highest-level question - why make the decision at codegen time rather than at compile time? You could presumably achieve the same effect with Rust features. I'm not saying you're wrong - merely curious as to why you chose as you did.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't have a good reason for that. I am a beginner in Rust development and learning.
I don't know what purpose this dev lib deserves. Since I could generate my project without it in the Cargo file, I dropped it, simply to keep the file as tiny as possible.
If you think it is a mistake, I can put it back.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I probably misunderstood your question (I thought it was about tokio-core
lib only).
For the whole library choice, the main reason is that since I'm learning Rust, I don't know how to use it yet.
Another reason whould be that hyper
and reqwest
templates are quite different: API main methods currently don't have the same return type (Result
vs Box<Future<Result>>>
. So I presume that generating a client managing both libs with features would have much more complex code (but since I don't know features, I may be wrong).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That seems reasonable. I could well imagine that trying to have both in the same crate could cause problems if they ever had incompatible dependencies.
Shippable failed tests seems unrelated to current PR. It fails in "ElixirPetstoreClientTests" which is not impacted by this branch commits. |
Thanks for your prompt answers. It looks if you're still busy making code changes. Please let me know when you've finished and I'll take a closer look. |
You can review it. It works pretty well (I used the reqwest generator with success on several projects). There are still some bugs, but I can't find an easy way to correct them, and won't have enough time to work on it soon. For now, I correct theses remaining problems post generation when they occur. FYI, here are the problems I spotted that are not corrected yet:
let query_string = {
let mut query = ::url::form_urlencoded::Serializer::new(String::new());
query.append_pair("vec_i32_param", &vec_i32_param.join(",").to_string());
query.finish()
}; It must be corrected by something like: let query_string = {
let mut query = ::url::form_urlencoded::Serializer::new(String::new());
let vec_str_param: Vec<String> = vec_i32_param.iter().map(|p| p.to_string()).collect();
query.append_pair("vec_i32_param", &vec_str_param.join(",").to_string());
query.finish()
};
With |
Another thought about headers. In I had to reintroduce the dependency because of headers. Implementing |
What about using |
Are theses booleans valuated for a list of integers/longs? I prefer waiting for @bjgill opinion:
|
Feel free to wait for @bjgill option. I was just throwing ideas... If it's a list, you will need to do the following instead:
Please use |
I don't think any of the Rust generators handle query parameters very cleanly. For lists, you proposal seems sensible. I wonder whether we want to special-case integers, though. Will we not want to do the same thing for all non-string types? Indeed, I believe that |
Sounds a good idea. I do that and let you review the whole PR ;) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks broadly sensible, thanks. I've reviewed the sample as being easier to do than reviewing the mustache templates themselves.
I may have commented on some of the preexisting code that you haven't changed (e.g. the models, maybe?). Feel free to dismiss any such comments - I'm certainly not expecting you to fix up oddities in previous contributions.
I would recommend looking at the warnings produced by cargo check and cargo clippy - I suspect they won't all be worth fixing, but some may be of interest.
export JAVA_OPTS="${JAVA_OPTS} -XX:MaxPermSize=256M -Xmx1024M -DloggerPath=conf/log4j.properties" | ||
ags="generate -t modules/openapi-generator/src/main/resources/rust -i modules/openapi-generator/src/test/resources/2_0/petstore.yaml -g rust -o samples/client/petstore/rust-reqwest -DpackageName=petstore_client --library=reqwest $@" | ||
|
||
java ${JAVA_OPTS} -jar ${executable} ${ags} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why have the rust-reqwest sample generation in a separate script? Could you not extend the rust sample generation to do both, reducing the risk that someone will do one and forget to do the other?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't have a particular reason. I will merge both scripts.
@@ -141,6 +133,15 @@ public RustClientCodegen() { | |||
cliOptions.add(new CliOption(CodegenConstants.HIDE_GENERATION_TIMESTAMP, CodegenConstants.HIDE_GENERATION_TIMESTAMP_DESC) | |||
.defaultValue(Boolean.TRUE.toString())); | |||
|
|||
supportedLibraries.put(HYPER_LIBRARY, "HTTP client: Hyper."); | |||
supportedLibraries.put(REQWEST_LIBRARY, "HTTP client: Reqwest"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor nit - inconsistent full stops?
supportingFiles.add(new SupportingFile("model_mod.mustache", modelFolder, "mod.rs")); | ||
supportingFiles.add(new SupportingFile("lib.rs", "src", "lib.rs")); | ||
supportingFiles.add(new SupportingFile("lib.rs.mustache", "src", "lib.rs")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor nit: the convention seems to be x.rs
=> x.mustache
...es/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustClientCodegen.java
Show resolved
Hide resolved
} | ||
} | ||
|
||
// TODO Manage Rust var codestyle (snake case) and compile problems (headers with special chars, like '-'). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you use StringUtils.underscore
to get the header name into a form that will always compile?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is possible, but complicated. It requires to introduce an object to maintain both native and underscored versions. Native version is required for the reqwest::header::Header
implementation (header name).
Ok(()) | ||
} | ||
|
||
fn upload_file(&self, pet_id: i64, additional_metadata: &str, file: ::models::File) -> Result<::models::ApiResponse, Error> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we be doing something with additional_metadata
and file
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
File
management is a remaining TODO forked from hyper
code.
/* | ||
* OpenAPI Petstore | ||
* | ||
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this correct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will check. I think I did not modify models. They come from the historical hyper
code.
* Generated by: https://openapi-generator.tech | ||
*/ | ||
|
||
/// ApiResponse : Describes the result of uploading an image resource |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is this doc comment attached to?
pub fn with_code(mut self, code: i32) -> ApiResponse { | ||
self.code = Some(code); | ||
self | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the purpose of all these getters and setters? Why not have the fields public?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Copied from original code. Don't know the reason...
But I imagine the purpose is to get a "fluent API", with method calls chained: model.with_code("code").with_name("name")...
. It is a common pattern in other languages like java
to keep fields private and to provide this access methods.
EDIT: I note this for later, but I won't correct it in this branch: it would be a breaking change in generated API since models are shared between hyper and reqwest versions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I note for later, but I won't correct it in this branch: it would be a breaking change in generated API since models are shared between hyper and reqwest versions.
code: None, | ||
_type: None, | ||
message: None | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you not implement default instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After a closer look, it is not as simple. When there is a required var in a model, default constructor new
has required vars as parameters. So, Default
trait cannot be implemented in this case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point - https://github.com/nrc/derive-new might be what you want, then.
@@ -56,7 +56,7 @@ impl {{classname}} for {{classname}}Client { | |||
let query_string = { | |||
let mut query = ::url::form_urlencoded::Serializer::new(String::new()); | |||
{{#queryParams}} | |||
query.append_pair("{{baseName}}", &{{paramName}}{{#isListContainer}}.join(","){{/isListContainer}}.to_string()); | |||
query.append_pair("{{baseName}}", &{{paramName}}{{#isListContainer}}.iter().map(|p| p.to_string()).collect::<Vec<String>>().join(","){{/isListContainer}}.to_string()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice.
For bonus points, you could use into_iter
instead of iter
. As I understand it, iter
produces references to p
, and to_string
will therefore clone p
. into_iter
would give you an owned p
, and thus to_string
would not clone..
Thanks for the complete review. I will correct this. But since I am quite busy, corrections may not be available before some days. |
I just looked at reqwest 0.9 release notes and 😭 :
Most custom header code added in configuration template can be dropped with this feature. |
…st transition feature).
@bjgill Most of the points you spotted have been corrected. You can review this new version. I did not correct model problems, because it would be a breaking change in generated API (since models are used by both |
Looks good, thanks. I think all my outstanding comments are things that are present in the hyper generator already and therefore should not block this PR. |
bin/rust-petstore.sh
Outdated
java ${JAVA_OPTS} -jar ${executable} ${ags} | ||
|
||
# Generate client using reqwest lib. | ||
ags="generate -t modules/openapi-generator/src/main/resources/rust -i modules/openapi-generator/src/test/resources/2_0/petstore.yaml -g rust -o samples/client/petstore/rust-reqwest -DpackageName=petstore_client --library=reqwest $@" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Usually, we create another script (e.g. bin/rust-petstore-reqwest.sh
) for a different HTTP library. (You will find a lot of ./bin/java-petstore-{library}.sh
scripts for different HTTP libraries supported by the Java client generator)
I suggest we do the same for Rust reqwest instead of putting both into one singel script.
I'm happy for this to be merged (modulo CI) - sorry for suggesting something that you then had to undo. |
@bjgill I was just trolling. Merging samples scripts (and undoing it) was trivial. |
@wing328 - anything else? Otherwise, I'll merge this tomorrow. |
@bjgill nothing from me. Please go ahead with the merge. |
Thank you @bcourtine again for the contribution and @bjgill for the review. I've sent out a tweet (https://twitter.com/oas_generator/status/1055863197687664640) to help promote the enhancement. Have a nice weekend. |
@bcourtine thanks for the PR, which is included in the v3.3.2 release: https://twitter.com/oas_generator/status/1057649626101112832 |
…1258) * Port of PR swagger-api/swagger-codegen#8804. * Correction of conflict with PR OpenAPITools#528 (missing template file). * Add rust-reqwest samples to Circle CI tests. * Add integration test pom.xml file with launcher to trigger cargo execution. * Deduplicate Maven project name. * Fix "api_key" header for Petstore. * Better API key management. * Fix query param for lists of objects other than strings (numbers, etc.). * Update to reqwest 0.9, and refactor of header management (using reqwest transition feature). * Merge scripts generating rust-hyper and rust-reqwest samples. * Consistent full stops. * Use raw variables in all Rust mustache templates. * Replace production build in CI with a quick simple check. * Update samples. * Finish Reqwest 0.9 migration (removing "hyper 0.11" transition feature). * Configuration implements Default trait. * API template reorganized: HashMap is not required anymore. * Revert "Merge scripts generating rust-hyper and rust-reqwest samples." This reverts commit 970f996. * Remove deprecated "-XX:MaxPermSize" java arg.
PR checklist
./bin/
to update Petstore sample so that CIs can verify the change. (For instance, only need to run./bin/{LANG}-petstore.sh
and./bin/security/{LANG}-petstore.sh
if updating the {LANG} (e.g. php, ruby, python, etc) code generator or {LANG} client's mustache templates). Windows batch files can be found in.\bin\windows\
.master
,3.4.x
,4.0.x
. Default:master
.CC @frol @farcaller @bjgill for review.
Description of the PR
Port of PR swagger-api/swagger-codegen#8804
Propose an alternative library (reqwest) to generate a Rust client. This library is much easier to use than hyper (especially to deal with https services).