Skip to content
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 @@ -391,6 +391,11 @@ fn body_from_string(s: String) -> BoxBody<Bytes, Infallible> {
BoxBody::new(Full::new(Bytes::from(s)))
}

#[allow(dead_code)]
fn body_from_bytes(b: Vec<u8>) -> BoxBody<Bytes, Infallible> {
BoxBody::new(Full::new(Bytes::from(b)))
}

#[async_trait]
impl<S, C, B> Api<C> for Client<S, C> where
S: Service<
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,23 +83,27 @@
{{^isByteArray}}
{{^isBinary}}
let body = param_{{{paramName}}};
*request.body_mut() = body_from_string(body);
{{/isBinary}}
{{/isByteArray}}
{{#isByteArray}}
let body = String::from_utf8(param_{{{paramName}}}.0).expect("Body was not valid UTF8");
// Raw binary body - pass the bytes through without coercing to UTF-8.
*request.body_mut() = body_from_bytes(param_{{{paramName}}}.0);
{{/isByteArray}}
{{#isBinary}}
let body = String::from_utf8(param_{{{paramName}}}.0).expect("Body was not valid UTF8");
// Raw binary body - pass the bytes through without coercing to UTF-8.
*request.body_mut() = body_from_bytes(param_{{{paramName}}}.0);
{{/isBinary}}
{{/x-consumes-plain-text}}
{{#x-consumes-xml}}
let body = param_{{{paramName}}}.as_xml();
*request.body_mut() = body_from_string(body);
{{/x-consumes-xml}}
{{#x-consumes-json}}
let body = serde_json::to_string(&param_{{{paramName}}}).expect("impossible to fail to serialize");
*request.body_mut() = body_from_string(body);
{{/x-consumes-json}}
{{/exts}}
*request.body_mut() = body_from_string(body);
{{^required}}
}
{{/required}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,4 +112,33 @@ public void testRequiredQueryParamWithoutExampleDisablesClientExample() throws I
// Clean up
target.toFile().deleteOnExit();
}

/**
* Test that binary/byte request bodies are passed through as raw bytes rather than being
* coerced to UTF-8, which panics on any non-UTF-8 payload (see issue #24094).
*/
@Test
public void testBinaryRequestBodyNotCoercedToUtf8() throws IOException {
Path target = Files.createTempDirectory("test");
final CodegenConfigurator configurator = new CodegenConfigurator()
.setGeneratorName("rust-server")
.setInputSpec("src/test/resources/3_0/rust-server/openapi-v3.yaml")
.setSkipOverwrite(false)
.setOutputDir(target.toAbsolutePath().toString().replace("\\", "/"));
List<File> files = new DefaultGenerator().opts(configurator.toClientOptInput()).generate();
files.forEach(File::deleteOnExit);

Path clientModPath = Path.of(target.toString(), "/src/client/mod.rs");
TestUtils.assertFileExists(clientModPath);

// format: binary request body should pass raw bytes through.
TestUtils.assertFileContains(clientModPath, "*request.body_mut() = body_from_bytes(param_body.0);");
// The bytes helper must be generated.
TestUtils.assertFileContains(clientModPath, "fn body_from_bytes(b: Vec<u8>) -> BoxBody<Bytes, Infallible> {");
// The request body must no longer be coerced to UTF-8 (would panic on non-UTF-8 payloads).
TestUtils.assertFileNotContains(clientModPath, "let body = String::from_utf8(param_body.0).expect(\"Body was not valid UTF8\");");

// Clean up
target.toFile().deleteOnExit();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,18 @@ paths:
responses:
'200':
description: 'OK'
/required_binary_stream:
put:
requestBody:
required: true
content:
application/octet-stream:
schema:
type: string
format: binary
responses:
'200':
description: 'OK'
/readonly_auth_scheme:
get:
security:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,272 @@
# Rust API for openapi-v3

API under test

## Overview

This client/server was generated by the [openapi-generator]
(https://openapi-generator.tech) project. By using the
[OpenAPI-Spec](https://github.com/OAI/OpenAPI-Specification) from a remote
server, you can easily generate a server stub.

To see how to make this your own, look here:

[README]((https://openapi-generator.tech))

- API version: 1.0.7
- Generator version: 7.24.0-SNAPSHOT



This autogenerated project defines an API crate `openapi-v3` which contains:
* An `Api` trait defining the API in Rust.
* Data types representing the underlying data model.
* A `Client` type which implements `Api` and issues HTTP requests for each operation.
* A router which accepts HTTP requests and invokes the appropriate `Api` method for each operation.
* A CLI tool to drive basic API operations from the command line.

It also contains an example server and client which make use of `openapi-v3`:

* The example server starts up a web server using the `openapi-v3`
router, and supplies a trivial implementation of `Api` which returns failure
for every operation.
* The example client provides a CLI which lets you invoke
any single operation on the `openapi-v3` client by passing appropriate
arguments on the command line.

You can use the example server and client as a basis for your own code.
See below for [more detail on the examples](#using-the-generated-library).

## CLI

Run the included CLI tool with:

```
cargo run --bin cli --features=cli
```

To pass in arguments, put them after `--`, for example:

```
cargo run --bin cli --features=cli -- --help
```

See the help text for available options.

To build a standalone tool, use:

```
cargo build --bin cli --features=cli --release
```

You'll find the binary at `target/release/cli`.

## Examples

Run examples with:

```
cargo run --example openapi-v3-<client|server>
```

To pass in arguments to the examples, put them after `--`, for example:

```
cargo run --example openapi-v3-client -- --help
```

### Running the example server
To run the server, follow these simple steps:

```
cargo run --example openapi-v3-server
```

### Running the example client
To run a client, follow one of the following simple steps:

```
cargo run --example openapi-v3-client AnyOfGet
cargo run --example openapi-v3-client CallbackWithHeaderPost
cargo run --example openapi-v3-client ComplexQueryParamGet
cargo run --example openapi-v3-client ExamplesTest
cargo run --example openapi-v3-client FormTest
cargo run --example openapi-v3-client GetWithBooleanParameter
cargo run --example openapi-v3-client JsonComplexQueryParamGet
cargo run --example openapi-v3-client MandatoryRequestHeaderGet
cargo run --example openapi-v3-client MergePatchJsonGet
cargo run --example openapi-v3-client MultigetGet
cargo run --example openapi-v3-client MultipleAuthSchemeGet
cargo run --example openapi-v3-client OneOfGet
cargo run --example openapi-v3-client OverrideServerGet
cargo run --example openapi-v3-client ParamgetGet
cargo run --example openapi-v3-client QueryExampleGet
cargo run --example openapi-v3-client ReadonlyAuthSchemeGet
cargo run --example openapi-v3-client RegisterCallbackPost
cargo run --example openapi-v3-client RequiredBinaryStreamPut
cargo run --example openapi-v3-client RequiredOctetStreamPut
cargo run --example openapi-v3-client ResponsesWithHeadersGet
cargo run --example openapi-v3-client Rfc7807Get
cargo run --example openapi-v3-client TwoFirstLetterHeaders
cargo run --example openapi-v3-client UntypedPropertyGet
cargo run --example openapi-v3-client UuidGet
cargo run --example openapi-v3-client XmlExtraPost
cargo run --example openapi-v3-client XmlOtherPost
cargo run --example openapi-v3-client XmlOtherPut
cargo run --example openapi-v3-client XmlPost
cargo run --example openapi-v3-client XmlPut
cargo run --example openapi-v3-client EnumInPathPathParamGet
cargo run --example openapi-v3-client MultiplePathParamsWithVeryLongPathToTestFormattingPathParamAPathParamBGet
cargo run --example openapi-v3-client CreateRepo
cargo run --example openapi-v3-client GetRepoInfo
```

### HTTPS
The examples can be run in HTTPS mode by passing in the flag `--https`, for example:

```
cargo run --example openapi-v3-server -- --https
```

This will use the keys/certificates from the examples directory. Note that the
server chain is signed with `CN=localhost`.

## Using the generated library

The generated library has a few optional features that can be activated through Cargo.

* `server`
* This defaults to enabled and creates the basic skeleton of a server implementation based on hyper
* To create the server stack you'll need to provide an implementation of the API trait to provide the server function.
* `client`
* This defaults to enabled and creates the basic skeleton of a client implementation based on hyper
* The constructed client implements the API trait by making remote API call.
* `conversions`
* This defaults to disabled and creates extra derives on models to allow "transmogrification" between objects of structurally similar types.
* `cli`
* This defaults to disabled and is required for building the included CLI tool.

See https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section for how to use features in your `Cargo.toml`.

## Documentation for API Endpoints

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

Method | HTTP request | Description
------------- | ------------- | -------------
[****](docs/default_api.md#) | **GET** /any-of |
[****](docs/default_api.md#) | **POST** /callback-with-header |
[****](docs/default_api.md#) | **GET** /complex-query-param |
[**ExamplesTest**](docs/default_api.md#ExamplesTest) | **GET** /examples-test | Test examples
[**FormTest**](docs/default_api.md#FormTest) | **POST** /form-test | Test a Form Post
[**GetWithBooleanParameter**](docs/default_api.md#GetWithBooleanParameter) | **GET** /get-with-bool |
[****](docs/default_api.md#) | **GET** /json-complex-query-param |
[****](docs/default_api.md#) | **GET** /mandatory-request-header |
[****](docs/default_api.md#) | **GET** /merge-patch-json |
[****](docs/default_api.md#) | **GET** /multiget | Get some stuff.
[****](docs/default_api.md#) | **GET** /multiple_auth_scheme |
[****](docs/default_api.md#) | **GET** /one-of |
[****](docs/default_api.md#) | **GET** /override-server |
[****](docs/default_api.md#) | **GET** /paramget | Get some stuff with parameters.
[**queryExampleGet**](docs/default_api.md#queryExampleGet) | **GET** /query-example | Test required query params with and without examples
[****](docs/default_api.md#) | **GET** /readonly_auth_scheme |
[****](docs/default_api.md#) | **POST** /register-callback |
[****](docs/default_api.md#) | **PUT** /required_binary_stream |
[****](docs/default_api.md#) | **PUT** /required_octet_stream |
[****](docs/default_api.md#) | **GET** /responses_with_headers |
[****](docs/default_api.md#) | **GET** /rfc7807 |
[**TwoFirstLetterHeaders**](docs/default_api.md#TwoFirstLetterHeaders) | **POST** /operation-two-first-letter-headers |
[****](docs/default_api.md#) | **GET** /untyped_property |
[****](docs/default_api.md#) | **GET** /uuid |
[****](docs/default_api.md#) | **POST** /xml_extra |
[****](docs/default_api.md#) | **POST** /xml_other |
[****](docs/default_api.md#) | **PUT** /xml_other |
[****](docs/default_api.md#) | **POST** /xml | Post an array. It's important we test apostrophes, so include one here.
[****](docs/default_api.md#) | **PUT** /xml |
[****](docs/default_api.md#) | **GET** /enum_in_path/{path_param} |
[****](docs/default_api.md#) | **GET** /multiple-path-params-with-very-long-path-to-test-formatting/{path_param_a}/{path_param_b} |
[**CreateRepo**](docs/repo_api.md#CreateRepo) | **POST** /repos |
[**GetRepoInfo**](docs/repo_api.md#GetRepoInfo) | **GET** /repos/{repoId} |


## Documentation For Models

- [AdditionalPropertiesReferencedAnyOfObject](docs/AdditionalPropertiesReferencedAnyOfObject.md)
- [AdditionalPropertiesWithList](docs/AdditionalPropertiesWithList.md)
- [AdditionalPropertiesWithNullable](docs/AdditionalPropertiesWithNullable.md)
- [AnotherXmlArray](docs/AnotherXmlArray.md)
- [AnotherXmlInner](docs/AnotherXmlInner.md)
- [AnotherXmlObject](docs/AnotherXmlObject.md)
- [AnyOfGet202Response](docs/AnyOfGet202Response.md)
- [AnyOfHashMapObject](docs/AnyOfHashMapObject.md)
- [AnyOfObject](docs/AnyOfObject.md)
- [AnyOfObjectAnyOf](docs/AnyOfObjectAnyOf.md)
- [AnyOfProperty](docs/AnyOfProperty.md)
- [DuplicateXmlObject](docs/DuplicateXmlObject.md)
- [EnumWithStarObject](docs/EnumWithStarObject.md)
- [Err](docs/Err.md)
- [Error](docs/Error.md)
- [FormTestRequestEnumField](docs/FormTestRequestEnumField.md)
- [Model12345AnyOfObject](docs/Model12345AnyOfObject.md)
- [Model12345AnyOfObjectAnyOf](docs/Model12345AnyOfObjectAnyOf.md)
- [MultigetGet201Response](docs/MultigetGet201Response.md)
- [MyId](docs/MyId.md)
- [MyIdList](docs/MyIdList.md)
- [NoTypeObject](docs/NoTypeObject.md)
- [NullableObject](docs/NullableObject.md)
- [NullableTest](docs/NullableTest.md)
- [ObjectHeader](docs/ObjectHeader.md)
- [ObjectParam](docs/ObjectParam.md)
- [ObjectUntypedProps](docs/ObjectUntypedProps.md)
- [ObjectWithArrayOfObjects](docs/ObjectWithArrayOfObjects.md)
- [Ok](docs/Ok.md)
- [OneOfGet200Response](docs/OneOfGet200Response.md)
- [OptionalObjectHeader](docs/OptionalObjectHeader.md)
- [RequiredObjectHeader](docs/RequiredObjectHeader.md)
- [Result](docs/Result.md)
- [StringEnum](docs/StringEnum.md)
- [StringObject](docs/StringObject.md)
- [UuidObject](docs/UuidObject.md)
- [XmlArray](docs/XmlArray.md)
- [XmlInner](docs/XmlInner.md)
- [XmlObject](docs/XmlObject.md)


## Documentation For Authorization

Authentication schemes defined for the API:
### authScheme
- **Type**: OAuth
- **Flow**: accessCode
- **Authorization URL**: http://example.org
- **Scopes**:
- **test.read**: Allowed to read state.
- **test.write**: Allowed to change state.

Example
```
```

Or via OAuth2 module to automatically refresh tokens and perform user authentication.
```
```
### additionalAuthScheme
- **Type**: OAuth
- **Flow**: accessCode
- **Authorization URL**: http://example.org
- **Scopes**:
- **additional.test.read**: Allowed to read state.
- **additional.test.write**: Allowed to change state.

Example
```
```

Or via OAuth2 module to automatically refresh tokens and perform user authentication.
```
```

## Author



Loading
Loading