Skip to content

[Rust] reqwest shallow object query param #21199

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

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
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
9 changes: 9 additions & 0 deletions bin/configs/rust-reqwest-object-query-param.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
generatorName: rust
outputDir: samples/client/others/rust/reqwest/object-query-param
library: reqwest
inputSpec: modules/openapi-generator/src/test/resources/3_0/objectQueryParam.yaml
templateDir: modules/openapi-generator/src/main/resources/rust
validateSpec: "false"
additionalProperties:
supportAsync: "true"
packageName: object-query-param-reqwest
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,13 @@ pub {{#supportAsync}}async {{/supportAsync}}fn {{{operationId}}}(configuration:
{{/isArray}}
{{^isArray}}
{{^isNullable}}
{{#isModel}}
let params = crate::apis::parse_flat_object(&serde_json::to_value({{{vendorExtensions.x-rust-param-identifier}}})?);
req_builder = req_builder.query(&params);
{{/isModel}}
{{^isModel}}
req_builder = req_builder.query(&[("{{{baseName}}}", &{{{vendorExtensions.x-rust-param-identifier}}}.to_string())]);
{{/isModel}}
{{/isNullable}}
{{#isNullable}}
{{#isDeepObject}}
Expand All @@ -165,9 +171,17 @@ pub {{#supportAsync}}async {{/supportAsync}}fn {{{operationId}}}(configuration:
};
{{/isDeepObject}}
{{^isDeepObject}}
{{#isModel}}
if let Some(ref param_value) = {{{vendorExtensions.x-rust-param-identifier}}} {
let params = crate::apis::parse_flat_object(&serde_json::to_value({{{vendorExtensions.x-rust-param-identifier}}})?);
req_builder = req_builder.query(&params);
};
{{/isModel}}
{{^isModel}}
if let Some(ref param_value) = {{{vendorExtensions.x-rust-param-identifier}}} {
req_builder = req_builder.query(&[("{{{baseName}}}", &param_value.to_string())]);
};
{{/isModel}}
{{/isDeepObject}}
{{/isNullable}}
{{/isArray}}
Expand All @@ -186,7 +200,13 @@ pub {{#supportAsync}}async {{/supportAsync}}fn {{{operationId}}}(configuration:
req_builder = req_builder.query(&params);
{{/isDeepObject}}
{{^isDeepObject}}
{{#isModel}}
let params = crate::apis::parse_flat_object(&serde_json::to_value(param_value)?);
req_builder = req_builder.query(&params);
{{/isModel}}
{{^isModel}}
req_builder = req_builder.query(&[("{{{baseName}}}", &param_value.to_string())]);
{{/isModel}}
{{/isDeepObject}}
{{/isArray}}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,33 @@ pub fn urlencode<T: AsRef<str>>(s: T) -> String {
::url::form_urlencoded::byte_serialize(s.as_ref().as_bytes()).collect()
}

pub fn parse_flat_object(value: &serde_json::Value) -> Vec<(String, String)> {
if let serde_json::Value::Object(object) = value {
let mut params = vec![];

for (key, value) in object {
match value {
serde_json::Value::Object(_) => {
unimplemented!(
"Only flat objects are supported, use parse_deep_object() instead"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMHO, having unimplemented here may cause unexpected panic from user point of view. How about auto fallback to parse_deep_object:

serde_json::Value::Object(_) => {
   return parse_deep_object("", value);
}

or just don't handle it, let it fallback to _ =>

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The user should never see that. That's a developer targeted error.
As far as I can tell, the only way a user sees this, is if a developer makes a mistake modifying the templates in a way that uses the wrong parse fn.

And I'd say that if I let it pass through, then it will generate incorrect values (I don't think converting objects to strings will be very useful) and will make figuring out that someone broke the templates more difficult.

Or I'm missing something here?

)
}
serde_json::Value::Array(array) => {
for (i, value) in array.iter().enumerate() {
params.push((format!("{key}[{i}]"), value.to_string()));
}
}
serde_json::Value::String(s) => params.push((key.to_string(), s.clone())),
_ => params.push((key.to_string(), value.to_string())),
}
}

return params;
}

unimplemented!("Only objects are supported")
}

pub fn parse_deep_object(prefix: &str, value: &serde_json::Value) -> Vec<(String, String)> {
if let serde_json::Value::Object(object) = value {
let mut params = vec![];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ paths:
parameters:
- in: query
name: pageQuery
required: true
schema:
type: object
properties:
Expand All @@ -22,4 +23,13 @@ paths:
format: int32
limit:
type: integer
format: int32
format: int32
- in: query
name: notRequired
required: false
schema:
type: object
properties:
foobar:
type: integer
format: int32
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,33 @@ pub fn urlencode<T: AsRef<str>>(s: T) -> String {
::url::form_urlencoded::byte_serialize(s.as_ref().as_bytes()).collect()
}

pub fn parse_flat_object(value: &serde_json::Value) -> Vec<(String, String)> {
if let serde_json::Value::Object(object) = value {
let mut params = vec![];

for (key, value) in object {
match value {
serde_json::Value::Object(_) => {
unimplemented!(
"Only flat objects are supported, use parse_deep_object() instead"
)
}
serde_json::Value::Array(array) => {
for (i, value) in array.iter().enumerate() {
params.push((format!("{key}[{i}]"), value.to_string()));
}
}
serde_json::Value::String(s) => params.push((key.to_string(), s.clone())),
_ => params.push((key.to_string(), value.to_string())),
}
}

return params;
}

unimplemented!("Only objects are supported")
}

pub fn parse_deep_object(prefix: &str, value: &serde_json::Value) -> Vec<(String, String)> {
if let serde_json::Value::Object(object) = value {
let mut params = vec![];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,33 @@ pub fn urlencode<T: AsRef<str>>(s: T) -> String {
::url::form_urlencoded::byte_serialize(s.as_ref().as_bytes()).collect()
}

pub fn parse_flat_object(value: &serde_json::Value) -> Vec<(String, String)> {
if let serde_json::Value::Object(object) = value {
let mut params = vec![];

for (key, value) in object {
match value {
serde_json::Value::Object(_) => {
unimplemented!(
"Only flat objects are supported, use parse_deep_object() instead"
)
}
serde_json::Value::Array(array) => {
for (i, value) in array.iter().enumerate() {
params.push((format!("{key}[{i}]"), value.to_string()));
}
}
serde_json::Value::String(s) => params.push((key.to_string(), s.clone())),
_ => params.push((key.to_string(), value.to_string())),
}
}

return params;
}

unimplemented!("Only objects are supported")
}

pub fn parse_deep_object(prefix: &str, value: &serde_json::Value) -> Vec<(String, String)> {
if let serde_json::Value::Object(object) = value {
let mut params = vec![];
Expand Down
27 changes: 27 additions & 0 deletions samples/client/others/rust/reqwest/composed-oneof/src/apis/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,33 @@ pub fn urlencode<T: AsRef<str>>(s: T) -> String {
::url::form_urlencoded::byte_serialize(s.as_ref().as_bytes()).collect()
}

pub fn parse_flat_object(value: &serde_json::Value) -> Vec<(String, String)> {
if let serde_json::Value::Object(object) = value {
let mut params = vec![];

for (key, value) in object {
match value {
serde_json::Value::Object(_) => {
unimplemented!(
"Only flat objects are supported, use parse_deep_object() instead"
)
}
serde_json::Value::Array(array) => {
for (i, value) in array.iter().enumerate() {
params.push((format!("{key}[{i}]"), value.to_string()));
}
}
serde_json::Value::String(s) => params.push((key.to_string(), s.clone())),
_ => params.push((key.to_string(), value.to_string())),
}
}

return params;
}

unimplemented!("Only objects are supported")
}

pub fn parse_deep_object(prefix: &str, value: &serde_json::Value) -> Vec<(String, String)> {
if let serde_json::Value::Object(object) = value {
let mut params = vec![];
Expand Down
27 changes: 27 additions & 0 deletions samples/client/others/rust/reqwest/emptyObject/src/apis/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,33 @@ pub fn urlencode<T: AsRef<str>>(s: T) -> String {
::url::form_urlencoded::byte_serialize(s.as_ref().as_bytes()).collect()
}

pub fn parse_flat_object(value: &serde_json::Value) -> Vec<(String, String)> {
if let serde_json::Value::Object(object) = value {
let mut params = vec![];

for (key, value) in object {
match value {
serde_json::Value::Object(_) => {
unimplemented!(
"Only flat objects are supported, use parse_deep_object() instead"
)
}
serde_json::Value::Array(array) => {
for (i, value) in array.iter().enumerate() {
params.push((format!("{key}[{i}]"), value.to_string()));
}
}
serde_json::Value::String(s) => params.push((key.to_string(), s.clone())),
_ => params.push((key.to_string(), value.to_string())),
}
}

return params;
}

unimplemented!("Only objects are supported")
}

pub fn parse_deep_object(prefix: &str, value: &serde_json::Value) -> Vec<(String, String)> {
if let serde_json::Value::Object(object) = value {
let mut params = vec![];
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/target/
**/*.rs.bk
Cargo.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# OpenAPI Generator Ignore
# Generated by openapi-generator https://github.com/openapitools/openapi-generator

# Use this file to prevent files from being overwritten by the generator.
# The patterns follow closely to .gitignore or .dockerignore.

# As an example, the C# client generator defines ApiClient.cs.
# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:
#ApiClient.cs

# You can match any string of characters against a directory, file or extension with a single asterisk (*):
#foo/*/qux
# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux

# You can recursively match patterns against a directory, file or extension with a double asterisk (**):
#foo/**/qux
# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux

# You can also negate patterns with an exclamation (!).
# For example, you can ignore all files in a docs folder with the file extension .md:
#docs/*.md
# Then explicitly reverse the ignore rule for a single file:
#!docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
.gitignore
.travis.yml
Cargo.toml
README.md
docs/DefaultApi.md
docs/ListNotRequiredParameter.md
docs/ListPageQueryParameter.md
git_push.sh
src/apis/configuration.rs
src/apis/default_api.rs
src/apis/mod.rs
src/lib.rs
src/models/list_not_required_parameter.rs
src/models/list_page_query_parameter.rs
src/models/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
7.14.0-SNAPSHOT
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
language: rust
14 changes: 14 additions & 0 deletions samples/client/others/rust/reqwest/object-query-param/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[package]
name = "object-query-param-reqwest"
version = "1.0.0"
authors = ["OpenAPI Generator team and contributors"]
description = "No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)"
license = "Apache-2.0"
edition = "2021"

[dependencies]
serde = { version = "^1.0", features = ["derive"] }
serde_json = "^1.0"
serde_repr = "^0.1"
url = "^2.5"
reqwest = { version = "^0.12", default-features = false, features = ["json", "multipart"] }
47 changes: 47 additions & 0 deletions samples/client/others/rust/reqwest/object-query-param/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Rust API client for object-query-param-reqwest

No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)


## Overview

This API client was generated by the [OpenAPI Generator](https://openapi-generator.tech) project. By using the [openapi-spec](https://openapis.org) from a remote server, you can easily generate an API client.

- API version: 1.0.0
- Package version: 1.0.0
- Generator version: 7.14.0-SNAPSHOT
- Build package: `org.openapitools.codegen.languages.RustClientCodegen`

## Installation

Put the package under your project folder in a directory named `object-query-param-reqwest` and add the following to `Cargo.toml` under `[dependencies]`:

```
object-query-param-reqwest = { path = "./object-query-param-reqwest" }
```

## Documentation for API Endpoints

All URIs are relative to *http://localhost:8080*

Class | Method | HTTP request | Description
------------ | ------------- | ------------- | -------------
*DefaultApi* | [**list**](docs/DefaultApi.md#list) | **GET** /pony |


## Documentation For Models

- [ListNotRequiredParameter](docs/ListNotRequiredParameter.md)
- [ListPageQueryParameter](docs/ListPageQueryParameter.md)


To get access to the crate's generated documentation, use:

```
cargo doc --open
```

## Author



Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# \DefaultApi

All URIs are relative to *http://localhost:8080*

Method | HTTP request | Description
------------- | ------------- | -------------
[**list**](DefaultApi.md#list) | **GET** /pony |



## list

> list(page_query, not_required)


### Parameters


Name | Type | Description | Required | Notes
------------- | ------------- | ------------- | ------------- | -------------
**page_query** | [**ListPageQueryParameter**](.md) | | [required] |
**not_required** | Option<[**ListNotRequiredParameter**](.md)> | | |

### 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)

Loading
Loading