Skip to content

Commit

Permalink
[Rust Server] Add auto-generated CLI Client (OpenAPITools#19392)
Browse files Browse the repository at this point in the history
* [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
  • Loading branch information
richardwhiuk authored Aug 24, 2024
1 parent 69cce24 commit 2b40a2c
Show file tree
Hide file tree
Showing 43 changed files with 5,197 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ public RustServerCodegen() {
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());
}
Expand Down Expand Up @@ -586,6 +587,13 @@ public CodegenOperation fromOperation(String path, String httpMethod, Operation
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);
Expand Down Expand Up @@ -858,6 +866,27 @@ private void postProcessOperationWithModels(CodegenOperation op, List<ModelMap>
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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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 }
Expand Down Expand Up @@ -153,3 +169,8 @@ required-features = ["client"]
[[example]]
name = "server"
required-features = ["server"]

[[bin]]
name = "{{{packageName}}}"
path = "bin/cli.rs"
required-features = ["client", "cli"]
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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}}}`:

Expand All @@ -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:
Expand Down Expand Up @@ -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`.

Expand Down
Loading

0 comments on commit 2b40a2c

Please sign in to comment.