[Rust Server] Add auto-generated CLI Client (#19392)

* [Rust Server] Add auto-generated CLI tool

* [Rust Server] Test multiple path parameters

* [Rust Server] Test boolean parameters and apostrophes

* [Rust Server] Test operation with two boolean parameters with same first letter

* [Rust Server] Test apostrophes in operation summary

* Update samples

* [Rust Server] Fix build errors with OpenSSL

* Update samples

* Update samples
This commit is contained in:
Richard Whitehouse 2024-08-24 16:57:03 +01:00 committed by GitHub
parent 69cce249f6
commit 2b40a2c058
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
43 changed files with 5197 additions and 29 deletions

View File

@ -250,6 +250,7 @@ public class RustServerCodegen extends AbstractRustCodegen implements CodegenCon
supportingFiles.add(new SupportingFile("example-ca.pem", "examples", "ca.pem"));
supportingFiles.add(new SupportingFile("example-server-chain.pem", "examples", "server-chain.pem"));
supportingFiles.add(new SupportingFile("example-server-key.pem", "examples", "server-key.pem"));
supportingFiles.add(new SupportingFile("bin-cli.mustache", "bin", "cli.rs"));
supportingFiles.add(new SupportingFile("README.mustache", "", "README.md")
.doNotOverwrite());
}
@ -586,6 +587,13 @@ public class RustServerCodegen extends AbstractRustCodegen implements CodegenCon
String vendorExtensionHttpMethod = op.httpMethod.toUpperCase(Locale.ROOT);
op.vendorExtensions.put("x-http-method", vendorExtensionHttpMethod);
boolean isDelete = op.httpMethod.toUpperCase(Locale.ROOT).equals("DELETE");
op.vendorExtensions.put("x-is-delete", isDelete);
if (isDelete) {
additionalProperties.put("apiHasDeleteMethods", true);
}
if (!op.vendorExtensions.containsKey("x-must-use-response")) {
// If there's more than one response, than by default the user must explicitly handle them
op.vendorExtensions.put("x-must-use-response", op.responses.size() > 1);
@ -858,6 +866,27 @@ public class RustServerCodegen extends AbstractRustCodegen implements CodegenCon
op.vendorExtensions.put("x-has-request-body", true);
}
// The CLI generates a structopt structure for each operation. This can only have a single
// use of a short option, which comes from the parameter name, so we need to police
// against duplicates
HashMap<Character, CodegenParameter> availableOptions = new HashMap();
for (CodegenParameter p : op.allParams) {
if (p.isBoolean && p.isPrimitiveType) {
char shortOption = p.paramName.charAt(0);
if (shortOption == 'a' || shortOption == 'o' || shortOption == 'f') {
// These are used by serverAddress, output, and force
p.vendorExtensions.put("x-provide-cli-short-opt", false);
} else if (availableOptions.containsKey(shortOption)) {
availableOptions.get(shortOption).vendorExtensions.put("x-provide-cli-short-opt", false);
p.vendorExtensions.put("x-provide-cli-short-opt", false);
} else {
availableOptions.put(shortOption, p);
p.vendorExtensions.put("x-provide-cli-short-opt", true);
}
}
}
String underscoredOperationId = underscore(op.operationId).toUpperCase(Locale.ROOT);
if (op.bodyParam != null) {

View File

@ -68,6 +68,12 @@ server = [
{{! Anything added to the list below, should probably be added to the callbacks list above }}
"serde_ignored", "hyper", "regex", "percent-encoding", "url", "lazy_static"
]
cli = [
{{#apiHasDeleteMethods}}
"dialoguer",
{{/apiHasDeleteMethods}}
"anyhow", "clap-verbosity-flag", "simple_logger", "structopt", "tokio"
]
conversion = ["frunk", "frunk_derives", "frunk_core", "frunk-enum-core", "frunk-enum-derive"]
[target.'cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))'.dependencies]
@ -126,6 +132,16 @@ lazy_static = { version = "1.4", optional = true }
percent-encoding = {version = "2.1.0", optional = true}
regex = {version = "1.3", optional = true}
# CLI-specific
anyhow = { version = "1", optional = true }
clap-verbosity-flag = { version = "0.3", optional = true }
simple_logger = { version = "2.0", features = ["stderr"], optional = true }
structopt = { version = "0.3", optional = true }
tokio = { version = "0.2", features = ["rt-threaded", "macros", "stream"], optional = true }
{{#apiHasDeleteMethods}}
dialoguer = { version = "0.8", optional = true }
{{/apiHasDeleteMethods}}
# Conversion
frunk = { version = "0.4.0", optional = true }
frunk_derives = { version = "0.4.0", optional = true }
@ -153,3 +169,8 @@ required-features = ["client"]
[[example]]
name = "server"
required-features = ["server"]
[[bin]]
name = "{{{packageName}}}"
path = "bin/cli.rs"
required-features = ["client", "cli"]

View File

@ -5,6 +5,7 @@ The Rust Server Generator templates use Mustache Partials.
The following tree shows which templates include which:
- `api_doc.mustache`
- `bin-cli.mustache`
- `cargo-config`
- `Cargo.mustache`
- `context.mustache`

View File

@ -28,6 +28,7 @@ This autogenerated project defines an API crate `{{{packageName}}}` which contai
* 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 `{{{packageName}}}`:
@ -41,6 +42,30 @@ It also contains an example server and client which make use of `{{{packageName}
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:
@ -103,6 +128,8 @@ The generated library has a few optional features that can be activated through
* 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`.

View File

@ -0,0 +1,317 @@
//! CLI tool driving the API client
use anyhow::{anyhow, Context, Result};
{{#apiHasDeleteMethods}}
use dialoguer::Confirm;
{{/apiHasDeleteMethods}}
use log::{debug, info};
// models may be unused if all inputs are primitive types
#[allow(unused_imports)]
use {{{externCrateName}}}::{
models, ApiNoContext, Client, ContextWrapperExt,
{{#apiInfo}}
{{#apis}}
{{#operations}}
{{#operation}}
{{{operationId}}}Response,
{{/operation}}
{{/operations}}
{{/apis}}
{{/apiInfo}}
};
use simple_logger::SimpleLogger;
use structopt::StructOpt;
use swagger::{AuthData, ContextBuilder, EmptyContext, Push, XSpanIdString};
type ClientContext = swagger::make_context_ty!(
ContextBuilder,
EmptyContext,
Option<AuthData>,
XSpanIdString
);
{{! See code in RustServerCodegen if you are adding additional short option usage here. }}
#[derive(StructOpt, Debug)]
#[structopt(
name = "{{appName}}",
version = "{{version}}",
about = "CLI access to {{appName}}"
)]
struct Cli {
#[structopt(subcommand)]
operation: Operation,
/// Address or hostname of the server hosting this API, including optional port
#[structopt(short = "a", long, default_value = "http://localhost")]
server_address: String,
/// Path to the client private key if using client-side TLS authentication
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
#[structopt(long, requires_all(&["client-certificate", "server-certificate"]))]
client_key: Option<String>,
/// Path to the client's public certificate associated with the private key
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
#[structopt(long, requires_all(&["client-key", "server-certificate"]))]
client_certificate: Option<String>,
/// Path to CA certificate used to authenticate the server
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
#[structopt(long)]
server_certificate: Option<String>,
/// If set, write output to file instead of stdout
#[structopt(short, long)]
output_file: Option<String>,
#[structopt(flatten)]
verbosity: clap_verbosity_flag::Verbosity,
{{#apiHasDeleteMethods}}
/// Don't ask for any confirmation prompts
#[allow(dead_code)]
#[structopt(short, long)]
force: bool,
{{/apiHasDeleteMethods}}
{{#hasHttpBearerMethods}}
/// Bearer token if used for authentication
#[structopt(env = "{{#lambda.uppercase}}{{externCrateName}}{{/lambda.uppercase}}_BEARER_TOKEN", hide_env_values = true)]
bearer_token: Option<String>,
{{/hasHttpBearerMethods}}
{{^hasHttpBearerMethods}}
{{#hasOAuthMethods}}
/// Bearer token if used for authentication
#[structopt(env = "{{#lambda.uppercase}}{{externCrateName}}{{/lambda.uppercase}}_BEARER_TOKEN", hide_env_values = true)]
bearer_token: Option<String>,
{{/hasOAuthMethods}}
{{/hasHttpBearerMethods}}
}
#[derive(StructOpt, Debug)]
enum Operation {
{{#apiInfo}}
{{#apis}}
{{#operations}}
{{#operation}}
{{#summary}}
/// {{{summary}}}
{{/summary}}
{{operationId}} {
{{#allParams}}
{{#description}}
/// {{{description}}}
{{/description}}
{{^isPrimitiveType}}
#[structopt(parse(try_from_str = parse_json){{#isArray}}, long{{/isArray}})]
{{/isPrimitiveType}}
{{#isByteArray}}
#[structopt(parse(try_from_str = parse_json))]
{{/isByteArray}}
{{#isBinary}}
#[structopt(parse(try_from_str = parse_json))]
{{/isBinary}}
{{#isBoolean}}
{{#isPrimitiveType}}
{{#vendorExtensions.x-provide-cli-short-opt}}
#[structopt(short, long)]
{{/vendorExtensions.x-provide-cli-short-opt}}
{{^vendorExtensions.x-provide-cli-short-opt}}
#[structopt(long)]
{{/vendorExtensions.x-provide-cli-short-opt}}
{{/isPrimitiveType}}
{{/isBoolean}}
{{{paramName}}}: {{^required}}Option<{{/required}}{{{dataType}}}{{^required}}>{{/required}},
{{/allParams}}
},
{{/operation}}
{{/operations}}
{{/apis}}
{{/apiInfo}}
}
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
fn create_client(args: &Cli, context: ClientContext) -> Result<Box<dyn ApiNoContext<ClientContext>>> {
if args.client_certificate.is_some() {
debug!("Using mutual TLS");
let client = Client::try_new_https_mutual(
&args.server_address,
args.server_certificate.clone().unwrap(),
args.client_key.clone().unwrap(),
args.client_certificate.clone().unwrap(),
)
.context("Failed to create HTTPS client")?;
Ok(Box::new(client.with_context(context)))
} else if args.server_certificate.is_some() {
debug!("Using TLS with pinned server certificate");
let client =
Client::try_new_https_pinned(&args.server_address, args.server_certificate.clone().unwrap())
.context("Failed to create HTTPS client")?;
Ok(Box::new(client.with_context(context)))
} else {
debug!("Using client without certificates");
let client =
Client::try_new(&args.server_address).context("Failed to create HTTP(S) client")?;
Ok(Box::new(client.with_context(context)))
}
}
#[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))]
fn create_client(args: &Cli, context: ClientContext) -> Result<Box<dyn ApiNoContext<ClientContext>>> {
let client =
Client::try_new(&args.server_address).context("Failed to create HTTP(S) client")?;
Ok(Box::new(client.with_context(context)))
}
#[tokio::main]
async fn main() -> Result<()> {
let args = Cli::from_args();
if let Some(log_level) = args.verbosity.log_level() {
SimpleLogger::new().with_level(log_level.to_level_filter()).init()?;
}
debug!("Arguments: {:?}", &args);
{{#hasHttpBearerMethods}}
let mut auth_data: Option<AuthData> = None;
if let Some(ref bearer_token) = args.bearer_token {
debug!("Using bearer token");
auth_data = Some(AuthData::bearer(bearer_token));
}
{{/hasHttpBearerMethods}}
{{^hasHttpBearerMethods}}
{{#hasOAuthMethods}}
let mut auth_data: Option<AuthData> = None;
if let Some(ref bearer_token) = args.bearer_token {
debug!("Using bearer token");
auth_data = Some(AuthData::bearer(bearer_token));
}
{{/hasOAuthMethods}}
{{/hasHttpBearerMethods}}
{{^hasHttpBearerMethods}}
{{^hasOAuthMethods}}
let auth_data: Option<AuthData> = None;
{{/hasOAuthMethods}}
{{/hasHttpBearerMethods}}
#[allow(trivial_casts)]
let context = swagger::make_context!(
ContextBuilder,
EmptyContext,
auth_data,
XSpanIdString::default()
);
let client = create_client(&args, context)?;
let result = match args.operation {
{{#apiInfo}}
{{#apis}}
{{#operations}}
{{#operation}}
Operation::{{operationId}} {
{{#allParams}}
{{paramName}},
{{/allParams}}
} => {
{{#vendorExtensions.x-is-delete}}
prompt(args.force, "This will delete the given entry, are you sure?")?;
{{/vendorExtensions.x-is-delete}}
info!("Performing a {{operationId}} request{{^pathParams}}");{{/pathParams}}{{#pathParams}}{{#-first}} on {:?}", ({{/-first}}{{/pathParams}}
{{#pathParams}}
&{{paramName}}{{^-last}},{{/-last}}
{{#-last}}
));
{{/-last}}
{{/pathParams}}
let result = client.{{{vendorExtensions.x-operation-id}}}(
{{#allParams}}
{{{paramName}}}{{#isArray}}.as_ref(){{/isArray}},
{{/allParams}}
).await?;
debug!("Result: {:?}", result);
match result {
{{#responses}}
{{{operationId}}}Response::{{{vendorExtensions.x-response-id}}}
{{#dataType}}
{{^hasHeaders}}
(body)
{{/hasHeaders}}
{{#hasHeaders}}
{
body,
{{/hasHeaders}}
{{/dataType}}
{{^dataType}}
{{#hasHeaders}}
{
{{/hasHeaders}}
{{/dataType}}
{{#headers}}
{{{name}}},
{{#-last}}
}
{{/-last}}
{{/headers}}
=> "{{{vendorExtensions.x-response-id}}}\n".to_string()
{{#dataType}}
+
{{/dataType}}
{{^dataType}}
{{#hasHeaders}}
+
{{/hasHeaders}}
{{^hasHeaders}}
,
{{/hasHeaders}}
{{/dataType}}
{{#dataType}}
{{^hasHeaders}}
&serde_json::to_string_pretty(&body)?,
{{/hasHeaders}}
{{#hasHeaders}}
&format!("body: {}\n", serde_json::to_string_pretty(&body)?) +
{{/hasHeaders}}
{{/dataType}}
{{#headers}}
&format!(
"{{{name}}}: {}\n",
serde_json::to_string_pretty(&{{{name}}})?
){{^-last}} +{{/-last}}{{#-last}},{{/-last}}
{{/headers}}
{{/responses}}
}
}
{{/operation}}
{{/operations}}
{{/apis}}
{{/apiInfo}}
};
if let Some(output_file) = args.output_file {
std::fs::write(output_file, result)?
} else {
println!("{}", result);
}
Ok(())
}
{{#apiHasDeleteMethods}}
fn prompt(force: bool, text: &str) -> Result<()> {
if force || Confirm::new().with_prompt(text).interact()? {
Ok(())
} else {
Err(anyhow!("Aborting"))
}
}
{{/apiHasDeleteMethods}}
// May be unused if all inputs are primitive types
#[allow(dead_code)]
fn parse_json<'a, T: serde::de::Deserialize<'a>>(json_string: &'a str) -> Result<T> {
serde_json::from_str(json_string).map_err(|err| anyhow!("Error parsing input: {}", err))
}

View File

@ -22,7 +22,7 @@ info:
paths:
/xml:
post:
summary: Post an array
summary: Post an array. It's important we test apostrophes, so include one here.
description: ''
requestBody:
content:
@ -449,6 +449,52 @@ paths:
responses:
'200':
description: Success
/multiple-path-params-with-very-long-path-to-test-formatting/{path_param_a}/{path_param_b}:
get:
parameters:
- name: path_param_a
in: path
required: true
schema:
$ref: '#/components/schemas/StringObject'
- name: path_param_b
in: path
required: true
schema:
$ref: '#/components/schemas/StringObject'
responses:
'200':
description: Success
/get-with-bool:
get:
operationId: GetWithBooleanParameter
description: Get with a boolean parameter
parameters:
- name: iambool
description: Let's check apostrophes get encoded properly!
in: query
required: true
schema:
type: boolean
responses:
200:
description: 'OK'
/operation-two-first-letter-headers:
post:
operationId: TwoFirstLetterHeaders
description: Check we don't barf if two boolean parameters have the same first letter
parameters:
- name: x-header-one
in: header
schema:
type: boolean
- name: x-header-two
in: header
schema:
type: boolean
responses:
200:
description: OK
components:
securitySchemes:
@ -461,6 +507,15 @@ components:
scopes:
test.read: Allowed to read state.
test.write: Allowed to change state.
additionalAuthScheme:
type: oauth2
flows:
authorizationCode:
authorizationUrl: 'http://example.org'
tokenUrl: 'http://example.org'
scopes:
additional.test.read: Allowed to read state.
additional.test.write: Allowed to change state.
schemas:
AnyOfProperty:
description: Test containing an anyOf object

View File

@ -3,6 +3,7 @@
Cargo.toml
README.md
api/openapi.yaml
bin/cli.rs
docs/MultipartRelatedRequest.md
docs/MultipartRequestObjectField.md
docs/MultipleIdenticalMimeTypesPostRequest.md

View File

@ -21,6 +21,9 @@ server = [
"hyper_0_10", "mime_multipart", "swagger/multipart_related",
"serde_ignored", "hyper", "regex", "percent-encoding", "url", "lazy_static"
]
cli = [
"anyhow", "clap-verbosity-flag", "simple_logger", "structopt", "tokio"
]
conversion = ["frunk", "frunk_derives", "frunk_core", "frunk-enum-core", "frunk-enum-derive"]
[target.'cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))'.dependencies]
@ -62,6 +65,13 @@ lazy_static = { version = "1.4", optional = true }
percent-encoding = {version = "2.1.0", optional = true}
regex = {version = "1.3", optional = true}
# CLI-specific
anyhow = { version = "1", optional = true }
clap-verbosity-flag = { version = "0.3", optional = true }
simple_logger = { version = "2.0", features = ["stderr"], optional = true }
structopt = { version = "0.3", optional = true }
tokio = { version = "0.2", features = ["rt-threaded", "macros", "stream"], optional = true }
# Conversion
frunk = { version = "0.4.0", optional = true }
frunk_derives = { version = "0.4.0", optional = true }
@ -89,3 +99,8 @@ required-features = ["client"]
[[example]]
name = "server"
required-features = ["server"]
[[bin]]
name = "multipart-v3"
path = "bin/cli.rs"
required-features = ["client", "cli"]

View File

@ -23,6 +23,7 @@ This autogenerated project defines an API crate `multipart-v3` which contains:
* 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 `multipart-v3`:
@ -36,6 +37,30 @@ It also contains an example server and client which make use of `multipart-v3`:
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:
@ -88,6 +113,8 @@ The generated library has a few optional features that can be activated through
* 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`.

View File

@ -0,0 +1,215 @@
//! CLI tool driving the API client
use anyhow::{anyhow, Context, Result};
use log::{debug, info};
// models may be unused if all inputs are primitive types
#[allow(unused_imports)]
use multipart_v3::{
models, ApiNoContext, Client, ContextWrapperExt,
MultipartRelatedRequestPostResponse,
MultipartRequestPostResponse,
MultipleIdenticalMimeTypesPostResponse,
};
use simple_logger::SimpleLogger;
use structopt::StructOpt;
use swagger::{AuthData, ContextBuilder, EmptyContext, Push, XSpanIdString};
type ClientContext = swagger::make_context_ty!(
ContextBuilder,
EmptyContext,
Option<AuthData>,
XSpanIdString
);
#[derive(StructOpt, Debug)]
#[structopt(
name = "Multipart OpenAPI V3 Rust Server Test",
version = "1.0.7",
about = "CLI access to Multipart OpenAPI V3 Rust Server Test"
)]
struct Cli {
#[structopt(subcommand)]
operation: Operation,
/// Address or hostname of the server hosting this API, including optional port
#[structopt(short = "a", long, default_value = "http://localhost")]
server_address: String,
/// Path to the client private key if using client-side TLS authentication
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
#[structopt(long, requires_all(&["client-certificate", "server-certificate"]))]
client_key: Option<String>,
/// Path to the client's public certificate associated with the private key
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
#[structopt(long, requires_all(&["client-key", "server-certificate"]))]
client_certificate: Option<String>,
/// Path to CA certificate used to authenticate the server
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
#[structopt(long)]
server_certificate: Option<String>,
/// If set, write output to file instead of stdout
#[structopt(short, long)]
output_file: Option<String>,
#[structopt(flatten)]
verbosity: clap_verbosity_flag::Verbosity,
}
#[derive(StructOpt, Debug)]
enum Operation {
MultipartRelatedRequestPost {
#[structopt(parse(try_from_str = parse_json))]
required_binary_field: swagger::ByteArray,
#[structopt(parse(try_from_str = parse_json))]
object_field: Option<models::MultipartRequestObjectField>,
#[structopt(parse(try_from_str = parse_json))]
optional_binary_field: Option<swagger::ByteArray>,
},
MultipartRequestPost {
string_field: String,
#[structopt(parse(try_from_str = parse_json))]
binary_field: swagger::ByteArray,
optional_string_field: Option<String>,
#[structopt(parse(try_from_str = parse_json))]
object_field: Option<models::MultipartRequestObjectField>,
},
MultipleIdenticalMimeTypesPost {
#[structopt(parse(try_from_str = parse_json))]
binary1: Option<swagger::ByteArray>,
#[structopt(parse(try_from_str = parse_json))]
binary2: Option<swagger::ByteArray>,
},
}
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
fn create_client(args: &Cli, context: ClientContext) -> Result<Box<dyn ApiNoContext<ClientContext>>> {
if args.client_certificate.is_some() {
debug!("Using mutual TLS");
let client = Client::try_new_https_mutual(
&args.server_address,
args.server_certificate.clone().unwrap(),
args.client_key.clone().unwrap(),
args.client_certificate.clone().unwrap(),
)
.context("Failed to create HTTPS client")?;
Ok(Box::new(client.with_context(context)))
} else if args.server_certificate.is_some() {
debug!("Using TLS with pinned server certificate");
let client =
Client::try_new_https_pinned(&args.server_address, args.server_certificate.clone().unwrap())
.context("Failed to create HTTPS client")?;
Ok(Box::new(client.with_context(context)))
} else {
debug!("Using client without certificates");
let client =
Client::try_new(&args.server_address).context("Failed to create HTTP(S) client")?;
Ok(Box::new(client.with_context(context)))
}
}
#[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))]
fn create_client(args: &Cli, context: ClientContext) -> Result<Box<dyn ApiNoContext<ClientContext>>> {
let client =
Client::try_new(&args.server_address).context("Failed to create HTTP(S) client")?;
Ok(Box::new(client.with_context(context)))
}
#[tokio::main]
async fn main() -> Result<()> {
let args = Cli::from_args();
if let Some(log_level) = args.verbosity.log_level() {
SimpleLogger::new().with_level(log_level.to_level_filter()).init()?;
}
debug!("Arguments: {:?}", &args);
let auth_data: Option<AuthData> = None;
#[allow(trivial_casts)]
let context = swagger::make_context!(
ContextBuilder,
EmptyContext,
auth_data,
XSpanIdString::default()
);
let client = create_client(&args, context)?;
let result = match args.operation {
Operation::MultipartRelatedRequestPost {
required_binary_field,
object_field,
optional_binary_field,
} => {
info!("Performing a MultipartRelatedRequestPost request");
let result = client.multipart_related_request_post(
required_binary_field,
object_field,
optional_binary_field,
).await?;
debug!("Result: {:?}", result);
match result {
MultipartRelatedRequestPostResponse::OK
=> "OK\n".to_string()
,
}
}
Operation::MultipartRequestPost {
string_field,
binary_field,
optional_string_field,
object_field,
} => {
info!("Performing a MultipartRequestPost request");
let result = client.multipart_request_post(
string_field,
binary_field,
optional_string_field,
object_field,
).await?;
debug!("Result: {:?}", result);
match result {
MultipartRequestPostResponse::OK
=> "OK\n".to_string()
,
}
}
Operation::MultipleIdenticalMimeTypesPost {
binary1,
binary2,
} => {
info!("Performing a MultipleIdenticalMimeTypesPost request");
let result = client.multiple_identical_mime_types_post(
binary1,
binary2,
).await?;
debug!("Result: {:?}", result);
match result {
MultipleIdenticalMimeTypesPostResponse::OK
=> "OK\n".to_string()
,
}
}
};
if let Some(output_file) = args.output_file {
std::fs::write(output_file, result)?
} else {
println!("{}", result);
}
Ok(())
}
// May be unused if all inputs are primitive types
#[allow(dead_code)]
fn parse_json<'a, T: serde::de::Deserialize<'a>>(json_string: &'a str) -> Result<T> {
serde_json::from_str(json_string).map_err(|err| anyhow!("Error parsing input: {}", err))
}

View File

@ -3,6 +3,7 @@
Cargo.toml
README.md
api/openapi.yaml
bin/cli.rs
docs/OpGetRequest.md
docs/default_api.md
examples/ca.pem

View File

@ -15,6 +15,9 @@ client = [
server = [
"serde_ignored", "hyper", "regex", "percent-encoding", "url", "lazy_static"
]
cli = [
"anyhow", "clap-verbosity-flag", "simple_logger", "structopt", "tokio"
]
conversion = ["frunk", "frunk_derives", "frunk_core", "frunk-enum-core", "frunk-enum-derive"]
[target.'cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))'.dependencies]
@ -52,6 +55,13 @@ lazy_static = { version = "1.4", optional = true }
percent-encoding = {version = "2.1.0", optional = true}
regex = {version = "1.3", optional = true}
# CLI-specific
anyhow = { version = "1", optional = true }
clap-verbosity-flag = { version = "0.3", optional = true }
simple_logger = { version = "2.0", features = ["stderr"], optional = true }
structopt = { version = "0.3", optional = true }
tokio = { version = "0.2", features = ["rt-threaded", "macros", "stream"], optional = true }
# Conversion
frunk = { version = "0.4.0", optional = true }
frunk_derives = { version = "0.4.0", optional = true }
@ -79,3 +89,8 @@ required-features = ["client"]
[[example]]
name = "server"
required-features = ["server"]
[[bin]]
name = "no-example-v3"
path = "bin/cli.rs"
required-features = ["client", "cli"]

View File

@ -23,6 +23,7 @@ This autogenerated project defines an API crate `no-example-v3` which contains:
* 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 `no-example-v3`:
@ -36,6 +37,30 @@ It also contains an example server and client which make use of `no-example-v3`:
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:
@ -85,6 +110,8 @@ The generated library has a few optional features that can be activated through
* 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`.

View File

@ -0,0 +1,151 @@
//! CLI tool driving the API client
use anyhow::{anyhow, Context, Result};
use log::{debug, info};
// models may be unused if all inputs are primitive types
#[allow(unused_imports)]
use no_example_v3::{
models, ApiNoContext, Client, ContextWrapperExt,
OpGetResponse,
};
use simple_logger::SimpleLogger;
use structopt::StructOpt;
use swagger::{AuthData, ContextBuilder, EmptyContext, Push, XSpanIdString};
type ClientContext = swagger::make_context_ty!(
ContextBuilder,
EmptyContext,
Option<AuthData>,
XSpanIdString
);
#[derive(StructOpt, Debug)]
#[structopt(
name = "Regression test for an API which doesn&#39;t have any example",
version = "0.0.1",
about = "CLI access to Regression test for an API which doesn&#39;t have any example"
)]
struct Cli {
#[structopt(subcommand)]
operation: Operation,
/// Address or hostname of the server hosting this API, including optional port
#[structopt(short = "a", long, default_value = "http://localhost")]
server_address: String,
/// Path to the client private key if using client-side TLS authentication
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
#[structopt(long, requires_all(&["client-certificate", "server-certificate"]))]
client_key: Option<String>,
/// Path to the client's public certificate associated with the private key
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
#[structopt(long, requires_all(&["client-key", "server-certificate"]))]
client_certificate: Option<String>,
/// Path to CA certificate used to authenticate the server
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
#[structopt(long)]
server_certificate: Option<String>,
/// If set, write output to file instead of stdout
#[structopt(short, long)]
output_file: Option<String>,
#[structopt(flatten)]
verbosity: clap_verbosity_flag::Verbosity,
}
#[derive(StructOpt, Debug)]
enum Operation {
OpGet {
#[structopt(parse(try_from_str = parse_json))]
op_get_request: models::OpGetRequest,
},
}
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
fn create_client(args: &Cli, context: ClientContext) -> Result<Box<dyn ApiNoContext<ClientContext>>> {
if args.client_certificate.is_some() {
debug!("Using mutual TLS");
let client = Client::try_new_https_mutual(
&args.server_address,
args.server_certificate.clone().unwrap(),
args.client_key.clone().unwrap(),
args.client_certificate.clone().unwrap(),
)
.context("Failed to create HTTPS client")?;
Ok(Box::new(client.with_context(context)))
} else if args.server_certificate.is_some() {
debug!("Using TLS with pinned server certificate");
let client =
Client::try_new_https_pinned(&args.server_address, args.server_certificate.clone().unwrap())
.context("Failed to create HTTPS client")?;
Ok(Box::new(client.with_context(context)))
} else {
debug!("Using client without certificates");
let client =
Client::try_new(&args.server_address).context("Failed to create HTTP(S) client")?;
Ok(Box::new(client.with_context(context)))
}
}
#[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))]
fn create_client(args: &Cli, context: ClientContext) -> Result<Box<dyn ApiNoContext<ClientContext>>> {
let client =
Client::try_new(&args.server_address).context("Failed to create HTTP(S) client")?;
Ok(Box::new(client.with_context(context)))
}
#[tokio::main]
async fn main() -> Result<()> {
let args = Cli::from_args();
if let Some(log_level) = args.verbosity.log_level() {
SimpleLogger::new().with_level(log_level.to_level_filter()).init()?;
}
debug!("Arguments: {:?}", &args);
let auth_data: Option<AuthData> = None;
#[allow(trivial_casts)]
let context = swagger::make_context!(
ContextBuilder,
EmptyContext,
auth_data,
XSpanIdString::default()
);
let client = create_client(&args, context)?;
let result = match args.operation {
Operation::OpGet {
op_get_request,
} => {
info!("Performing a OpGet request");
let result = client.op_get(
op_get_request,
).await?;
debug!("Result: {:?}", result);
match result {
OpGetResponse::OK
=> "OK\n".to_string()
,
}
}
};
if let Some(output_file) = args.output_file {
std::fs::write(output_file, result)?
} else {
println!("{}", result);
}
Ok(())
}
// May be unused if all inputs are primitive types
#[allow(dead_code)]
fn parse_json<'a, T: serde::de::Deserialize<'a>>(json_string: &'a str) -> Result<T> {
serde_json::from_str(json_string).map_err(|err| anyhow!("Error parsing input: {}", err))
}

View File

@ -3,6 +3,7 @@
Cargo.toml
README.md
api/openapi.yaml
bin/cli.rs
docs/AdditionalPropertiesWithList.md
docs/AnotherXmlArray.md
docs/AnotherXmlInner.md

View File

@ -17,6 +17,9 @@ server = [
"native-tls", "hyper-openssl", "hyper-tls", "openssl",
"serde_ignored", "hyper", "regex", "percent-encoding", "url", "lazy_static"
]
cli = [
"anyhow", "clap-verbosity-flag", "simple_logger", "structopt", "tokio"
]
conversion = ["frunk", "frunk_derives", "frunk_core", "frunk-enum-core", "frunk-enum-derive"]
[target.'cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))'.dependencies]
@ -58,6 +61,13 @@ lazy_static = { version = "1.4", optional = true }
percent-encoding = {version = "2.1.0", optional = true}
regex = {version = "1.3", optional = true}
# CLI-specific
anyhow = { version = "1", optional = true }
clap-verbosity-flag = { version = "0.3", optional = true }
simple_logger = { version = "2.0", features = ["stderr"], optional = true }
structopt = { version = "0.3", optional = true }
tokio = { version = "0.2", features = ["rt-threaded", "macros", "stream"], optional = true }
# Conversion
frunk = { version = "0.4.0", optional = true }
frunk_derives = { version = "0.4.0", optional = true }
@ -85,3 +95,8 @@ required-features = ["client"]
[[example]]
name = "server"
required-features = ["server"]
[[bin]]
name = "openapi-v3"
path = "bin/cli.rs"
required-features = ["client", "cli"]

View File

@ -23,6 +23,7 @@ This autogenerated project defines an API crate `openapi-v3` which contains:
* 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`:
@ -36,6 +37,30 @@ It also contains an example server and client which make use of `openapi-v3`:
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:
@ -64,6 +89,7 @@ To run a client, follow one of the following simple steps:
cargo run --example client AnyOfGet
cargo run --example client CallbackWithHeaderPost
cargo run --example client ComplexQueryParamGet
cargo run --example client GetWithBooleanParameter
cargo run --example client JsonComplexQueryParamGet
cargo run --example client MandatoryRequestHeaderGet
cargo run --example client MergePatchJsonGet
@ -77,6 +103,7 @@ cargo run --example client RegisterCallbackPost
cargo run --example client RequiredOctetStreamPut
cargo run --example client ResponsesWithHeadersGet
cargo run --example client Rfc7807Get
cargo run --example client TwoFirstLetterHeaders
cargo run --example client UntypedPropertyGet
cargo run --example client UuidGet
cargo run --example client XmlExtraPost
@ -84,6 +111,7 @@ cargo run --example client XmlOtherPost
cargo run --example client XmlOtherPut
cargo run --example client XmlPost
cargo run --example client XmlPut
cargo run --example client MultiplePathParamsWithVeryLongPathToTestFormattingPathParamAPathParamBGet
cargo run --example client CreateRepo
cargo run --example client GetRepoInfo
```
@ -110,6 +138,8 @@ The generated library has a few optional features that can be activated through
* 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`.
@ -122,6 +152,7 @@ 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 |
[**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 |
@ -135,14 +166,16 @@ Method | HTTP request | Description
[****](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
[****](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} |
@ -200,6 +233,21 @@ 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.
```
```

View File

@ -19,7 +19,8 @@ paths:
description: OK
"400":
description: Bad Request
summary: Post an array
summary: "Post an array. It's important we test apostrophes, so include one\
\ here."
put:
requestBody:
content:
@ -465,6 +466,65 @@ paths:
responses:
"200":
description: Success
/multiple-path-params-with-very-long-path-to-test-formatting/{path_param_a}/{path_param_b}:
get:
parameters:
- explode: false
in: path
name: path_param_a
required: true
schema:
$ref: '#/components/schemas/StringObject'
style: simple
- explode: false
in: path
name: path_param_b
required: true
schema:
$ref: '#/components/schemas/StringObject'
style: simple
responses:
"200":
description: Success
/get-with-bool:
get:
description: Get with a boolean parameter
operationId: GetWithBooleanParameter
parameters:
- description: Let's check apostrophes get encoded properly!
explode: true
in: query
name: iambool
required: true
schema:
type: boolean
style: form
responses:
"200":
description: OK
/operation-two-first-letter-headers:
post:
description: Check we don't barf if two boolean parameters have the same first
letter
operationId: TwoFirstLetterHeaders
parameters:
- explode: false
in: header
name: x-header-one
required: false
schema:
type: boolean
style: simple
- explode: false
in: header
name: x-header-two
required: false
schema:
type: boolean
style: simple
responses:
"200":
description: OK
components:
schemas:
AnyOfProperty:
@ -722,4 +782,13 @@ components:
test.write: Allowed to change state.
tokenUrl: http://example.org
type: oauth2
additionalAuthScheme:
flows:
authorizationCode:
authorizationUrl: http://example.org
scopes:
additional.test.read: Allowed to read state.
additional.test.write: Allowed to change state.
tokenUrl: http://example.org
type: oauth2

View File

@ -0,0 +1,854 @@
//! CLI tool driving the API client
use anyhow::{anyhow, Context, Result};
use log::{debug, info};
// models may be unused if all inputs are primitive types
#[allow(unused_imports)]
use openapi_v3::{
models, ApiNoContext, Client, ContextWrapperExt,
AnyOfGetResponse,
CallbackWithHeaderPostResponse,
ComplexQueryParamGetResponse,
GetWithBooleanParameterResponse,
JsonComplexQueryParamGetResponse,
MandatoryRequestHeaderGetResponse,
MergePatchJsonGetResponse,
MultigetGetResponse,
MultipleAuthSchemeGetResponse,
OneOfGetResponse,
OverrideServerGetResponse,
ParamgetGetResponse,
ReadonlyAuthSchemeGetResponse,
RegisterCallbackPostResponse,
RequiredOctetStreamPutResponse,
ResponsesWithHeadersGetResponse,
Rfc7807GetResponse,
TwoFirstLetterHeadersResponse,
UntypedPropertyGetResponse,
UuidGetResponse,
XmlExtraPostResponse,
XmlOtherPostResponse,
XmlOtherPutResponse,
XmlPostResponse,
XmlPutResponse,
EnumInPathPathParamGetResponse,
MultiplePathParamsWithVeryLongPathToTestFormattingPathParamAPathParamBGetResponse,
CreateRepoResponse,
GetRepoInfoResponse,
};
use simple_logger::SimpleLogger;
use structopt::StructOpt;
use swagger::{AuthData, ContextBuilder, EmptyContext, Push, XSpanIdString};
type ClientContext = swagger::make_context_ty!(
ContextBuilder,
EmptyContext,
Option<AuthData>,
XSpanIdString
);
#[derive(StructOpt, Debug)]
#[structopt(
name = "My title",
version = "1.0.7",
about = "CLI access to My title"
)]
struct Cli {
#[structopt(subcommand)]
operation: Operation,
/// Address or hostname of the server hosting this API, including optional port
#[structopt(short = "a", long, default_value = "http://localhost")]
server_address: String,
/// Path to the client private key if using client-side TLS authentication
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
#[structopt(long, requires_all(&["client-certificate", "server-certificate"]))]
client_key: Option<String>,
/// Path to the client's public certificate associated with the private key
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
#[structopt(long, requires_all(&["client-key", "server-certificate"]))]
client_certificate: Option<String>,
/// Path to CA certificate used to authenticate the server
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
#[structopt(long)]
server_certificate: Option<String>,
/// If set, write output to file instead of stdout
#[structopt(short, long)]
output_file: Option<String>,
#[structopt(flatten)]
verbosity: clap_verbosity_flag::Verbosity,
/// Bearer token if used for authentication
#[structopt(env = "OPENAPI_V3_BEARER_TOKEN", hide_env_values = true)]
bearer_token: Option<String>,
}
#[derive(StructOpt, Debug)]
enum Operation {
AnyOfGet {
/// list of any of objects
#[structopt(parse(try_from_str = parse_json), long)]
any_of: Option<Vec<models::AnyOfObject>>,
},
CallbackWithHeaderPost {
url: String,
},
ComplexQueryParamGet {
#[structopt(parse(try_from_str = parse_json), long)]
list_of_strings: Option<Vec<models::StringObject>>,
},
GetWithBooleanParameter {
/// Let's check apostrophes get encoded properly!
#[structopt(short, long)]
iambool: bool,
},
JsonComplexQueryParamGet {
#[structopt(parse(try_from_str = parse_json), long)]
list_of_strings: Option<Vec<models::StringObject>>,
},
MandatoryRequestHeaderGet {
x_header: String,
},
MergePatchJsonGet {
},
/// Get some stuff.
MultigetGet {
},
MultipleAuthSchemeGet {
},
OneOfGet {
},
OverrideServerGet {
},
/// Get some stuff with parameters.
ParamgetGet {
/// The stuff to get
#[structopt(parse(try_from_str = parse_json))]
uuid: Option<uuid::Uuid>,
/// Some object to pass as query parameter
#[structopt(parse(try_from_str = parse_json))]
some_object: Option<models::ObjectParam>,
/// Some list to pass as query parameter
#[structopt(parse(try_from_str = parse_json))]
some_list: Option<models::MyIdList>,
},
ReadonlyAuthSchemeGet {
},
RegisterCallbackPost {
url: String,
},
RequiredOctetStreamPut {
#[structopt(parse(try_from_str = parse_json))]
body: swagger::ByteArray,
},
ResponsesWithHeadersGet {
},
Rfc7807Get {
},
TwoFirstLetterHeaders {
#[structopt(long)]
x_header_one: Option<bool>,
#[structopt(long)]
x_header_two: Option<bool>,
},
UntypedPropertyGet {
#[structopt(parse(try_from_str = parse_json))]
object_untyped_props: Option<models::ObjectUntypedProps>,
},
UuidGet {
},
XmlExtraPost {
#[structopt(parse(try_from_str = parse_json))]
duplicate_xml_object: Option<models::DuplicateXmlObject>,
},
XmlOtherPost {
#[structopt(parse(try_from_str = parse_json))]
another_xml_object: Option<models::AnotherXmlObject>,
},
XmlOtherPut {
#[structopt(parse(try_from_str = parse_json))]
another_xml_array: Option<models::AnotherXmlArray>,
},
/// Post an array. It's important we test apostrophes, so include one here.
XmlPost {
#[structopt(parse(try_from_str = parse_json))]
xml_array: Option<models::XmlArray>,
},
XmlPut {
#[structopt(parse(try_from_str = parse_json))]
xml_object: Option<models::XmlObject>,
},
EnumInPathPathParamGet {
#[structopt(parse(try_from_str = parse_json))]
path_param: models::StringEnum,
},
MultiplePathParamsWithVeryLongPathToTestFormattingPathParamAPathParamBGet {
path_param_a: String,
path_param_b: String,
},
CreateRepo {
#[structopt(parse(try_from_str = parse_json))]
object_param: models::ObjectParam,
},
GetRepoInfo {
repo_id: String,
},
}
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
fn create_client(args: &Cli, context: ClientContext) -> Result<Box<dyn ApiNoContext<ClientContext>>> {
if args.client_certificate.is_some() {
debug!("Using mutual TLS");
let client = Client::try_new_https_mutual(
&args.server_address,
args.server_certificate.clone().unwrap(),
args.client_key.clone().unwrap(),
args.client_certificate.clone().unwrap(),
)
.context("Failed to create HTTPS client")?;
Ok(Box::new(client.with_context(context)))
} else if args.server_certificate.is_some() {
debug!("Using TLS with pinned server certificate");
let client =
Client::try_new_https_pinned(&args.server_address, args.server_certificate.clone().unwrap())
.context("Failed to create HTTPS client")?;
Ok(Box::new(client.with_context(context)))
} else {
debug!("Using client without certificates");
let client =
Client::try_new(&args.server_address).context("Failed to create HTTP(S) client")?;
Ok(Box::new(client.with_context(context)))
}
}
#[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))]
fn create_client(args: &Cli, context: ClientContext) -> Result<Box<dyn ApiNoContext<ClientContext>>> {
let client =
Client::try_new(&args.server_address).context("Failed to create HTTP(S) client")?;
Ok(Box::new(client.with_context(context)))
}
#[tokio::main]
async fn main() -> Result<()> {
let args = Cli::from_args();
if let Some(log_level) = args.verbosity.log_level() {
SimpleLogger::new().with_level(log_level.to_level_filter()).init()?;
}
debug!("Arguments: {:?}", &args);
let mut auth_data: Option<AuthData> = None;
if let Some(ref bearer_token) = args.bearer_token {
debug!("Using bearer token");
auth_data = Some(AuthData::bearer(bearer_token));
}
#[allow(trivial_casts)]
let context = swagger::make_context!(
ContextBuilder,
EmptyContext,
auth_data,
XSpanIdString::default()
);
let client = create_client(&args, context)?;
let result = match args.operation {
Operation::AnyOfGet {
any_of,
} => {
info!("Performing a AnyOfGet request");
let result = client.any_of_get(
any_of.as_ref(),
).await?;
debug!("Result: {:?}", result);
match result {
AnyOfGetResponse::Success
(body)
=> "Success\n".to_string()
+
&serde_json::to_string_pretty(&body)?,
AnyOfGetResponse::AlternateSuccess
(body)
=> "AlternateSuccess\n".to_string()
+
&serde_json::to_string_pretty(&body)?,
AnyOfGetResponse::AnyOfSuccess
(body)
=> "AnyOfSuccess\n".to_string()
+
&serde_json::to_string_pretty(&body)?,
}
}
Operation::CallbackWithHeaderPost {
url,
} => {
info!("Performing a CallbackWithHeaderPost request");
let result = client.callback_with_header_post(
url,
).await?;
debug!("Result: {:?}", result);
match result {
CallbackWithHeaderPostResponse::OK
=> "OK\n".to_string()
,
}
}
Operation::ComplexQueryParamGet {
list_of_strings,
} => {
info!("Performing a ComplexQueryParamGet request");
let result = client.complex_query_param_get(
list_of_strings.as_ref(),
).await?;
debug!("Result: {:?}", result);
match result {
ComplexQueryParamGetResponse::Success
=> "Success\n".to_string()
,
}
}
Operation::GetWithBooleanParameter {
iambool,
} => {
info!("Performing a GetWithBooleanParameter request");
let result = client.get_with_boolean_parameter(
iambool,
).await?;
debug!("Result: {:?}", result);
match result {
GetWithBooleanParameterResponse::OK
=> "OK\n".to_string()
,
}
}
Operation::JsonComplexQueryParamGet {
list_of_strings,
} => {
info!("Performing a JsonComplexQueryParamGet request");
let result = client.json_complex_query_param_get(
list_of_strings.as_ref(),
).await?;
debug!("Result: {:?}", result);
match result {
JsonComplexQueryParamGetResponse::Success
=> "Success\n".to_string()
,
}
}
Operation::MandatoryRequestHeaderGet {
x_header,
} => {
info!("Performing a MandatoryRequestHeaderGet request");
let result = client.mandatory_request_header_get(
x_header,
).await?;
debug!("Result: {:?}", result);
match result {
MandatoryRequestHeaderGetResponse::Success
=> "Success\n".to_string()
,
}
}
Operation::MergePatchJsonGet {
} => {
info!("Performing a MergePatchJsonGet request");
let result = client.merge_patch_json_get(
).await?;
debug!("Result: {:?}", result);
match result {
MergePatchJsonGetResponse::Merge
(body)
=> "Merge\n".to_string()
+
&serde_json::to_string_pretty(&body)?,
}
}
Operation::MultigetGet {
} => {
info!("Performing a MultigetGet request");
let result = client.multiget_get(
).await?;
debug!("Result: {:?}", result);
match result {
MultigetGetResponse::JSONRsp
(body)
=> "JSONRsp\n".to_string()
+
&serde_json::to_string_pretty(&body)?,
MultigetGetResponse::XMLRsp
(body)
=> "XMLRsp\n".to_string()
+
&serde_json::to_string_pretty(&body)?,
MultigetGetResponse::OctetRsp
(body)
=> "OctetRsp\n".to_string()
+
&serde_json::to_string_pretty(&body)?,
MultigetGetResponse::StringRsp
(body)
=> "StringRsp\n".to_string()
+
&serde_json::to_string_pretty(&body)?,
MultigetGetResponse::DuplicateResponseLongText
(body)
=> "DuplicateResponseLongText\n".to_string()
+
&serde_json::to_string_pretty(&body)?,
MultigetGetResponse::DuplicateResponseLongText_2
(body)
=> "DuplicateResponseLongText_2\n".to_string()
+
&serde_json::to_string_pretty(&body)?,
MultigetGetResponse::DuplicateResponseLongText_3
(body)
=> "DuplicateResponseLongText_3\n".to_string()
+
&serde_json::to_string_pretty(&body)?,
}
}
Operation::MultipleAuthSchemeGet {
} => {
info!("Performing a MultipleAuthSchemeGet request");
let result = client.multiple_auth_scheme_get(
).await?;
debug!("Result: {:?}", result);
match result {
MultipleAuthSchemeGetResponse::CheckThatLimitingToMultipleRequiredAuthSchemesWorks
=> "CheckThatLimitingToMultipleRequiredAuthSchemesWorks\n".to_string()
,
}
}
Operation::OneOfGet {
} => {
info!("Performing a OneOfGet request");
let result = client.one_of_get(
).await?;
debug!("Result: {:?}", result);
match result {
OneOfGetResponse::Success
(body)
=> "Success\n".to_string()
+
&serde_json::to_string_pretty(&body)?,
}
}
Operation::OverrideServerGet {
} => {
info!("Performing a OverrideServerGet request");
let result = client.override_server_get(
).await?;
debug!("Result: {:?}", result);
match result {
OverrideServerGetResponse::Success
=> "Success\n".to_string()
,
}
}
Operation::ParamgetGet {
uuid,
some_object,
some_list,
} => {
info!("Performing a ParamgetGet request");
let result = client.paramget_get(
uuid,
some_object,
some_list,
).await?;
debug!("Result: {:?}", result);
match result {
ParamgetGetResponse::JSONRsp
(body)
=> "JSONRsp\n".to_string()
+
&serde_json::to_string_pretty(&body)?,
}
}
Operation::ReadonlyAuthSchemeGet {
} => {
info!("Performing a ReadonlyAuthSchemeGet request");
let result = client.readonly_auth_scheme_get(
).await?;
debug!("Result: {:?}", result);
match result {
ReadonlyAuthSchemeGetResponse::CheckThatLimitingToASingleRequiredAuthSchemeWorks
=> "CheckThatLimitingToASingleRequiredAuthSchemeWorks\n".to_string()
,
}
}
Operation::RegisterCallbackPost {
url,
} => {
info!("Performing a RegisterCallbackPost request");
let result = client.register_callback_post(
url,
).await?;
debug!("Result: {:?}", result);
match result {
RegisterCallbackPostResponse::OK
=> "OK\n".to_string()
,
}
}
Operation::RequiredOctetStreamPut {
body,
} => {
info!("Performing a RequiredOctetStreamPut request");
let result = client.required_octet_stream_put(
body,
).await?;
debug!("Result: {:?}", result);
match result {
RequiredOctetStreamPutResponse::OK
=> "OK\n".to_string()
,
}
}
Operation::ResponsesWithHeadersGet {
} => {
info!("Performing a ResponsesWithHeadersGet request");
let result = client.responses_with_headers_get(
).await?;
debug!("Result: {:?}", result);
match result {
ResponsesWithHeadersGetResponse::Success
{
body,
success_info,
bool_header,
object_header,
}
=> "Success\n".to_string()
+
&format!("body: {}\n", serde_json::to_string_pretty(&body)?) +
&format!(
"success_info: {}\n",
serde_json::to_string_pretty(&success_info)?
) +
&format!(
"bool_header: {}\n",
serde_json::to_string_pretty(&bool_header)?
) +
&format!(
"object_header: {}\n",
serde_json::to_string_pretty(&object_header)?
),
ResponsesWithHeadersGetResponse::PreconditionFailed
{
further_info,
failure_info,
}
=> "PreconditionFailed\n".to_string()
+
&format!(
"further_info: {}\n",
serde_json::to_string_pretty(&further_info)?
) +
&format!(
"failure_info: {}\n",
serde_json::to_string_pretty(&failure_info)?
),
}
}
Operation::Rfc7807Get {
} => {
info!("Performing a Rfc7807Get request");
let result = client.rfc7807_get(
).await?;
debug!("Result: {:?}", result);
match result {
Rfc7807GetResponse::OK
(body)
=> "OK\n".to_string()
+
&serde_json::to_string_pretty(&body)?,
Rfc7807GetResponse::NotFound
(body)
=> "NotFound\n".to_string()
+
&serde_json::to_string_pretty(&body)?,
Rfc7807GetResponse::NotAcceptable
(body)
=> "NotAcceptable\n".to_string()
+
&serde_json::to_string_pretty(&body)?,
}
}
Operation::TwoFirstLetterHeaders {
x_header_one,
x_header_two,
} => {
info!("Performing a TwoFirstLetterHeaders request");
let result = client.two_first_letter_headers(
x_header_one,
x_header_two,
).await?;
debug!("Result: {:?}", result);
match result {
TwoFirstLetterHeadersResponse::OK
=> "OK\n".to_string()
,
}
}
Operation::UntypedPropertyGet {
object_untyped_props,
} => {
info!("Performing a UntypedPropertyGet request");
let result = client.untyped_property_get(
object_untyped_props,
).await?;
debug!("Result: {:?}", result);
match result {
UntypedPropertyGetResponse::CheckThatUntypedPropertiesWorks
=> "CheckThatUntypedPropertiesWorks\n".to_string()
,
}
}
Operation::UuidGet {
} => {
info!("Performing a UuidGet request");
let result = client.uuid_get(
).await?;
debug!("Result: {:?}", result);
match result {
UuidGetResponse::DuplicateResponseLongText
(body)
=> "DuplicateResponseLongText\n".to_string()
+
&serde_json::to_string_pretty(&body)?,
}
}
Operation::XmlExtraPost {
duplicate_xml_object,
} => {
info!("Performing a XmlExtraPost request");
let result = client.xml_extra_post(
duplicate_xml_object,
).await?;
debug!("Result: {:?}", result);
match result {
XmlExtraPostResponse::OK
=> "OK\n".to_string()
,
XmlExtraPostResponse::BadRequest
=> "BadRequest\n".to_string()
,
}
}
Operation::XmlOtherPost {
another_xml_object,
} => {
info!("Performing a XmlOtherPost request");
let result = client.xml_other_post(
another_xml_object,
).await?;
debug!("Result: {:?}", result);
match result {
XmlOtherPostResponse::OK
(body)
=> "OK\n".to_string()
+
&serde_json::to_string_pretty(&body)?,
XmlOtherPostResponse::BadRequest
=> "BadRequest\n".to_string()
,
}
}
Operation::XmlOtherPut {
another_xml_array,
} => {
info!("Performing a XmlOtherPut request");
let result = client.xml_other_put(
another_xml_array,
).await?;
debug!("Result: {:?}", result);
match result {
XmlOtherPutResponse::OK
=> "OK\n".to_string()
,
XmlOtherPutResponse::BadRequest
=> "BadRequest\n".to_string()
,
}
}
Operation::XmlPost {
xml_array,
} => {
info!("Performing a XmlPost request");
let result = client.xml_post(
xml_array,
).await?;
debug!("Result: {:?}", result);
match result {
XmlPostResponse::OK
=> "OK\n".to_string()
,
XmlPostResponse::BadRequest
=> "BadRequest\n".to_string()
,
}
}
Operation::XmlPut {
xml_object,
} => {
info!("Performing a XmlPut request");
let result = client.xml_put(
xml_object,
).await?;
debug!("Result: {:?}", result);
match result {
XmlPutResponse::OK
=> "OK\n".to_string()
,
XmlPutResponse::BadRequest
=> "BadRequest\n".to_string()
,
}
}
Operation::EnumInPathPathParamGet {
path_param,
} => {
info!("Performing a EnumInPathPathParamGet request on {:?}", (
&path_param
));
let result = client.enum_in_path_path_param_get(
path_param,
).await?;
debug!("Result: {:?}", result);
match result {
EnumInPathPathParamGetResponse::Success
=> "Success\n".to_string()
,
}
}
Operation::MultiplePathParamsWithVeryLongPathToTestFormattingPathParamAPathParamBGet {
path_param_a,
path_param_b,
} => {
info!("Performing a MultiplePathParamsWithVeryLongPathToTestFormattingPathParamAPathParamBGet request on {:?}", (
&path_param_a,
&path_param_b
));
let result = client.multiple_path_params_with_very_long_path_to_test_formatting_path_param_a_path_param_b_get(
path_param_a,
path_param_b,
).await?;
debug!("Result: {:?}", result);
match result {
MultiplePathParamsWithVeryLongPathToTestFormattingPathParamAPathParamBGetResponse::Success
=> "Success\n".to_string()
,
}
}
Operation::CreateRepo {
object_param,
} => {
info!("Performing a CreateRepo request");
let result = client.create_repo(
object_param,
).await?;
debug!("Result: {:?}", result);
match result {
CreateRepoResponse::Success
=> "Success\n".to_string()
,
}
}
Operation::GetRepoInfo {
repo_id,
} => {
info!("Performing a GetRepoInfo request on {:?}", (
&repo_id
));
let result = client.get_repo_info(
repo_id,
).await?;
debug!("Result: {:?}", result);
match result {
GetRepoInfoResponse::OK
(body)
=> "OK\n".to_string()
+
&serde_json::to_string_pretty(&body)?,
}
}
};
if let Some(output_file) = args.output_file {
std::fs::write(output_file, result)?
} else {
println!("{}", result);
}
Ok(())
}
// May be unused if all inputs are primitive types
#[allow(dead_code)]
fn parse_json<'a, T: serde::de::Deserialize<'a>>(json_string: &'a str) -> Result<T> {
serde_json::from_str(json_string).map_err(|err| anyhow!("Error parsing input: {}", err))
}

View File

@ -7,6 +7,7 @@ Method | HTTP request | Description
****](default_api.md#) | **GET** /any-of |
****](default_api.md#) | **POST** /callback-with-header |
****](default_api.md#) | **GET** /complex-query-param |
**GetWithBooleanParameter**](default_api.md#GetWithBooleanParameter) | **GET** /get-with-bool |
****](default_api.md#) | **GET** /json-complex-query-param |
****](default_api.md#) | **GET** /mandatory-request-header |
****](default_api.md#) | **GET** /merge-patch-json |
@ -20,14 +21,16 @@ Method | HTTP request | Description
****](default_api.md#) | **PUT** /required_octet_stream |
****](default_api.md#) | **GET** /responses_with_headers |
****](default_api.md#) | **GET** /rfc7807 |
**TwoFirstLetterHeaders**](default_api.md#TwoFirstLetterHeaders) | **POST** /operation-two-first-letter-headers |
****](default_api.md#) | **GET** /untyped_property |
****](default_api.md#) | **GET** /uuid |
****](default_api.md#) | **POST** /xml_extra |
****](default_api.md#) | **POST** /xml_other |
****](default_api.md#) | **PUT** /xml_other |
****](default_api.md#) | **POST** /xml | Post an array
****](default_api.md#) | **POST** /xml | Post an array. It's important we test apostrophes, so include one here.
****](default_api.md#) | **PUT** /xml |
****](default_api.md#) | **GET** /enum_in_path/{path_param} |
****](default_api.md#) | **GET** /multiple-path-params-with-very-long-path-to-test-formatting/{path_param_a}/{path_param_b} |
# ****
@ -119,6 +122,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)
# **GetWithBooleanParameter**
> GetWithBooleanParameter(iambool)
Get with a boolean parameter
### Required Parameters
Name | Type | Description | Notes
------------- | ------------- | ------------- | -------------
**iambool** | **bool**| Let's check apostrophes get encoded properly! |
### 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)
# ****
> (optional)
@ -436,6 +466,41 @@ 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)
# **TwoFirstLetterHeaders**
> TwoFirstLetterHeaders(optional)
Check we don't barf if two boolean parameters have the same first letter
### Required Parameters
Name | Type | Description | Notes
------------- | ------------- | ------------- | -------------
**optional** | **map[string]interface{}** | optional parameters | nil if no parameters
### Optional Parameters
Optional parameters are passed through a map[string]interface{}.
Name | Type | Description | Notes
------------- | ------------- | ------------- | -------------
**x_header_one** | **bool**| |
**x_header_two** | **bool**| |
### 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)
# ****
> (optional)
@ -588,7 +653,7 @@ No authorization required
# ****
> (optional)
Post an array
Post an array. It's important we test apostrophes, so include one here.
@ -677,3 +742,29 @@ 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)
# ****
> (path_param_a, path_param_b)
### Required Parameters
Name | Type | Description | Notes
------------- | ------------- | ------------- | -------------
**path_param_a** | **String**| |
**path_param_b** | **String**| |
### 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)

View File

@ -9,6 +9,7 @@ use openapi_v3::{Api, ApiNoContext, Claims, Client, ContextWrapperExt, models,
AnyOfGetResponse,
CallbackWithHeaderPostResponse,
ComplexQueryParamGetResponse,
GetWithBooleanParameterResponse,
JsonComplexQueryParamGetResponse,
MandatoryRequestHeaderGetResponse,
MergePatchJsonGetResponse,
@ -22,6 +23,7 @@ use openapi_v3::{Api, ApiNoContext, Claims, Client, ContextWrapperExt, models,
RequiredOctetStreamPutResponse,
ResponsesWithHeadersGetResponse,
Rfc7807GetResponse,
TwoFirstLetterHeadersResponse,
UntypedPropertyGetResponse,
UuidGetResponse,
XmlExtraPostResponse,
@ -30,6 +32,7 @@ use openapi_v3::{Api, ApiNoContext, Claims, Client, ContextWrapperExt, models,
XmlPostResponse,
XmlPutResponse,
EnumInPathPathParamGetResponse,
MultiplePathParamsWithVeryLongPathToTestFormattingPathParamAPathParamBGetResponse,
CreateRepoResponse,
GetRepoInfoResponse,
};
@ -63,6 +66,7 @@ fn main() {
"AnyOfGet",
"CallbackWithHeaderPost",
"ComplexQueryParamGet",
"GetWithBooleanParameter",
"JsonComplexQueryParamGet",
"MandatoryRequestHeaderGet",
"MergePatchJsonGet",
@ -76,6 +80,7 @@ fn main() {
"RequiredOctetStreamPut",
"ResponsesWithHeadersGet",
"Rfc7807Get",
"TwoFirstLetterHeaders",
"UntypedPropertyGet",
"UuidGet",
"XmlExtraPost",
@ -83,6 +88,7 @@ fn main() {
"XmlOtherPut",
"XmlPost",
"XmlPut",
"MultiplePathParamsWithVeryLongPathToTestFormattingPathParamAPathParamBGet",
"CreateRepo",
"GetRepoInfo",
])
@ -120,6 +126,8 @@ fn main() {
[
"test.read",
"test.write",
"additional.test.read",
"additional.test.write",
].join::<&str>(", ")
},
b"secret").unwrap();
@ -177,6 +185,12 @@ fn main() {
));
info!("{:?} (X-Span-ID: {:?})", result, (client.context() as &dyn Has<XSpanIdString>).get().clone());
},
Some("GetWithBooleanParameter") => {
let result = rt.block_on(client.get_with_boolean_parameter(
true
));
info!("{:?} (X-Span-ID: {:?})", result, (client.context() as &dyn Has<XSpanIdString>).get().clone());
},
Some("JsonComplexQueryParamGet") => {
let result = rt.block_on(client.json_complex_query_param_get(
Some(&Vec::new())
@ -249,6 +263,13 @@ fn main() {
));
info!("{:?} (X-Span-ID: {:?})", result, (client.context() as &dyn Has<XSpanIdString>).get().clone());
},
Some("TwoFirstLetterHeaders") => {
let result = rt.block_on(client.two_first_letter_headers(
Some(true),
Some(true)
));
info!("{:?} (X-Span-ID: {:?})", result, (client.context() as &dyn Has<XSpanIdString>).get().clone());
},
Some("UntypedPropertyGet") => {
let result = rt.block_on(client.untyped_property_get(
None
@ -298,6 +319,13 @@ fn main() {
info!("{:?} (X-Span-ID: {:?})", result, (client.context() as &dyn Has<XSpanIdString>).get().clone());
},
*/
Some("MultiplePathParamsWithVeryLongPathToTestFormattingPathParamAPathParamBGet") => {
let result = rt.block_on(client.multiple_path_params_with_very_long_path_to_test_formatting_path_param_a_path_param_b_get(
"path_param_a_example".to_string(),
"path_param_b_example".to_string()
));
info!("{:?} (X-Span-ID: {:?})", result, (client.context() as &dyn Has<XSpanIdString>).get().clone());
},
Some("CreateRepo") => {
let result = rt.block_on(client.create_repo(
serde_json::from_str::<models::ObjectParam>(r#"{"requiredParam":true}"#).expect("Failed to parse JSON example")

View File

@ -105,6 +105,7 @@ use openapi_v3::{
AnyOfGetResponse,
CallbackWithHeaderPostResponse,
ComplexQueryParamGetResponse,
GetWithBooleanParameterResponse,
JsonComplexQueryParamGetResponse,
MandatoryRequestHeaderGetResponse,
MergePatchJsonGetResponse,
@ -118,6 +119,7 @@ use openapi_v3::{
RequiredOctetStreamPutResponse,
ResponsesWithHeadersGetResponse,
Rfc7807GetResponse,
TwoFirstLetterHeadersResponse,
UntypedPropertyGetResponse,
UuidGetResponse,
XmlExtraPostResponse,
@ -126,6 +128,7 @@ use openapi_v3::{
XmlPostResponse,
XmlPutResponse,
EnumInPathPathParamGetResponse,
MultiplePathParamsWithVeryLongPathToTestFormattingPathParamAPathParamBGetResponse,
CreateRepoResponse,
GetRepoInfoResponse,
};
@ -163,6 +166,15 @@ impl<C> Api<C> for Server<C> where C: Has<XSpanIdString> + Send + Sync
Err(ApiError("Api-Error: Operation is NOT implemented".into()))
}
async fn get_with_boolean_parameter(
&self,
iambool: bool,
context: &C) -> Result<GetWithBooleanParameterResponse, ApiError>
{
info!("get_with_boolean_parameter({}) - X-Span-ID: {:?}", iambool, context.get().0.clone());
Err(ApiError("Api-Error: Operation is NOT implemented".into()))
}
async fn json_complex_query_param_get(
&self,
list_of_strings: Option<&Vec<models::StringObject>>,
@ -276,6 +288,16 @@ impl<C> Api<C> for Server<C> where C: Has<XSpanIdString> + Send + Sync
Err(ApiError("Api-Error: Operation is NOT implemented".into()))
}
async fn two_first_letter_headers(
&self,
x_header_one: Option<bool>,
x_header_two: Option<bool>,
context: &C) -> Result<TwoFirstLetterHeadersResponse, ApiError>
{
info!("two_first_letter_headers({:?}, {:?}) - X-Span-ID: {:?}", x_header_one, x_header_two, context.get().0.clone());
Err(ApiError("Api-Error: Operation is NOT implemented".into()))
}
async fn untyped_property_get(
&self,
object_untyped_props: Option<models::ObjectUntypedProps>,
@ -320,7 +342,7 @@ impl<C> Api<C> for Server<C> where C: Has<XSpanIdString> + Send + Sync
Err(ApiError("Api-Error: Operation is NOT implemented".into()))
}
/// Post an array
/// Post an array. It's important we test apostrophes, so include one here.
async fn xml_post(
&self,
xml_array: Option<models::XmlArray>,
@ -348,6 +370,16 @@ impl<C> Api<C> for Server<C> where C: Has<XSpanIdString> + Send + Sync
Err(ApiError("Api-Error: Operation is NOT implemented".into()))
}
async fn multiple_path_params_with_very_long_path_to_test_formatting_path_param_a_path_param_b_get(
&self,
path_param_a: String,
path_param_b: String,
context: &C) -> Result<MultiplePathParamsWithVeryLongPathToTestFormattingPathParamAPathParamBGetResponse, ApiError>
{
info!("multiple_path_params_with_very_long_path_to_test_formatting_path_param_a_path_param_b_get(\"{}\", \"{}\") - X-Span-ID: {:?}", path_param_a, path_param_b, context.get().0.clone());
Err(ApiError("Api-Error: Operation is NOT implemented".into()))
}
async fn create_repo(
&self,
object_param: models::ObjectParam,

View File

@ -27,6 +27,8 @@ fn full_permission_claim() -> Claims {
[
"test.read",
"test.write",
"additional.test.read",
"additional.test.write",
].join::<&str>(", ")
}
}

View File

@ -39,6 +39,7 @@ use crate::{Api,
AnyOfGetResponse,
CallbackWithHeaderPostResponse,
ComplexQueryParamGetResponse,
GetWithBooleanParameterResponse,
JsonComplexQueryParamGetResponse,
MandatoryRequestHeaderGetResponse,
MergePatchJsonGetResponse,
@ -52,6 +53,7 @@ use crate::{Api,
RequiredOctetStreamPutResponse,
ResponsesWithHeadersGetResponse,
Rfc7807GetResponse,
TwoFirstLetterHeadersResponse,
UntypedPropertyGetResponse,
UuidGetResponse,
XmlExtraPostResponse,
@ -60,6 +62,7 @@ use crate::{Api,
XmlPostResponse,
XmlPutResponse,
EnumInPathPathParamGetResponse,
MultiplePathParamsWithVeryLongPathToTestFormattingPathParamAPathParamBGetResponse,
CreateRepoResponse,
GetRepoInfoResponse
};
@ -667,6 +670,77 @@ impl<S, C> Api<C> for Client<S, C> where
}
}
async fn get_with_boolean_parameter(
&self,
param_iambool: bool,
context: &C) -> Result<GetWithBooleanParameterResponse, ApiError>
{
let mut client_service = self.client_service.clone();
let mut uri = format!(
"{}/get-with-bool",
self.base_path
);
// Query parameters
let query_string = {
let mut query_string = form_urlencoded::Serializer::new("".to_owned());
query_string.append_pair("iambool",
&param_iambool.to_string());
query_string.finish()
};
if !query_string.is_empty() {
uri += "?";
uri += &query_string;
}
let uri = match Uri::from_str(&uri) {
Ok(uri) => uri,
Err(err) => return Err(ApiError(format!("Unable to build URI: {}", err))),
};
let mut request = match Request::builder()
.method("GET")
.uri(uri)
.body(Body::empty()) {
Ok(req) => req,
Err(e) => return Err(ApiError(format!("Unable to create request: {}", e)))
};
let header = HeaderValue::from_str(Has::<XSpanIdString>::get(context).0.as_str());
request.headers_mut().insert(HeaderName::from_static("x-span-id"), match header {
Ok(h) => h,
Err(e) => return Err(ApiError(format!("Unable to create X-Span ID header value: {}", e)))
});
let response = client_service.call((request, context.clone()))
.map_err(|e| ApiError(format!("No response received: {}", e))).await?;
match response.status().as_u16() {
200 => {
Ok(
GetWithBooleanParameterResponse::OK
)
}
code => {
let headers = response.headers().clone();
let body = response.into_body()
.take(100)
.into_raw().await;
Err(ApiError(format!("Unexpected response code {}:\n{:?}\n\n{}",
code,
headers,
match body {
Ok(body) => match String::from_utf8(body) {
Ok(body) => body,
Err(e) => format!("<Body was not UTF8: {:?}>", e),
},
Err(e) => format!("<Failed to read body: {}>", e),
}
)))
}
}
}
async fn json_complex_query_param_get(
&self,
param_list_of_strings: Option<&Vec<models::StringObject>>,
@ -1918,6 +1992,111 @@ impl<S, C> Api<C> for Client<S, C> where
}
}
async fn two_first_letter_headers(
&self,
param_x_header_one: Option<bool>,
param_x_header_two: Option<bool>,
context: &C) -> Result<TwoFirstLetterHeadersResponse, ApiError>
{
let mut client_service = self.client_service.clone();
let mut uri = format!(
"{}/operation-two-first-letter-headers",
self.base_path
);
// Query parameters
let query_string = {
let mut query_string = form_urlencoded::Serializer::new("".to_owned());
query_string.finish()
};
if !query_string.is_empty() {
uri += "?";
uri += &query_string;
}
let uri = match Uri::from_str(&uri) {
Ok(uri) => uri,
Err(err) => return Err(ApiError(format!("Unable to build URI: {}", err))),
};
let mut request = match Request::builder()
.method("POST")
.uri(uri)
.body(Body::empty()) {
Ok(req) => req,
Err(e) => return Err(ApiError(format!("Unable to create request: {}", e)))
};
let header = HeaderValue::from_str(Has::<XSpanIdString>::get(context).0.as_str());
request.headers_mut().insert(HeaderName::from_static("x-span-id"), match header {
Ok(h) => h,
Err(e) => return Err(ApiError(format!("Unable to create X-Span ID header value: {}", e)))
});
// Header parameters
#[allow(clippy::single_match)]
match param_x_header_one {
Some(param_x_header_one) => {
request.headers_mut().append(
HeaderName::from_static("x-header-one"),
#[allow(clippy::redundant_clone)]
match header::IntoHeaderValue(param_x_header_one.clone()).try_into() {
Ok(header) => header,
Err(e) => {
return Err(ApiError(format!(
"Invalid header x_header_one - {}", e)));
},
});
},
None => {}
}
#[allow(clippy::single_match)]
match param_x_header_two {
Some(param_x_header_two) => {
request.headers_mut().append(
HeaderName::from_static("x-header-two"),
#[allow(clippy::redundant_clone)]
match header::IntoHeaderValue(param_x_header_two.clone()).try_into() {
Ok(header) => header,
Err(e) => {
return Err(ApiError(format!(
"Invalid header x_header_two - {}", e)));
},
});
},
None => {}
}
let response = client_service.call((request, context.clone()))
.map_err(|e| ApiError(format!("No response received: {}", e))).await?;
match response.status().as_u16() {
200 => {
Ok(
TwoFirstLetterHeadersResponse::OK
)
}
code => {
let headers = response.headers().clone();
let body = response.into_body()
.take(100)
.into_raw().await;
Err(ApiError(format!("Unexpected response code {}:\n{:?}\n\n{}",
code,
headers,
match body {
Ok(body) => match String::from_utf8(body) {
Ok(body) => body,
Err(e) => format!("<Body was not UTF8: {:?}>", e),
},
Err(e) => format!("<Failed to read body: {}>", e),
}
)))
}
}
}
async fn untyped_property_get(
&self,
param_object_untyped_props: Option<models::ObjectUntypedProps>,
@ -2597,6 +2776,78 @@ impl<S, C> Api<C> for Client<S, C> where
}
}
async fn multiple_path_params_with_very_long_path_to_test_formatting_path_param_a_path_param_b_get(
&self,
param_path_param_a: String,
param_path_param_b: String,
context: &C) -> Result<MultiplePathParamsWithVeryLongPathToTestFormattingPathParamAPathParamBGetResponse, ApiError>
{
let mut client_service = self.client_service.clone();
let mut uri = format!(
"{}/multiple-path-params-with-very-long-path-to-test-formatting/{path_param_a}/{path_param_b}",
self.base_path
,path_param_a=utf8_percent_encode(&param_path_param_a.to_string(), ID_ENCODE_SET)
,path_param_b=utf8_percent_encode(&param_path_param_b.to_string(), ID_ENCODE_SET)
);
// Query parameters
let query_string = {
let mut query_string = form_urlencoded::Serializer::new("".to_owned());
query_string.finish()
};
if !query_string.is_empty() {
uri += "?";
uri += &query_string;
}
let uri = match Uri::from_str(&uri) {
Ok(uri) => uri,
Err(err) => return Err(ApiError(format!("Unable to build URI: {}", err))),
};
let mut request = match Request::builder()
.method("GET")
.uri(uri)
.body(Body::empty()) {
Ok(req) => req,
Err(e) => return Err(ApiError(format!("Unable to create request: {}", e)))
};
let header = HeaderValue::from_str(Has::<XSpanIdString>::get(context).0.as_str());
request.headers_mut().insert(HeaderName::from_static("x-span-id"), match header {
Ok(h) => h,
Err(e) => return Err(ApiError(format!("Unable to create X-Span ID header value: {}", e)))
});
let response = client_service.call((request, context.clone()))
.map_err(|e| ApiError(format!("No response received: {}", e))).await?;
match response.status().as_u16() {
200 => {
Ok(
MultiplePathParamsWithVeryLongPathToTestFormattingPathParamAPathParamBGetResponse::Success
)
}
code => {
let headers = response.headers().clone();
let body = response.into_body()
.take(100)
.into_raw().await;
Err(ApiError(format!("Unexpected response code {}:\n{:?}\n\n{}",
code,
headers,
match body {
Ok(body) => match String::from_utf8(body) {
Ok(body) => body,
Err(e) => format!("<Body was not UTF8: {:?}>", e),
},
Err(e) => format!("<Failed to read body: {}>", e),
}
)))
}
}
}
async fn create_repo(
&self,
param_object_param: models::ObjectParam,

View File

@ -105,6 +105,25 @@ impl<T, A, B, C, D, ReqBody> Service<Request<ReqBody>> for AddContext<T, A, B, C
let context = A::default().push(XSpanIdString::get_or_generate(&request));
let headers = request.headers();
{
use swagger::auth::Bearer;
use std::ops::Deref;
if let Some(bearer) = swagger::auth::from_headers::<Bearer>(headers) {
let authorization = self.inner.bearer_authorization(&bearer);
let auth_data = AuthData::Bearer(bearer);
let context = context.push(Some(auth_data));
let context = match authorization {
Ok(auth) => context.push(Some(auth)),
Err(err) => {
error!("Error during Authorization: {err:?}");
context.push(None::<Authorization>)
}
};
return self.inner.call((request, context))
}
}
{
use swagger::auth::Bearer;
use std::ops::Deref;

View File

@ -48,6 +48,12 @@ pub enum ComplexQueryParamGetResponse {
Success
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum GetWithBooleanParameterResponse {
/// OK
OK
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum JsonComplexQueryParamGetResponse {
/// Success
@ -195,6 +201,12 @@ pub enum Rfc7807GetResponse {
(models::ObjectWithArrayOfObjects)
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum TwoFirstLetterHeadersResponse {
/// OK
OK
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum UntypedPropertyGetResponse {
/// Check that untyped properties works
@ -265,6 +277,12 @@ pub enum EnumInPathPathParamGetResponse {
Success
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum MultiplePathParamsWithVeryLongPathToTestFormattingPathParamAPathParamBGetResponse {
/// Success
Success
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum CreateRepoResponse {
/// Success
@ -301,6 +319,11 @@ pub trait Api<C: Send + Sync> {
list_of_strings: Option<&Vec<models::StringObject>>,
context: &C) -> Result<ComplexQueryParamGetResponse, ApiError>;
async fn get_with_boolean_parameter(
&self,
iambool: bool,
context: &C) -> Result<GetWithBooleanParameterResponse, ApiError>;
async fn json_complex_query_param_get(
&self,
list_of_strings: Option<&Vec<models::StringObject>>,
@ -362,6 +385,12 @@ pub trait Api<C: Send + Sync> {
&self,
context: &C) -> Result<Rfc7807GetResponse, ApiError>;
async fn two_first_letter_headers(
&self,
x_header_one: Option<bool>,
x_header_two: Option<bool>,
context: &C) -> Result<TwoFirstLetterHeadersResponse, ApiError>;
async fn untyped_property_get(
&self,
object_untyped_props: Option<models::ObjectUntypedProps>,
@ -386,7 +415,7 @@ pub trait Api<C: Send + Sync> {
another_xml_array: Option<models::AnotherXmlArray>,
context: &C) -> Result<XmlOtherPutResponse, ApiError>;
/// Post an array
/// Post an array. It's important we test apostrophes, so include one here.
async fn xml_post(
&self,
xml_array: Option<models::XmlArray>,
@ -402,6 +431,12 @@ pub trait Api<C: Send + Sync> {
path_param: models::StringEnum,
context: &C) -> Result<EnumInPathPathParamGetResponse, ApiError>;
async fn multiple_path_params_with_very_long_path_to_test_formatting_path_param_a_path_param_b_get(
&self,
path_param_a: String,
path_param_b: String,
context: &C) -> Result<MultiplePathParamsWithVeryLongPathToTestFormattingPathParamAPathParamBGetResponse, ApiError>;
async fn create_repo(
&self,
object_param: models::ObjectParam,
@ -438,6 +473,11 @@ pub trait ApiNoContext<C: Send + Sync> {
list_of_strings: Option<&Vec<models::StringObject>>,
) -> Result<ComplexQueryParamGetResponse, ApiError>;
async fn get_with_boolean_parameter(
&self,
iambool: bool,
) -> Result<GetWithBooleanParameterResponse, ApiError>;
async fn json_complex_query_param_get(
&self,
list_of_strings: Option<&Vec<models::StringObject>>,
@ -499,6 +539,12 @@ pub trait ApiNoContext<C: Send + Sync> {
&self,
) -> Result<Rfc7807GetResponse, ApiError>;
async fn two_first_letter_headers(
&self,
x_header_one: Option<bool>,
x_header_two: Option<bool>,
) -> Result<TwoFirstLetterHeadersResponse, ApiError>;
async fn untyped_property_get(
&self,
object_untyped_props: Option<models::ObjectUntypedProps>,
@ -523,7 +569,7 @@ pub trait ApiNoContext<C: Send + Sync> {
another_xml_array: Option<models::AnotherXmlArray>,
) -> Result<XmlOtherPutResponse, ApiError>;
/// Post an array
/// Post an array. It's important we test apostrophes, so include one here.
async fn xml_post(
&self,
xml_array: Option<models::XmlArray>,
@ -539,6 +585,12 @@ pub trait ApiNoContext<C: Send + Sync> {
path_param: models::StringEnum,
) -> Result<EnumInPathPathParamGetResponse, ApiError>;
async fn multiple_path_params_with_very_long_path_to_test_formatting_path_param_a_path_param_b_get(
&self,
path_param_a: String,
path_param_b: String,
) -> Result<MultiplePathParamsWithVeryLongPathToTestFormattingPathParamAPathParamBGetResponse, ApiError>;
async fn create_repo(
&self,
object_param: models::ObjectParam,
@ -601,6 +653,15 @@ impl<T: Api<C> + Send + Sync, C: Clone + Send + Sync> ApiNoContext<C> for Contex
self.api().complex_query_param_get(list_of_strings, &context).await
}
async fn get_with_boolean_parameter(
&self,
iambool: bool,
) -> Result<GetWithBooleanParameterResponse, ApiError>
{
let context = self.context().clone();
self.api().get_with_boolean_parameter(iambool, &context).await
}
async fn json_complex_query_param_get(
&self,
list_of_strings: Option<&Vec<models::StringObject>>,
@ -714,6 +775,16 @@ impl<T: Api<C> + Send + Sync, C: Clone + Send + Sync> ApiNoContext<C> for Contex
self.api().rfc7807_get(&context).await
}
async fn two_first_letter_headers(
&self,
x_header_one: Option<bool>,
x_header_two: Option<bool>,
) -> Result<TwoFirstLetterHeadersResponse, ApiError>
{
let context = self.context().clone();
self.api().two_first_letter_headers(x_header_one, x_header_two, &context).await
}
async fn untyped_property_get(
&self,
object_untyped_props: Option<models::ObjectUntypedProps>,
@ -758,7 +829,7 @@ impl<T: Api<C> + Send + Sync, C: Clone + Send + Sync> ApiNoContext<C> for Contex
self.api().xml_other_put(another_xml_array, &context).await
}
/// Post an array
/// Post an array. It's important we test apostrophes, so include one here.
async fn xml_post(
&self,
xml_array: Option<models::XmlArray>,
@ -786,6 +857,16 @@ impl<T: Api<C> + Send + Sync, C: Clone + Send + Sync> ApiNoContext<C> for Contex
self.api().enum_in_path_path_param_get(path_param, &context).await
}
async fn multiple_path_params_with_very_long_path_to_test_formatting_path_param_a_path_param_b_get(
&self,
path_param_a: String,
path_param_b: String,
) -> Result<MultiplePathParamsWithVeryLongPathToTestFormattingPathParamAPathParamBGetResponse, ApiError>
{
let context = self.context().clone();
self.api().multiple_path_params_with_very_long_path_to_test_formatting_path_param_a_path_param_b_get(path_param_a, path_param_b, &context).await
}
async fn create_repo(
&self,
object_param: models::ObjectParam,

View File

@ -24,6 +24,7 @@ use crate::{Api,
AnyOfGetResponse,
CallbackWithHeaderPostResponse,
ComplexQueryParamGetResponse,
GetWithBooleanParameterResponse,
JsonComplexQueryParamGetResponse,
MandatoryRequestHeaderGetResponse,
MergePatchJsonGetResponse,
@ -37,6 +38,7 @@ use crate::{Api,
RequiredOctetStreamPutResponse,
ResponsesWithHeadersGetResponse,
Rfc7807GetResponse,
TwoFirstLetterHeadersResponse,
UntypedPropertyGetResponse,
UuidGetResponse,
XmlExtraPostResponse,
@ -45,6 +47,7 @@ use crate::{Api,
XmlPostResponse,
XmlPutResponse,
EnumInPathPathParamGetResponse,
MultiplePathParamsWithVeryLongPathToTestFormattingPathParamAPathParamBGetResponse,
CreateRepoResponse,
GetRepoInfoResponse
};
@ -62,12 +65,15 @@ mod paths {
r"^/callback-with-header$",
r"^/complex-query-param$",
r"^/enum_in_path/(?P<path_param>[^/?#]*)$",
r"^/get-with-bool$",
r"^/json-complex-query-param$",
r"^/mandatory-request-header$",
r"^/merge-patch-json$",
r"^/multiget$",
r"^/multiple-path-params-with-very-long-path-to-test-formatting/(?P<path_param_a>[^/?#]*)/(?P<path_param_b>[^/?#]*)$",
r"^/multiple_auth_scheme$",
r"^/one-of$",
r"^/operation-two-first-letter-headers$",
r"^/override-server$",
r"^/paramget$",
r"^/readonly_auth_scheme$",
@ -95,32 +101,41 @@ mod paths {
regex::Regex::new(r"^/enum_in_path/(?P<path_param>[^/?#]*)$")
.expect("Unable to create regex for ENUM_IN_PATH_PATH_PARAM");
}
pub(crate) static ID_JSON_COMPLEX_QUERY_PARAM: usize = 4;
pub(crate) static ID_MANDATORY_REQUEST_HEADER: usize = 5;
pub(crate) static ID_MERGE_PATCH_JSON: usize = 6;
pub(crate) static ID_MULTIGET: usize = 7;
pub(crate) static ID_MULTIPLE_AUTH_SCHEME: usize = 8;
pub(crate) static ID_ONE_OF: usize = 9;
pub(crate) static ID_OVERRIDE_SERVER: usize = 10;
pub(crate) static ID_PARAMGET: usize = 11;
pub(crate) static ID_READONLY_AUTH_SCHEME: usize = 12;
pub(crate) static ID_REGISTER_CALLBACK: usize = 13;
pub(crate) static ID_REPOS: usize = 14;
pub(crate) static ID_REPOS_REPOID: usize = 15;
pub(crate) static ID_GET_WITH_BOOL: usize = 4;
pub(crate) static ID_JSON_COMPLEX_QUERY_PARAM: usize = 5;
pub(crate) static ID_MANDATORY_REQUEST_HEADER: usize = 6;
pub(crate) static ID_MERGE_PATCH_JSON: usize = 7;
pub(crate) static ID_MULTIGET: usize = 8;
pub(crate) static ID_MULTIPLE_PATH_PARAMS_WITH_VERY_LONG_PATH_TO_TEST_FORMATTING_PATH_PARAM_A_PATH_PARAM_B: usize = 9;
lazy_static! {
pub static ref REGEX_MULTIPLE_PATH_PARAMS_WITH_VERY_LONG_PATH_TO_TEST_FORMATTING_PATH_PARAM_A_PATH_PARAM_B: regex::Regex =
#[allow(clippy::invalid_regex)]
regex::Regex::new(r"^/multiple-path-params-with-very-long-path-to-test-formatting/(?P<path_param_a>[^/?#]*)/(?P<path_param_b>[^/?#]*)$")
.expect("Unable to create regex for MULTIPLE_PATH_PARAMS_WITH_VERY_LONG_PATH_TO_TEST_FORMATTING_PATH_PARAM_A_PATH_PARAM_B");
}
pub(crate) static ID_MULTIPLE_AUTH_SCHEME: usize = 10;
pub(crate) static ID_ONE_OF: usize = 11;
pub(crate) static ID_OPERATION_TWO_FIRST_LETTER_HEADERS: usize = 12;
pub(crate) static ID_OVERRIDE_SERVER: usize = 13;
pub(crate) static ID_PARAMGET: usize = 14;
pub(crate) static ID_READONLY_AUTH_SCHEME: usize = 15;
pub(crate) static ID_REGISTER_CALLBACK: usize = 16;
pub(crate) static ID_REPOS: usize = 17;
pub(crate) static ID_REPOS_REPOID: usize = 18;
lazy_static! {
pub static ref REGEX_REPOS_REPOID: regex::Regex =
#[allow(clippy::invalid_regex)]
regex::Regex::new(r"^/repos/(?P<repoId>[^/?#]*)$")
.expect("Unable to create regex for REPOS_REPOID");
}
pub(crate) static ID_REQUIRED_OCTET_STREAM: usize = 16;
pub(crate) static ID_RESPONSES_WITH_HEADERS: usize = 17;
pub(crate) static ID_RFC7807: usize = 18;
pub(crate) static ID_UNTYPED_PROPERTY: usize = 19;
pub(crate) static ID_UUID: usize = 20;
pub(crate) static ID_XML: usize = 21;
pub(crate) static ID_XML_EXTRA: usize = 22;
pub(crate) static ID_XML_OTHER: usize = 23;
pub(crate) static ID_REQUIRED_OCTET_STREAM: usize = 19;
pub(crate) static ID_RESPONSES_WITH_HEADERS: usize = 20;
pub(crate) static ID_RFC7807: usize = 21;
pub(crate) static ID_UNTYPED_PROPERTY: usize = 22;
pub(crate) static ID_UUID: usize = 23;
pub(crate) static ID_XML: usize = 24;
pub(crate) static ID_XML_EXTRA: usize = 25;
pub(crate) static ID_XML_OTHER: usize = 26;
}
@ -406,6 +421,64 @@ impl<T, C> hyper::service::Service<(Request<Body>, C)> for Service<T, C> where
Ok(response)
},
// GetWithBooleanParameter - GET /get-with-bool
hyper::Method::GET if path.matched(paths::ID_GET_WITH_BOOL) => {
// Query parameters (note that non-required or collection query parameters will ignore garbage values, rather than causing a 400 response)
let query_params = form_urlencoded::parse(uri.query().unwrap_or_default().as_bytes()).collect::<Vec<_>>();
let param_iambool = query_params.iter().filter(|e| e.0 == "iambool").map(|e| e.1.clone())
.next();
let param_iambool = match param_iambool {
Some(param_iambool) => {
let param_iambool =
<bool as std::str::FromStr>::from_str
(&param_iambool);
match param_iambool {
Ok(param_iambool) => Some(param_iambool),
Err(e) => return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST)
.body(Body::from(format!("Couldn't parse query parameter iambool - doesn't match schema: {}", e)))
.expect("Unable to create Bad Request response for invalid query parameter iambool")),
}
},
None => None,
};
let param_iambool = match param_iambool {
Some(param_iambool) => param_iambool,
None => return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST)
.body(Body::from("Missing required query parameter iambool"))
.expect("Unable to create Bad Request response for missing query parameter iambool")),
};
let result = api_impl.get_with_boolean_parameter(
param_iambool,
&context
).await;
let mut response = Response::new(Body::empty());
response.headers_mut().insert(
HeaderName::from_static("x-span-id"),
HeaderValue::from_str((&context as &dyn Has<XSpanIdString>).get().0.clone().as_str())
.expect("Unable to create X-Span-ID header value"));
match result {
Ok(rsp) => match rsp {
GetWithBooleanParameterResponse::OK
=> {
*response.status_mut() = StatusCode::from_u16(200).expect("Unable to turn 200 into a StatusCode");
},
},
Err(_) => {
// Application code returned an error. This should not happen, as the implementation should
// return a valid response.
*response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR;
*response.body_mut() = Body::from("An internal error occurred");
},
}
Ok(response)
},
// JsonComplexQueryParamGet - GET /json-complex-query-param
hyper::Method::GET if path.matched(paths::ID_JSON_COMPLEX_QUERY_PARAM) => {
// Query parameters (note that non-required or collection query parameters will ignore garbage values, rather than causing a 400 response)
@ -1261,6 +1334,76 @@ impl<T, C> hyper::service::Service<(Request<Body>, C)> for Service<T, C> where
Ok(response)
},
// TwoFirstLetterHeaders - POST /operation-two-first-letter-headers
hyper::Method::POST if path.matched(paths::ID_OPERATION_TWO_FIRST_LETTER_HEADERS) => {
// Header parameters
let param_x_header_one = headers.get(HeaderName::from_static("x-header-one"));
let param_x_header_one = match param_x_header_one {
Some(v) => match header::IntoHeaderValue::<bool>::try_from((*v).clone()) {
Ok(result) =>
Some(result.0),
Err(err) => {
return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST)
.body(Body::from(format!("Invalid header x-header-one - {}", err)))
.expect("Unable to create Bad Request response for invalid header x-header-one"));
},
},
None => {
None
}
};
let param_x_header_two = headers.get(HeaderName::from_static("x-header-two"));
let param_x_header_two = match param_x_header_two {
Some(v) => match header::IntoHeaderValue::<bool>::try_from((*v).clone()) {
Ok(result) =>
Some(result.0),
Err(err) => {
return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST)
.body(Body::from(format!("Invalid header x-header-two - {}", err)))
.expect("Unable to create Bad Request response for invalid header x-header-two"));
},
},
None => {
None
}
};
let result = api_impl.two_first_letter_headers(
param_x_header_one,
param_x_header_two,
&context
).await;
let mut response = Response::new(Body::empty());
response.headers_mut().insert(
HeaderName::from_static("x-span-id"),
HeaderValue::from_str((&context as &dyn Has<XSpanIdString>).get().0.clone().as_str())
.expect("Unable to create X-Span-ID header value"));
match result {
Ok(rsp) => match rsp {
TwoFirstLetterHeadersResponse::OK
=> {
*response.status_mut() = StatusCode::from_u16(200).expect("Unable to turn 200 into a StatusCode");
},
},
Err(_) => {
// Application code returned an error. This should not happen, as the implementation should
// return a valid response.
*response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR;
*response.body_mut() = Body::from("An internal error occurred");
},
}
Ok(response)
},
// UntypedPropertyGet - GET /untyped_property
hyper::Method::GET if path.matched(paths::ID_UNTYPED_PROPERTY) => {
// Handle body parameters (note that non-required body parameters will ignore garbage
@ -1774,6 +1917,75 @@ impl<T, C> hyper::service::Service<(Request<Body>, C)> for Service<T, C> where
Ok(response)
},
// MultiplePathParamsWithVeryLongPathToTestFormattingPathParamAPathParamBGet - GET /multiple-path-params-with-very-long-path-to-test-formatting/{path_param_a}/{path_param_b}
hyper::Method::GET if path.matched(paths::ID_MULTIPLE_PATH_PARAMS_WITH_VERY_LONG_PATH_TO_TEST_FORMATTING_PATH_PARAM_A_PATH_PARAM_B) => {
// Path parameters
let path: &str = uri.path();
let path_params =
paths::REGEX_MULTIPLE_PATH_PARAMS_WITH_VERY_LONG_PATH_TO_TEST_FORMATTING_PATH_PARAM_A_PATH_PARAM_B
.captures(path)
.unwrap_or_else(||
panic!("Path {} matched RE MULTIPLE_PATH_PARAMS_WITH_VERY_LONG_PATH_TO_TEST_FORMATTING_PATH_PARAM_A_PATH_PARAM_B in set but failed match against \"{}\"", path, paths::REGEX_MULTIPLE_PATH_PARAMS_WITH_VERY_LONG_PATH_TO_TEST_FORMATTING_PATH_PARAM_A_PATH_PARAM_B.as_str())
);
let param_path_param_a = match percent_encoding::percent_decode(path_params["path_param_a"].as_bytes()).decode_utf8() {
Ok(param_path_param_a) => match param_path_param_a.parse::<String>() {
Ok(param_path_param_a) => param_path_param_a,
Err(e) => return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST)
.body(Body::from(format!("Couldn't parse path parameter path_param_a: {}", e)))
.expect("Unable to create Bad Request response for invalid path parameter")),
},
Err(_) => return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST)
.body(Body::from(format!("Couldn't percent-decode path parameter as UTF-8: {}", &path_params["path_param_a"])))
.expect("Unable to create Bad Request response for invalid percent decode"))
};
let param_path_param_b = match percent_encoding::percent_decode(path_params["path_param_b"].as_bytes()).decode_utf8() {
Ok(param_path_param_b) => match param_path_param_b.parse::<String>() {
Ok(param_path_param_b) => param_path_param_b,
Err(e) => return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST)
.body(Body::from(format!("Couldn't parse path parameter path_param_b: {}", e)))
.expect("Unable to create Bad Request response for invalid path parameter")),
},
Err(_) => return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST)
.body(Body::from(format!("Couldn't percent-decode path parameter as UTF-8: {}", &path_params["path_param_b"])))
.expect("Unable to create Bad Request response for invalid percent decode"))
};
let result = api_impl.multiple_path_params_with_very_long_path_to_test_formatting_path_param_a_path_param_b_get(
param_path_param_a,
param_path_param_b,
&context
).await;
let mut response = Response::new(Body::empty());
response.headers_mut().insert(
HeaderName::from_static("x-span-id"),
HeaderValue::from_str((&context as &dyn Has<XSpanIdString>).get().0.clone().as_str())
.expect("Unable to create X-Span-ID header value"));
match result {
Ok(rsp) => match rsp {
MultiplePathParamsWithVeryLongPathToTestFormattingPathParamAPathParamBGetResponse::Success
=> {
*response.status_mut() = StatusCode::from_u16(200).expect("Unable to turn 200 into a StatusCode");
},
},
Err(_) => {
// Application code returned an error. This should not happen, as the implementation should
// return a valid response.
*response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR;
*response.body_mut() = Body::from("An internal error occurred");
},
}
Ok(response)
},
// CreateRepo - POST /repos
hyper::Method::POST if path.matched(paths::ID_REPOS) => {
// Handle body parameters (note that non-required body parameters will ignore garbage
@ -1914,12 +2126,15 @@ impl<T, C> hyper::service::Service<(Request<Body>, C)> for Service<T, C> where
_ if path.matched(paths::ID_CALLBACK_WITH_HEADER) => method_not_allowed(),
_ if path.matched(paths::ID_COMPLEX_QUERY_PARAM) => method_not_allowed(),
_ if path.matched(paths::ID_ENUM_IN_PATH_PATH_PARAM) => method_not_allowed(),
_ if path.matched(paths::ID_GET_WITH_BOOL) => method_not_allowed(),
_ if path.matched(paths::ID_JSON_COMPLEX_QUERY_PARAM) => method_not_allowed(),
_ if path.matched(paths::ID_MANDATORY_REQUEST_HEADER) => method_not_allowed(),
_ if path.matched(paths::ID_MERGE_PATCH_JSON) => method_not_allowed(),
_ if path.matched(paths::ID_MULTIGET) => method_not_allowed(),
_ if path.matched(paths::ID_MULTIPLE_PATH_PARAMS_WITH_VERY_LONG_PATH_TO_TEST_FORMATTING_PATH_PARAM_A_PATH_PARAM_B) => method_not_allowed(),
_ if path.matched(paths::ID_MULTIPLE_AUTH_SCHEME) => method_not_allowed(),
_ if path.matched(paths::ID_ONE_OF) => method_not_allowed(),
_ if path.matched(paths::ID_OPERATION_TWO_FIRST_LETTER_HEADERS) => method_not_allowed(),
_ if path.matched(paths::ID_OVERRIDE_SERVER) => method_not_allowed(),
_ if path.matched(paths::ID_PARAMGET) => method_not_allowed(),
_ if path.matched(paths::ID_READONLY_AUTH_SCHEME) => method_not_allowed(),
@ -1958,6 +2173,8 @@ impl<T> RequestParser<T> for ApiRequestParser {
hyper::Method::POST if path.matched(paths::ID_CALLBACK_WITH_HEADER) => Some("CallbackWithHeaderPost"),
// ComplexQueryParamGet - GET /complex-query-param
hyper::Method::GET if path.matched(paths::ID_COMPLEX_QUERY_PARAM) => Some("ComplexQueryParamGet"),
// GetWithBooleanParameter - GET /get-with-bool
hyper::Method::GET if path.matched(paths::ID_GET_WITH_BOOL) => Some("GetWithBooleanParameter"),
// JsonComplexQueryParamGet - GET /json-complex-query-param
hyper::Method::GET if path.matched(paths::ID_JSON_COMPLEX_QUERY_PARAM) => Some("JsonComplexQueryParamGet"),
// MandatoryRequestHeaderGet - GET /mandatory-request-header
@ -1984,6 +2201,8 @@ impl<T> RequestParser<T> for ApiRequestParser {
hyper::Method::GET if path.matched(paths::ID_RESPONSES_WITH_HEADERS) => Some("ResponsesWithHeadersGet"),
// Rfc7807Get - GET /rfc7807
hyper::Method::GET if path.matched(paths::ID_RFC7807) => Some("Rfc7807Get"),
// TwoFirstLetterHeaders - POST /operation-two-first-letter-headers
hyper::Method::POST if path.matched(paths::ID_OPERATION_TWO_FIRST_LETTER_HEADERS) => Some("TwoFirstLetterHeaders"),
// UntypedPropertyGet - GET /untyped_property
hyper::Method::GET if path.matched(paths::ID_UNTYPED_PROPERTY) => Some("UntypedPropertyGet"),
// UuidGet - GET /uuid
@ -2000,6 +2219,8 @@ impl<T> RequestParser<T> for ApiRequestParser {
hyper::Method::PUT if path.matched(paths::ID_XML) => Some("XmlPut"),
// EnumInPathPathParamGet - GET /enum_in_path/{path_param}
hyper::Method::GET if path.matched(paths::ID_ENUM_IN_PATH_PATH_PARAM) => Some("EnumInPathPathParamGet"),
// MultiplePathParamsWithVeryLongPathToTestFormattingPathParamAPathParamBGet - GET /multiple-path-params-with-very-long-path-to-test-formatting/{path_param_a}/{path_param_b}
hyper::Method::GET if path.matched(paths::ID_MULTIPLE_PATH_PARAMS_WITH_VERY_LONG_PATH_TO_TEST_FORMATTING_PATH_PARAM_A_PATH_PARAM_B) => Some("MultiplePathParamsWithVeryLongPathToTestFormattingPathParamAPathParamBGet"),
// CreateRepo - POST /repos
hyper::Method::POST if path.matched(paths::ID_REPOS) => Some("CreateRepo"),
// GetRepoInfo - GET /repos/{repoId}

View File

@ -3,6 +3,7 @@
Cargo.toml
README.md
api/openapi.yaml
bin/cli.rs
docs/default_api.md
examples/ca.pem
examples/client/client_auth.rs

View File

@ -15,6 +15,9 @@ client = [
server = [
"serde_ignored", "hyper", "regex", "percent-encoding", "url", "lazy_static"
]
cli = [
"anyhow", "clap-verbosity-flag", "simple_logger", "structopt", "tokio"
]
conversion = ["frunk", "frunk_derives", "frunk_core", "frunk-enum-core", "frunk-enum-derive"]
[target.'cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))'.dependencies]
@ -52,6 +55,13 @@ lazy_static = { version = "1.4", optional = true }
percent-encoding = {version = "2.1.0", optional = true}
regex = {version = "1.3", optional = true}
# CLI-specific
anyhow = { version = "1", optional = true }
clap-verbosity-flag = { version = "0.3", optional = true }
simple_logger = { version = "2.0", features = ["stderr"], optional = true }
structopt = { version = "0.3", optional = true }
tokio = { version = "0.2", features = ["rt-threaded", "macros", "stream"], optional = true }
# Conversion
frunk = { version = "0.4.0", optional = true }
frunk_derives = { version = "0.4.0", optional = true }
@ -79,3 +89,8 @@ required-features = ["client"]
[[example]]
name = "server"
required-features = ["server"]
[[bin]]
name = "ops-v3"
path = "bin/cli.rs"
required-features = ["client", "cli"]

View File

@ -23,6 +23,7 @@ This autogenerated project defines an API crate `ops-v3` which contains:
* 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 `ops-v3`:
@ -36,6 +37,30 @@ It also contains an example server and client which make use of `ops-v3`:
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:
@ -122,6 +147,8 @@ The generated library has a few optional features that can be activated through
* 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`.

View File

@ -0,0 +1,759 @@
//! CLI tool driving the API client
use anyhow::{anyhow, Context, Result};
use log::{debug, info};
// models may be unused if all inputs are primitive types
#[allow(unused_imports)]
use ops_v3::{
models, ApiNoContext, Client, ContextWrapperExt,
Op10GetResponse,
Op11GetResponse,
Op12GetResponse,
Op13GetResponse,
Op14GetResponse,
Op15GetResponse,
Op16GetResponse,
Op17GetResponse,
Op18GetResponse,
Op19GetResponse,
Op1GetResponse,
Op20GetResponse,
Op21GetResponse,
Op22GetResponse,
Op23GetResponse,
Op24GetResponse,
Op25GetResponse,
Op26GetResponse,
Op27GetResponse,
Op28GetResponse,
Op29GetResponse,
Op2GetResponse,
Op30GetResponse,
Op31GetResponse,
Op32GetResponse,
Op33GetResponse,
Op34GetResponse,
Op35GetResponse,
Op36GetResponse,
Op37GetResponse,
Op3GetResponse,
Op4GetResponse,
Op5GetResponse,
Op6GetResponse,
Op7GetResponse,
Op8GetResponse,
Op9GetResponse,
};
use simple_logger::SimpleLogger;
use structopt::StructOpt;
use swagger::{AuthData, ContextBuilder, EmptyContext, Push, XSpanIdString};
type ClientContext = swagger::make_context_ty!(
ContextBuilder,
EmptyContext,
Option<AuthData>,
XSpanIdString
);
#[derive(StructOpt, Debug)]
#[structopt(
name = "Regression test for large number of operations",
version = "0.0.1",
about = "CLI access to Regression test for large number of operations"
)]
struct Cli {
#[structopt(subcommand)]
operation: Operation,
/// Address or hostname of the server hosting this API, including optional port
#[structopt(short = "a", long, default_value = "http://localhost")]
server_address: String,
/// Path to the client private key if using client-side TLS authentication
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
#[structopt(long, requires_all(&["client-certificate", "server-certificate"]))]
client_key: Option<String>,
/// Path to the client's public certificate associated with the private key
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
#[structopt(long, requires_all(&["client-key", "server-certificate"]))]
client_certificate: Option<String>,
/// Path to CA certificate used to authenticate the server
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
#[structopt(long)]
server_certificate: Option<String>,
/// If set, write output to file instead of stdout
#[structopt(short, long)]
output_file: Option<String>,
#[structopt(flatten)]
verbosity: clap_verbosity_flag::Verbosity,
}
#[derive(StructOpt, Debug)]
enum Operation {
Op10Get {
},
Op11Get {
},
Op12Get {
},
Op13Get {
},
Op14Get {
},
Op15Get {
},
Op16Get {
},
Op17Get {
},
Op18Get {
},
Op19Get {
},
Op1Get {
},
Op20Get {
},
Op21Get {
},
Op22Get {
},
Op23Get {
},
Op24Get {
},
Op25Get {
},
Op26Get {
},
Op27Get {
},
Op28Get {
},
Op29Get {
},
Op2Get {
},
Op30Get {
},
Op31Get {
},
Op32Get {
},
Op33Get {
},
Op34Get {
},
Op35Get {
},
Op36Get {
},
Op37Get {
},
Op3Get {
},
Op4Get {
},
Op5Get {
},
Op6Get {
},
Op7Get {
},
Op8Get {
},
Op9Get {
},
}
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
fn create_client(args: &Cli, context: ClientContext) -> Result<Box<dyn ApiNoContext<ClientContext>>> {
if args.client_certificate.is_some() {
debug!("Using mutual TLS");
let client = Client::try_new_https_mutual(
&args.server_address,
args.server_certificate.clone().unwrap(),
args.client_key.clone().unwrap(),
args.client_certificate.clone().unwrap(),
)
.context("Failed to create HTTPS client")?;
Ok(Box::new(client.with_context(context)))
} else if args.server_certificate.is_some() {
debug!("Using TLS with pinned server certificate");
let client =
Client::try_new_https_pinned(&args.server_address, args.server_certificate.clone().unwrap())
.context("Failed to create HTTPS client")?;
Ok(Box::new(client.with_context(context)))
} else {
debug!("Using client without certificates");
let client =
Client::try_new(&args.server_address).context("Failed to create HTTP(S) client")?;
Ok(Box::new(client.with_context(context)))
}
}
#[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))]
fn create_client(args: &Cli, context: ClientContext) -> Result<Box<dyn ApiNoContext<ClientContext>>> {
let client =
Client::try_new(&args.server_address).context("Failed to create HTTP(S) client")?;
Ok(Box::new(client.with_context(context)))
}
#[tokio::main]
async fn main() -> Result<()> {
let args = Cli::from_args();
if let Some(log_level) = args.verbosity.log_level() {
SimpleLogger::new().with_level(log_level.to_level_filter()).init()?;
}
debug!("Arguments: {:?}", &args);
let auth_data: Option<AuthData> = None;
#[allow(trivial_casts)]
let context = swagger::make_context!(
ContextBuilder,
EmptyContext,
auth_data,
XSpanIdString::default()
);
let client = create_client(&args, context)?;
let result = match args.operation {
Operation::Op10Get {
} => {
info!("Performing a Op10Get request");
let result = client.op10_get(
).await?;
debug!("Result: {:?}", result);
match result {
Op10GetResponse::OK
=> "OK\n".to_string()
,
}
}
Operation::Op11Get {
} => {
info!("Performing a Op11Get request");
let result = client.op11_get(
).await?;
debug!("Result: {:?}", result);
match result {
Op11GetResponse::OK
=> "OK\n".to_string()
,
}
}
Operation::Op12Get {
} => {
info!("Performing a Op12Get request");
let result = client.op12_get(
).await?;
debug!("Result: {:?}", result);
match result {
Op12GetResponse::OK
=> "OK\n".to_string()
,
}
}
Operation::Op13Get {
} => {
info!("Performing a Op13Get request");
let result = client.op13_get(
).await?;
debug!("Result: {:?}", result);
match result {
Op13GetResponse::OK
=> "OK\n".to_string()
,
}
}
Operation::Op14Get {
} => {
info!("Performing a Op14Get request");
let result = client.op14_get(
).await?;
debug!("Result: {:?}", result);
match result {
Op14GetResponse::OK
=> "OK\n".to_string()
,
}
}
Operation::Op15Get {
} => {
info!("Performing a Op15Get request");
let result = client.op15_get(
).await?;
debug!("Result: {:?}", result);
match result {
Op15GetResponse::OK
=> "OK\n".to_string()
,
}
}
Operation::Op16Get {
} => {
info!("Performing a Op16Get request");
let result = client.op16_get(
).await?;
debug!("Result: {:?}", result);
match result {
Op16GetResponse::OK
=> "OK\n".to_string()
,
}
}
Operation::Op17Get {
} => {
info!("Performing a Op17Get request");
let result = client.op17_get(
).await?;
debug!("Result: {:?}", result);
match result {
Op17GetResponse::OK
=> "OK\n".to_string()
,
}
}
Operation::Op18Get {
} => {
info!("Performing a Op18Get request");
let result = client.op18_get(
).await?;
debug!("Result: {:?}", result);
match result {
Op18GetResponse::OK
=> "OK\n".to_string()
,
}
}
Operation::Op19Get {
} => {
info!("Performing a Op19Get request");
let result = client.op19_get(
).await?;
debug!("Result: {:?}", result);
match result {
Op19GetResponse::OK
=> "OK\n".to_string()
,
}
}
Operation::Op1Get {
} => {
info!("Performing a Op1Get request");
let result = client.op1_get(
).await?;
debug!("Result: {:?}", result);
match result {
Op1GetResponse::OK
=> "OK\n".to_string()
,
}
}
Operation::Op20Get {
} => {
info!("Performing a Op20Get request");
let result = client.op20_get(
).await?;
debug!("Result: {:?}", result);
match result {
Op20GetResponse::OK
=> "OK\n".to_string()
,
}
}
Operation::Op21Get {
} => {
info!("Performing a Op21Get request");
let result = client.op21_get(
).await?;
debug!("Result: {:?}", result);
match result {
Op21GetResponse::OK
=> "OK\n".to_string()
,
}
}
Operation::Op22Get {
} => {
info!("Performing a Op22Get request");
let result = client.op22_get(
).await?;
debug!("Result: {:?}", result);
match result {
Op22GetResponse::OK
=> "OK\n".to_string()
,
}
}
Operation::Op23Get {
} => {
info!("Performing a Op23Get request");
let result = client.op23_get(
).await?;
debug!("Result: {:?}", result);
match result {
Op23GetResponse::OK
=> "OK\n".to_string()
,
}
}
Operation::Op24Get {
} => {
info!("Performing a Op24Get request");
let result = client.op24_get(
).await?;
debug!("Result: {:?}", result);
match result {
Op24GetResponse::OK
=> "OK\n".to_string()
,
}
}
Operation::Op25Get {
} => {
info!("Performing a Op25Get request");
let result = client.op25_get(
).await?;
debug!("Result: {:?}", result);
match result {
Op25GetResponse::OK
=> "OK\n".to_string()
,
}
}
Operation::Op26Get {
} => {
info!("Performing a Op26Get request");
let result = client.op26_get(
).await?;
debug!("Result: {:?}", result);
match result {
Op26GetResponse::OK
=> "OK\n".to_string()
,
}
}
Operation::Op27Get {
} => {
info!("Performing a Op27Get request");
let result = client.op27_get(
).await?;
debug!("Result: {:?}", result);
match result {
Op27GetResponse::OK
=> "OK\n".to_string()
,
}
}
Operation::Op28Get {
} => {
info!("Performing a Op28Get request");
let result = client.op28_get(
).await?;
debug!("Result: {:?}", result);
match result {
Op28GetResponse::OK
=> "OK\n".to_string()
,
}
}
Operation::Op29Get {
} => {
info!("Performing a Op29Get request");
let result = client.op29_get(
).await?;
debug!("Result: {:?}", result);
match result {
Op29GetResponse::OK
=> "OK\n".to_string()
,
}
}
Operation::Op2Get {
} => {
info!("Performing a Op2Get request");
let result = client.op2_get(
).await?;
debug!("Result: {:?}", result);
match result {
Op2GetResponse::OK
=> "OK\n".to_string()
,
}
}
Operation::Op30Get {
} => {
info!("Performing a Op30Get request");
let result = client.op30_get(
).await?;
debug!("Result: {:?}", result);
match result {
Op30GetResponse::OK
=> "OK\n".to_string()
,
}
}
Operation::Op31Get {
} => {
info!("Performing a Op31Get request");
let result = client.op31_get(
).await?;
debug!("Result: {:?}", result);
match result {
Op31GetResponse::OK
=> "OK\n".to_string()
,
}
}
Operation::Op32Get {
} => {
info!("Performing a Op32Get request");
let result = client.op32_get(
).await?;
debug!("Result: {:?}", result);
match result {
Op32GetResponse::OK
=> "OK\n".to_string()
,
}
}
Operation::Op33Get {
} => {
info!("Performing a Op33Get request");
let result = client.op33_get(
).await?;
debug!("Result: {:?}", result);
match result {
Op33GetResponse::OK
=> "OK\n".to_string()
,
}
}
Operation::Op34Get {
} => {
info!("Performing a Op34Get request");
let result = client.op34_get(
).await?;
debug!("Result: {:?}", result);
match result {
Op34GetResponse::OK
=> "OK\n".to_string()
,
}
}
Operation::Op35Get {
} => {
info!("Performing a Op35Get request");
let result = client.op35_get(
).await?;
debug!("Result: {:?}", result);
match result {
Op35GetResponse::OK
=> "OK\n".to_string()
,
}
}
Operation::Op36Get {
} => {
info!("Performing a Op36Get request");
let result = client.op36_get(
).await?;
debug!("Result: {:?}", result);
match result {
Op36GetResponse::OK
=> "OK\n".to_string()
,
}
}
Operation::Op37Get {
} => {
info!("Performing a Op37Get request");
let result = client.op37_get(
).await?;
debug!("Result: {:?}", result);
match result {
Op37GetResponse::OK
=> "OK\n".to_string()
,
}
}
Operation::Op3Get {
} => {
info!("Performing a Op3Get request");
let result = client.op3_get(
).await?;
debug!("Result: {:?}", result);
match result {
Op3GetResponse::OK
=> "OK\n".to_string()
,
}
}
Operation::Op4Get {
} => {
info!("Performing a Op4Get request");
let result = client.op4_get(
).await?;
debug!("Result: {:?}", result);
match result {
Op4GetResponse::OK
=> "OK\n".to_string()
,
}
}
Operation::Op5Get {
} => {
info!("Performing a Op5Get request");
let result = client.op5_get(
).await?;
debug!("Result: {:?}", result);
match result {
Op5GetResponse::OK
=> "OK\n".to_string()
,
}
}
Operation::Op6Get {
} => {
info!("Performing a Op6Get request");
let result = client.op6_get(
).await?;
debug!("Result: {:?}", result);
match result {
Op6GetResponse::OK
=> "OK\n".to_string()
,
}
}
Operation::Op7Get {
} => {
info!("Performing a Op7Get request");
let result = client.op7_get(
).await?;
debug!("Result: {:?}", result);
match result {
Op7GetResponse::OK
=> "OK\n".to_string()
,
}
}
Operation::Op8Get {
} => {
info!("Performing a Op8Get request");
let result = client.op8_get(
).await?;
debug!("Result: {:?}", result);
match result {
Op8GetResponse::OK
=> "OK\n".to_string()
,
}
}
Operation::Op9Get {
} => {
info!("Performing a Op9Get request");
let result = client.op9_get(
).await?;
debug!("Result: {:?}", result);
match result {
Op9GetResponse::OK
=> "OK\n".to_string()
,
}
}
};
if let Some(output_file) = args.output_file {
std::fs::write(output_file, result)?
} else {
println!("{}", result);
}
Ok(())
}
// May be unused if all inputs are primitive types
#[allow(dead_code)]
fn parse_json<'a, T: serde::de::Deserialize<'a>>(json_string: &'a str) -> Result<T> {
serde_json::from_str(json_string).map_err(|err| anyhow!("Error parsing input: {}", err))
}

View File

@ -3,6 +3,7 @@
Cargo.toml
README.md
api/openapi.yaml
bin/cli.rs
docs/AdditionalPropertiesClass.md
docs/Animal.md
docs/AnimalFarm.md

View File

@ -20,6 +20,10 @@ server = [
"multipart", "multipart/server", "swagger/multipart_form",
"serde_ignored", "hyper", "regex", "percent-encoding", "url", "lazy_static"
]
cli = [
"dialoguer",
"anyhow", "clap-verbosity-flag", "simple_logger", "structopt", "tokio"
]
conversion = ["frunk", "frunk_derives", "frunk_core", "frunk-enum-core", "frunk-enum-derive"]
[target.'cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))'.dependencies]
@ -64,6 +68,14 @@ lazy_static = { version = "1.4", optional = true }
percent-encoding = {version = "2.1.0", optional = true}
regex = {version = "1.3", optional = true}
# CLI-specific
anyhow = { version = "1", optional = true }
clap-verbosity-flag = { version = "0.3", optional = true }
simple_logger = { version = "2.0", features = ["stderr"], optional = true }
structopt = { version = "0.3", optional = true }
tokio = { version = "0.2", features = ["rt-threaded", "macros", "stream"], optional = true }
dialoguer = { version = "0.8", optional = true }
# Conversion
frunk = { version = "0.4.0", optional = true }
frunk_derives = { version = "0.4.0", optional = true }
@ -91,3 +103,8 @@ required-features = ["client"]
[[example]]
name = "server"
required-features = ["server"]
[[bin]]
name = "petstore-with-fake-endpoints-models-for-testing"
path = "bin/cli.rs"
required-features = ["client", "cli"]

View File

@ -23,6 +23,7 @@ This autogenerated project defines an API crate `petstore-with-fake-endpoints-mo
* 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 `petstore-with-fake-endpoints-models-for-testing`:
@ -36,6 +37,30 @@ It also contains an example server and client which make use of `petstore-with-f
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:
@ -110,6 +135,8 @@ The generated library has a few optional features that can be activated through
* 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`.

View File

@ -3,6 +3,7 @@
Cargo.toml
README.md
api/openapi.yaml
bin/cli.rs
docs/default_api.md
examples/ca.pem
examples/client/client_auth.rs

View File

@ -15,6 +15,9 @@ client = [
server = [
"serde_ignored", "hyper", "regex", "percent-encoding", "url", "lazy_static"
]
cli = [
"anyhow", "clap-verbosity-flag", "simple_logger", "structopt", "tokio"
]
conversion = ["frunk", "frunk_derives", "frunk_core", "frunk-enum-core", "frunk-enum-derive"]
[target.'cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))'.dependencies]
@ -52,6 +55,13 @@ lazy_static = { version = "1.4", optional = true }
percent-encoding = {version = "2.1.0", optional = true}
regex = {version = "1.3", optional = true}
# CLI-specific
anyhow = { version = "1", optional = true }
clap-verbosity-flag = { version = "0.3", optional = true }
simple_logger = { version = "2.0", features = ["stderr"], optional = true }
structopt = { version = "0.3", optional = true }
tokio = { version = "0.2", features = ["rt-threaded", "macros", "stream"], optional = true }
# Conversion
frunk = { version = "0.4.0", optional = true }
frunk_derives = { version = "0.4.0", optional = true }
@ -79,3 +89,8 @@ required-features = ["client"]
[[example]]
name = "server"
required-features = ["server"]
[[bin]]
name = "ping-bearer-auth"
path = "bin/cli.rs"
required-features = ["client", "cli"]

View File

@ -23,6 +23,7 @@ This autogenerated project defines an API crate `ping-bearer-auth` which contain
* 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 `ping-bearer-auth`:
@ -36,6 +37,30 @@ It also contains an example server and client which make use of `ping-bearer-aut
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:
@ -86,6 +111,8 @@ The generated library has a few optional features that can be activated through
* 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`.

View File

@ -0,0 +1,156 @@
//! CLI tool driving the API client
use anyhow::{anyhow, Context, Result};
use log::{debug, info};
// models may be unused if all inputs are primitive types
#[allow(unused_imports)]
use ping_bearer_auth::{
models, ApiNoContext, Client, ContextWrapperExt,
PingGetResponse,
};
use simple_logger::SimpleLogger;
use structopt::StructOpt;
use swagger::{AuthData, ContextBuilder, EmptyContext, Push, XSpanIdString};
type ClientContext = swagger::make_context_ty!(
ContextBuilder,
EmptyContext,
Option<AuthData>,
XSpanIdString
);
#[derive(StructOpt, Debug)]
#[structopt(
name = "ping test",
version = "1.0",
about = "CLI access to ping test"
)]
struct Cli {
#[structopt(subcommand)]
operation: Operation,
/// Address or hostname of the server hosting this API, including optional port
#[structopt(short = "a", long, default_value = "http://localhost")]
server_address: String,
/// Path to the client private key if using client-side TLS authentication
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
#[structopt(long, requires_all(&["client-certificate", "server-certificate"]))]
client_key: Option<String>,
/// Path to the client's public certificate associated with the private key
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
#[structopt(long, requires_all(&["client-key", "server-certificate"]))]
client_certificate: Option<String>,
/// Path to CA certificate used to authenticate the server
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
#[structopt(long)]
server_certificate: Option<String>,
/// If set, write output to file instead of stdout
#[structopt(short, long)]
output_file: Option<String>,
#[structopt(flatten)]
verbosity: clap_verbosity_flag::Verbosity,
/// Bearer token if used for authentication
#[structopt(env = "PING_BEARER_AUTH_BEARER_TOKEN", hide_env_values = true)]
bearer_token: Option<String>,
}
#[derive(StructOpt, Debug)]
enum Operation {
PingGet {
},
}
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
fn create_client(args: &Cli, context: ClientContext) -> Result<Box<dyn ApiNoContext<ClientContext>>> {
if args.client_certificate.is_some() {
debug!("Using mutual TLS");
let client = Client::try_new_https_mutual(
&args.server_address,
args.server_certificate.clone().unwrap(),
args.client_key.clone().unwrap(),
args.client_certificate.clone().unwrap(),
)
.context("Failed to create HTTPS client")?;
Ok(Box::new(client.with_context(context)))
} else if args.server_certificate.is_some() {
debug!("Using TLS with pinned server certificate");
let client =
Client::try_new_https_pinned(&args.server_address, args.server_certificate.clone().unwrap())
.context("Failed to create HTTPS client")?;
Ok(Box::new(client.with_context(context)))
} else {
debug!("Using client without certificates");
let client =
Client::try_new(&args.server_address).context("Failed to create HTTP(S) client")?;
Ok(Box::new(client.with_context(context)))
}
}
#[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))]
fn create_client(args: &Cli, context: ClientContext) -> Result<Box<dyn ApiNoContext<ClientContext>>> {
let client =
Client::try_new(&args.server_address).context("Failed to create HTTP(S) client")?;
Ok(Box::new(client.with_context(context)))
}
#[tokio::main]
async fn main() -> Result<()> {
let args = Cli::from_args();
if let Some(log_level) = args.verbosity.log_level() {
SimpleLogger::new().with_level(log_level.to_level_filter()).init()?;
}
debug!("Arguments: {:?}", &args);
let mut auth_data: Option<AuthData> = None;
if let Some(ref bearer_token) = args.bearer_token {
debug!("Using bearer token");
auth_data = Some(AuthData::bearer(bearer_token));
}
#[allow(trivial_casts)]
let context = swagger::make_context!(
ContextBuilder,
EmptyContext,
auth_data,
XSpanIdString::default()
);
let client = create_client(&args, context)?;
let result = match args.operation {
Operation::PingGet {
} => {
info!("Performing a PingGet request");
let result = client.ping_get(
).await?;
debug!("Result: {:?}", result);
match result {
PingGetResponse::OK
=> "OK\n".to_string()
,
}
}
};
if let Some(output_file) = args.output_file {
std::fs::write(output_file, result)?
} else {
println!("{}", result);
}
Ok(())
}
// May be unused if all inputs are primitive types
#[allow(dead_code)]
fn parse_json<'a, T: serde::de::Deserialize<'a>>(json_string: &'a str) -> Result<T> {
serde_json::from_str(json_string).map_err(|err| anyhow!("Error parsing input: {}", err))
}

View File

@ -3,6 +3,7 @@
Cargo.toml
README.md
api/openapi.yaml
bin/cli.rs
docs/ANullableContainer.md
docs/AdditionalPropertiesObject.md
docs/AllOfObject.md

View File

@ -15,6 +15,9 @@ client = [
server = [
"serde_ignored", "hyper", "regex", "percent-encoding", "url", "lazy_static"
]
cli = [
"anyhow", "clap-verbosity-flag", "simple_logger", "structopt", "tokio"
]
conversion = ["frunk", "frunk_derives", "frunk_core", "frunk-enum-core", "frunk-enum-derive"]
[target.'cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))'.dependencies]
@ -52,6 +55,13 @@ lazy_static = { version = "1.4", optional = true }
percent-encoding = {version = "2.1.0", optional = true}
regex = {version = "1.3", optional = true}
# CLI-specific
anyhow = { version = "1", optional = true }
clap-verbosity-flag = { version = "0.3", optional = true }
simple_logger = { version = "2.0", features = ["stderr"], optional = true }
structopt = { version = "0.3", optional = true }
tokio = { version = "0.2", features = ["rt-threaded", "macros", "stream"], optional = true }
# Conversion
frunk = { version = "0.4.0", optional = true }
frunk_derives = { version = "0.4.0", optional = true }
@ -79,3 +89,8 @@ required-features = ["client"]
[[example]]
name = "server"
required-features = ["server"]
[[bin]]
name = "rust-server-test"
path = "bin/cli.rs"
required-features = ["client", "cli"]

View File

@ -23,6 +23,7 @@ This autogenerated project defines an API crate `rust-server-test` which contain
* 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 `rust-server-test`:
@ -36,6 +37,30 @@ It also contains an example server and client which make use of `rust-server-tes
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:
@ -92,6 +117,8 @@ The generated library has a few optional features that can be activated through
* 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`.

View File

@ -0,0 +1,312 @@
//! CLI tool driving the API client
use anyhow::{anyhow, Context, Result};
use log::{debug, info};
// models may be unused if all inputs are primitive types
#[allow(unused_imports)]
use rust_server_test::{
models, ApiNoContext, Client, ContextWrapperExt,
AllOfGetResponse,
DummyGetResponse,
DummyPutResponse,
FileResponseGetResponse,
GetStructuredYamlResponse,
HtmlPostResponse,
PostYamlResponse,
RawJsonGetResponse,
SoloObjectPostResponse,
};
use simple_logger::SimpleLogger;
use structopt::StructOpt;
use swagger::{AuthData, ContextBuilder, EmptyContext, Push, XSpanIdString};
type ClientContext = swagger::make_context_ty!(
ContextBuilder,
EmptyContext,
Option<AuthData>,
XSpanIdString
);
#[derive(StructOpt, Debug)]
#[structopt(
name = "rust-server-test",
version = "2.3.4",
about = "CLI access to rust-server-test"
)]
struct Cli {
#[structopt(subcommand)]
operation: Operation,
/// Address or hostname of the server hosting this API, including optional port
#[structopt(short = "a", long, default_value = "http://localhost")]
server_address: String,
/// Path to the client private key if using client-side TLS authentication
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
#[structopt(long, requires_all(&["client-certificate", "server-certificate"]))]
client_key: Option<String>,
/// Path to the client's public certificate associated with the private key
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
#[structopt(long, requires_all(&["client-key", "server-certificate"]))]
client_certificate: Option<String>,
/// Path to CA certificate used to authenticate the server
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
#[structopt(long)]
server_certificate: Option<String>,
/// If set, write output to file instead of stdout
#[structopt(short, long)]
output_file: Option<String>,
#[structopt(flatten)]
verbosity: clap_verbosity_flag::Verbosity,
}
#[derive(StructOpt, Debug)]
enum Operation {
AllOfGet {
},
/// A dummy endpoint to make the spec valid.
DummyGet {
},
DummyPut {
#[structopt(parse(try_from_str = parse_json))]
nested_response: models::DummyPutRequest,
},
/// Get a file
FileResponseGet {
},
GetStructuredYaml {
},
/// Test HTML handling
HtmlPost {
body: String,
},
PostYaml {
/// The YAML body to test
value: String,
},
/// Get an arbitrary JSON blob.
RawJsonGet {
},
/// Send an arbitrary JSON blob
SoloObjectPost {
value: serde_json::Value,
},
}
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
fn create_client(args: &Cli, context: ClientContext) -> Result<Box<dyn ApiNoContext<ClientContext>>> {
if args.client_certificate.is_some() {
debug!("Using mutual TLS");
let client = Client::try_new_https_mutual(
&args.server_address,
args.server_certificate.clone().unwrap(),
args.client_key.clone().unwrap(),
args.client_certificate.clone().unwrap(),
)
.context("Failed to create HTTPS client")?;
Ok(Box::new(client.with_context(context)))
} else if args.server_certificate.is_some() {
debug!("Using TLS with pinned server certificate");
let client =
Client::try_new_https_pinned(&args.server_address, args.server_certificate.clone().unwrap())
.context("Failed to create HTTPS client")?;
Ok(Box::new(client.with_context(context)))
} else {
debug!("Using client without certificates");
let client =
Client::try_new(&args.server_address).context("Failed to create HTTP(S) client")?;
Ok(Box::new(client.with_context(context)))
}
}
#[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))]
fn create_client(args: &Cli, context: ClientContext) -> Result<Box<dyn ApiNoContext<ClientContext>>> {
let client =
Client::try_new(&args.server_address).context("Failed to create HTTP(S) client")?;
Ok(Box::new(client.with_context(context)))
}
#[tokio::main]
async fn main() -> Result<()> {
let args = Cli::from_args();
if let Some(log_level) = args.verbosity.log_level() {
SimpleLogger::new().with_level(log_level.to_level_filter()).init()?;
}
debug!("Arguments: {:?}", &args);
let auth_data: Option<AuthData> = None;
#[allow(trivial_casts)]
let context = swagger::make_context!(
ContextBuilder,
EmptyContext,
auth_data,
XSpanIdString::default()
);
let client = create_client(&args, context)?;
let result = match args.operation {
Operation::AllOfGet {
} => {
info!("Performing a AllOfGet request");
let result = client.all_of_get(
).await?;
debug!("Result: {:?}", result);
match result {
AllOfGetResponse::OK
(body)
=> "OK\n".to_string()
+
&serde_json::to_string_pretty(&body)?,
}
}
Operation::DummyGet {
} => {
info!("Performing a DummyGet request");
let result = client.dummy_get(
).await?;
debug!("Result: {:?}", result);
match result {
DummyGetResponse::Success
=> "Success\n".to_string()
,
}
}
Operation::DummyPut {
nested_response,
} => {
info!("Performing a DummyPut request");
let result = client.dummy_put(
nested_response,
).await?;
debug!("Result: {:?}", result);
match result {
DummyPutResponse::Success
=> "Success\n".to_string()
,
}
}
Operation::FileResponseGet {
} => {
info!("Performing a FileResponseGet request");
let result = client.file_response_get(
).await?;
debug!("Result: {:?}", result);
match result {
FileResponseGetResponse::Success
(body)
=> "Success\n".to_string()
+
&serde_json::to_string_pretty(&body)?,
}
}
Operation::GetStructuredYaml {
} => {
info!("Performing a GetStructuredYaml request");
let result = client.get_structured_yaml(
).await?;
debug!("Result: {:?}", result);
match result {
GetStructuredYamlResponse::OK
(body)
=> "OK\n".to_string()
+
&serde_json::to_string_pretty(&body)?,
}
}
Operation::HtmlPost {
body,
} => {
info!("Performing a HtmlPost request");
let result = client.html_post(
body,
).await?;
debug!("Result: {:?}", result);
match result {
HtmlPostResponse::Success
(body)
=> "Success\n".to_string()
+
&serde_json::to_string_pretty(&body)?,
}
}
Operation::PostYaml {
value,
} => {
info!("Performing a PostYaml request");
let result = client.post_yaml(
value,
).await?;
debug!("Result: {:?}", result);
match result {
PostYamlResponse::OK
=> "OK\n".to_string()
,
}
}
Operation::RawJsonGet {
} => {
info!("Performing a RawJsonGet request");
let result = client.raw_json_get(
).await?;
debug!("Result: {:?}", result);
match result {
RawJsonGetResponse::Success
(body)
=> "Success\n".to_string()
+
&serde_json::to_string_pretty(&body)?,
}
}
Operation::SoloObjectPost {
value,
} => {
info!("Performing a SoloObjectPost request");
let result = client.solo_object_post(
value,
).await?;
debug!("Result: {:?}", result);
match result {
SoloObjectPostResponse::OK
=> "OK\n".to_string()
,
}
}
};
if let Some(output_file) = args.output_file {
std::fs::write(output_file, result)?
} else {
println!("{}", result);
}
Ok(())
}
// May be unused if all inputs are primitive types
#[allow(dead_code)]
fn parse_json<'a, T: serde::de::Deserialize<'a>>(json_string: &'a str) -> Result<T> {
serde_json::from_str(json_string).map_err(|err| anyhow!("Error parsing input: {}", err))
}