forked from loafle/openapi-generator-original
[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:
parent
69cce249f6
commit
2b40a2c058
@ -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) {
|
||||
|
@ -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"]
|
||||
|
@ -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`
|
||||
|
@ -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`.
|
||||
|
||||
|
317
modules/openapi-generator/src/main/resources/rust-server/bin-cli.mustache
vendored
Normal file
317
modules/openapi-generator/src/main/resources/rust-server/bin-cli.mustache
vendored
Normal 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))
|
||||
}
|
@ -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
|
||||
|
@ -3,6 +3,7 @@
|
||||
Cargo.toml
|
||||
README.md
|
||||
api/openapi.yaml
|
||||
bin/cli.rs
|
||||
docs/MultipartRelatedRequest.md
|
||||
docs/MultipartRequestObjectField.md
|
||||
docs/MultipleIdenticalMimeTypesPostRequest.md
|
||||
|
@ -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"]
|
||||
|
@ -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`.
|
||||
|
||||
|
@ -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))
|
||||
}
|
@ -3,6 +3,7 @@
|
||||
Cargo.toml
|
||||
README.md
|
||||
api/openapi.yaml
|
||||
bin/cli.rs
|
||||
docs/OpGetRequest.md
|
||||
docs/default_api.md
|
||||
examples/ca.pem
|
||||
|
@ -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"]
|
||||
|
@ -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`.
|
||||
|
||||
|
@ -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't have any example",
|
||||
version = "0.0.1",
|
||||
about = "CLI access to Regression test for an API which doesn'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))
|
||||
}
|
@ -3,6 +3,7 @@
|
||||
Cargo.toml
|
||||
README.md
|
||||
api/openapi.yaml
|
||||
bin/cli.rs
|
||||
docs/AdditionalPropertiesWithList.md
|
||||
docs/AnotherXmlArray.md
|
||||
docs/AnotherXmlInner.md
|
||||
|
@ -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"]
|
||||
|
@ -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.
|
||||
```
|
||||
```
|
||||
|
@ -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
|
||||
|
||||
|
854
samples/server/petstore/rust-server/output/openapi-v3/bin/cli.rs
Normal file
854
samples/server/petstore/rust-server/output/openapi-v3/bin/cli.rs
Normal 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))
|
||||
}
|
@ -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)
|
||||
|
||||
|
@ -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")
|
||||
|
@ -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,
|
||||
|
@ -27,6 +27,8 @@ fn full_permission_claim() -> Claims {
|
||||
[
|
||||
"test.read",
|
||||
"test.write",
|
||||
"additional.test.read",
|
||||
"additional.test.write",
|
||||
].join::<&str>(", ")
|
||||
}
|
||||
}
|
||||
|
@ -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",
|
||||
¶m_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(¶m_path_param_a.to_string(), ID_ENCODE_SET)
|
||||
,path_param_b=utf8_percent_encode(¶m_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,
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
(¶m_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}
|
||||
|
@ -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
|
||||
|
@ -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"]
|
||||
|
@ -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`.
|
||||
|
||||
|
759
samples/server/petstore/rust-server/output/ops-v3/bin/cli.rs
Normal file
759
samples/server/petstore/rust-server/output/ops-v3/bin/cli.rs
Normal 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))
|
||||
}
|
@ -3,6 +3,7 @@
|
||||
Cargo.toml
|
||||
README.md
|
||||
api/openapi.yaml
|
||||
bin/cli.rs
|
||||
docs/AdditionalPropertiesClass.md
|
||||
docs/Animal.md
|
||||
docs/AnimalFarm.md
|
||||
|
@ -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"]
|
||||
|
@ -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`.
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
|
@ -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"]
|
||||
|
@ -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`.
|
||||
|
||||
|
@ -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))
|
||||
}
|
@ -3,6 +3,7 @@
|
||||
Cargo.toml
|
||||
README.md
|
||||
api/openapi.yaml
|
||||
bin/cli.rs
|
||||
docs/ANullableContainer.md
|
||||
docs/AdditionalPropertiesObject.md
|
||||
docs/AllOfObject.md
|
||||
|
@ -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"]
|
||||
|
@ -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`.
|
||||
|
||||
|
@ -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))
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user