[Rust] Client library choice between hyper and reqwest#1258
[Rust] Client library choice between hyper and reqwest#1258bjgill merged 19 commits intoOpenAPITools:masterfrom
Conversation
|
@bcourtine thanks for the PR. I'd a quick look and overall it looks good. Can you please add Line 1027 in aac88a5 |
bjgill
left a comment
There was a problem hiding this comment.
Thanks for the PR. Before I give this a more in-depth look, I've got a couple of high-level questions/comments.
| # 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.
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.
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.
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.
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);
| [dev-dependencies] | ||
| {{#hyper}} | ||
| tokio-core = "*" | ||
| {{/hyper}} No newline at end of file |
There was a problem hiding this comment.
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.
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.
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.
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 ;) |
bjgill
left a comment
There was a problem hiding this comment.
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.
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.
I don't have a particular reason. I will merge both scripts.
| .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.
Minor nit - inconsistent full stops?
| supportingFiles.add(new SupportingFile("request.rs", apiFolder, "request.rs")); | ||
| 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.
Minor nit: the convention seems to be x.rs => x.mustache
| } | ||
| } | ||
|
|
||
| // TODO Manage Rust var codestyle (snake case) and compile problems (headers with special chars, like '-'). |
There was a problem hiding this comment.
Could you use StringUtils.underscore to get the header name into a form that will always compile?
There was a problem hiding this comment.
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.
Should we be doing something with additional_metadata and file?
There was a problem hiding this comment.
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.
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.
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.
What is the purpose of all these getters and setters? Why not have the fields public?
There was a problem hiding this comment.
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.
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.
Can you not implement default instead?
There was a problem hiding this comment.
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.
Good point - https://github.com/nrc/derive-new might be what you want, then.
| 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.
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.
|
| 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.
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.shand./bin/security/{LANG}-petstore.shif 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).