From 1a831f73f38feb09fa12f6e94a70574023706bd3 Mon Sep 17 00:00:00 2001 From: Benjamin Gill Date: Fri, 8 Dec 2017 10:42:14 +0000 Subject: [PATCH] [rust-server] Plaintext support; encode params (#7082) * MMORCH-428: Export the Authorization struct This is needed so that code can check that the version of Authorization in the auto-generated code is the same as the version it is using. If the versions are not exactly the same then the lookup into the TypeMap will not work. * Add Rust as a supported language for client and server. Clarify that there are two Rust client implementations, and one Rust server implementation. * Percent-encode path and query parameters in client URLs Fixes #122 Also don't include a question mark at the end of the path when there are no query paramters. Fixes #121 * Rust2 client: add --host and --port parameters to example client. Allows the example command-line client to override the default host and port. * Extract default host and port from Swagger file. * Derive 'Eq' and 'Ord' on enums * Rust2: improve server code structure. server.rs (main.rs) - main entry point for binary; starts the web server and points it at the server code within the library. server_lib/mod.rs (lib.rs) - root of library; creates the server. server_lib/server.rs (server.rs) - actual server code The old server_lib/mod.rs is now server_lib/server.rs; server_lib/mod.rs is new. This structure is easy to map onto a server implementation; unfortunately we can't get it exactly right here because of the limitations of cargo's examples/ folder. * Rust2: Explain fully how to use the example code in your project. * Added plaintext support * Linting * MGJ Markups --- README.md | 36 +-- .../codegen/languages/RustServerCodegen.java | 41 ++- .../main/resources/rust-server/Cargo.mustache | 11 +- .../resources/rust-server/README.mustache | 51 +++ .../resources/rust-server/client.mustache | 36 ++- .../rust-server/example-client.mustache | 23 +- .../rust-server/example-server.mustache | 18 +- .../rust-server/example-server_lib.mustache | 39 +-- .../example-server_server.mustache | 30 ++ .../resources/rust-server/models.mustache | 2 +- .../resources/rust-server/server.mustache | 28 +- ...ith-fake-endpoints-models-for-testing.yaml | 2 +- .../rust2/examples/server_lib/server.rs | 294 ++++++++++++++++++ .../server/petstore/rust-server/Cargo.toml | 11 +- samples/server/petstore/rust-server/README.md | 53 +++- .../petstore/rust-server/api/swagger.yaml | 30 +- .../petstore/rust-server/examples/client.rs | 23 +- .../petstore/rust-server/examples/server.rs | 22 +- .../rust-server/examples/server_lib/mod.rs | 279 +---------------- .../rust-server/examples/server_lib/server.rs | 270 ++++++++++++++++ .../server/petstore/rust-server/src/client.rs | 189 +++++++---- .../server/petstore/rust-server/src/models.rs | 4 +- .../server/petstore/rust-server/src/server.rs | 78 +++-- 23 files changed, 1111 insertions(+), 459 deletions(-) create mode 100644 modules/swagger-codegen/src/main/resources/rust-server/example-server_server.mustache create mode 100644 samples/client/petstore/rust2/examples/server_lib/server.rs create mode 100644 samples/server/petstore/rust-server/examples/server_lib/server.rs diff --git a/README.md b/README.md index 90d496b155a..243a0b009ca 100644 --- a/README.md +++ b/README.md @@ -24,9 +24,9 @@ ## Overview This is the swagger codegen project, which allows generation of API client libraries (SDK generation), server stubs and documentation automatically given an [OpenAPI Spec](https://github.com/OAI/OpenAPI-Specification). Currently, the following languages/frameworks are supported: -- **API clients**: **ActionScript**, **Ada**, **Apex**, **Bash**, **C#** (.net 2.0, 3.5 or later), **C++** (cpprest, Qt5, Tizen), **Clojure**, **Dart**, **Elixir**, **Eiffel**, **Erlang**, **Go**, **Groovy**, **Haskell** (http-client, Servant), **Java** (Jersey1.x, Jersey2.x, OkHttp, Retrofit1.x, Retrofit2.x, Feign, RestTemplate, RESTEasy, Vertx, Google API Client Library for Java), **Kotlin**, **Lua**, **Node.js** (ES5, ES6, AngularJS with Google Closure Compiler annotations) **Objective-C**, **Perl**, **PHP**, **PowerShell**, **Python**, **R**, **Ruby**, **Rust**, **Scala** (akka, http4s, swagger-async-httpclient), **Swift** (2.x, 3.x, 4.x), **Typescript** (Angular1.x, Angular2.x, Fetch, jQuery, Node) -- **Server stubs**: **C#** (ASP.NET Core, NancyFx), **C++** (Pistache, Restbed), **Erlang**, **Go**, **Haskell** (Servant), **Java** (MSF4J, Spring, Undertow, JAX-RS: CDI, CXF, Inflector, RestEasy, Play Framework), **PHP** (Lumen, Slim, Silex, [Symfony](https://symfony.com/), [Zend Expressive](https://github.com/zendframework/zend-expressive)), **Python** (Flask), **NodeJS**, **Ruby** (Sinatra, Rails5), **Rust**, **Scala** ([Finch](https://github.com/finagle/finch), [Lagom](https://github.com/lagom/lagom), Scalatra) -- **API documentation generators**: **HTML**, **Confluence Wiki** +- **API clients**: **ActionScript**, **Ada**, **Apex**, **Bash**, **C#** (.net 2.0, 3.5 or later), **C++** (cpprest, Qt5, Tizen), **Clojure**, **Dart**, **Elixir**, **Eiffel**, **Erlang**, **Go**, **Groovy**, **Haskell** (http-client, Servant), **Java** (Jersey1.x, Jersey2.x, OkHttp, Retrofit1.x, Retrofit2.x, Feign, RestTemplate, RESTEasy, Vertx, Google API Client Library for Java), **Kotlin**, **Lua**, **Node.js** (ES5, ES6, AngularJS with Google Closure Compiler annotations) **Objective-C**, **Perl**, **PHP**, **PowerShell**, **Python**, **R**, **Ruby**, **Rust** (rust, rust-server), **Scala** (akka, http4s, swagger-async-httpclient), **Swift** (2.x, 3.x, 4.x), **Typescript** (Angular1.x, Angular2.x, Fetch, jQuery, Node) +- **Server stubs**: **C#** (ASP.NET Core, NancyFx), **C++** (Pistache, Restbed), **Erlang**, **Go**, **Haskell** (Servant), **Java** (MSF4J, Spring, Undertow, JAX-RS: CDI, CXF, Inflector, RestEasy, Play Framework), **PHP** (Lumen, Slim, Silex, [Symfony](https://symfony.com/), [Zend Expressive](https://github.com/zendframework/zend-expressive)), **Python** (Flask), **NodeJS**, **Ruby** (Sinatra, Rails5), **Rust** (rust-server), **Scala** ([Finch](https://github.com/finagle/finch), [Lagom](https://github.com/lagom/lagom), Scalatra) +- **API documentation generators**: **HTML**, **Confluence Wiki** - **Configuration files**: [**Apache2**](https://httpd.apache.org/) - **Others**: **JMeter** @@ -300,8 +300,8 @@ OPTIONS adds authorization headers when fetching the swagger definitions remotely. Pass in a URL-encoded string of name:header with a comma separating multiple values - -...... (results omitted) + +...... (results omitted) -v, --verbose verbose mode @@ -497,7 +497,7 @@ and `config.json` contains the following as an example: } ``` -Supported config options can be different per language. Running `config-help -l {lang}` will show available options. +Supported config options can be different per language. Running `config-help -l {lang}` will show available options. **These options are applied via configuration file (e.g. config.json) or by passing them with `-D{optionName}={optionValue}`**. (If `-D{optionName}` does not work, please open a [ticket](https://github.com/swagger-api/swagger-codegen/issues/new) and we'll look into it) ```sh @@ -789,7 +789,7 @@ Here are some companies/projects using Swagger Codegen in production. To add you - [High Technologies Center](http://htc-cs.com) - [IBM](https://www.ibm.com) - [IMS Health](http://www.imshealth.com/en/solution-areas/technology-and-applications) -- [Individual Standard IVS](http://www.individual-standard.com) +- [Individual Standard IVS](http://www.individual-standard.com) - [Intent HQ](http://www.intenthq.com) - [Kabuku](http://www.kabuku.co.jp/en) - [Kurio](https://kurio.co.id) @@ -890,7 +890,7 @@ Presentations/Videos/Tutorials/Books - 2016/11/25 - [Swagger Codegen for Swift3 and NodeJS](https://normand1.github.io/blog/swift/swagger/codegen/2016/11/25/Swagger-Codegen-for-Swift3-and-NodeJS.html) by [David Norman](https://github.com/normand1) - 2016/12/08 - [Generate client side code using Swagger Codegen](https://carra-lucia-ltd.co.uk/2016/12/08/generate-client-side-code-using-swagger-codegen/) by [theFerkel](https://carra-lucia-ltd.co.uk/author/theferkel/) - 2017/01/16 - [Zero to API in 4 minutes](https://cidrblock.github.io/zero-to-api-in-4-minutes.html) by [Bradley A. Thornton](https://github.com/cidrblock) -- 2017/02/09 - [「Swaggerを利用した新規サービス開発」というタイトルで登壇して来ました](https://techblog.recochoku.jp/1055) by [recotech](https://www.slideshare.net/recotech) +- 2017/02/09 - [「Swaggerを利用した新規サービス開発」というタイトルで登壇して来ました](https://techblog.recochoku.jp/1055) by [recotech](https://www.slideshare.net/recotech) - 2017/03/03 - [Swagger Codegen の使い方の簡単な説明です](https://speakerdeck.com/wagyu298/swagger-codegen) by [wagyu298](https://github.com/wagyu298) - 2017/03/24 - [Using Open API Specification To Put Lyft SDK Support in the Fast Lane](https://medium.com/lyft-developer-platform/using-open-api-specification-to-put-lyft-sdk-support-in-the-fast-lane-7b623218e4ee) by [Val Polouchkine](https://github.com/vpolouchkine) - 2017/04/13 - [Automatically Generating your API Client with Swagger and Swagger Codegen](https://www.youtube.com/watch?v=EzKwi-u9jQo) by [Jesse Collis](https://github.com/jessedc) @ Melbourne Cocoaheads @@ -903,7 +903,7 @@ Presentations/Videos/Tutorials/Books - 2017/07/11 - [OpenAPI development with Python](https://www.slideshare.net/TakuroWada/20170711-euro-python2017) by [和田拓朗](https://github.com/taxpon) at [EuroPython 2017](https://ep2017.europython.eu/en/) - 2017/07/29 - [How Square makes its SDKs](https://medium.com/square-corner-blog/how-square-makes-its-sdks-6a0fd7ea4b2d) by [Tristan Sokol](https://github.com/tristansokol) ([Square](https://github.com/square)) - 2017/07/31 - [How to Generate a Deployable REST CXF3 Application from a Swagger-Contract](https://www.youtube.com/watch?v=gM63rJlUHZQ) by [Johannes Fiala](https://github.com/jfiala) @ Voxxed Days Vienna -- 2017/08/11 - [Swagger Codegen 自动生成Retrofit 代码](https://juejin.im/entry/598d8eb86fb9a03c52459e2a) by [徐磊](http://www.jianshu.com/u/792c738b33fc) +- 2017/08/11 - [Swagger Codegen 自动生成Retrofit 代码](https://juejin.im/entry/598d8eb86fb9a03c52459e2a) by [徐磊](http://www.jianshu.com/u/792c738b33fc) - 2017/08/24 - [APIs First](https://engineering.squarespace.com/blog/2017/apis-first) by [roykachouh](https://github.com/roykachouh) ([Square](https://github.com/square)) - 2017/08/31 - [Bringing Jenkins Remote Access API To The Masses](http://blog.cliffano.com/2017/09/01/jenkins-world-2017/) by [Cliffano Subagio](http://cliffano.com) from [Shine Solutions](https://shinesolutions.com/) @ [Jenkins World 2017](https://jenkinsworld20162017.sched.com/) - 2017/09/08 - [Swagger Codegen で自動生成したクライアントSDKを使う(iOS編)](http://blog.techium.jp/entry/2017/09/08/071650) by [kfurue](http://profile.hatena.ne.jp/kfurue/) @@ -928,12 +928,12 @@ Swagger Codegen core team members are contributors who have been making signific | Languages | Core Team (join date) | |:-------------|:-------------| | ActionScript | | -| C++ | | +| C++ | | | C# | @jimschubert (2016/05/01) | | Clojure | @xhh (2016/05/01) | -| Dart | | -| Groovy | | -| Go | @guohuang (2016/05/01) @neilotoole (2016/05/01) | +| Dart | | +| Groovy | | +| Go | @guohuang (2016/05/01) @neilotoole (2016/05/01) | | Java | @cbornet (2016/05/01) @xhh (2016/05/01) @epaul (2016/06/04) | | Java (Spring Cloud) | @cbornet (2016/07/19) | | Kotlin | @jimschubert (2016/05/01) | @@ -960,7 +960,7 @@ Swagger Codegen core team members are contributors who have been making signific | Java JAX-RS | | | Java Play Framework | | | NancyFX | | -| NodeJS | @kolyjjj (2016/05/01) | +| NodeJS | @kolyjjj (2016/05/01) | | PHP Lumen | @abcsun (2016/05/01) | | PHP Silex | | | PHP Slim | | @@ -1010,7 +1010,7 @@ Here is a list of template creators: * R: @ramnov * Rust: @farcaller * Rust (rust-server): @metaswitch - * Scala (scalaz & http4s): @tbrown1979 + * Scala (scalaz & http4s): @tbrown1979 * Swift: @tkqubo * Swift 3: @hexelon * Swift 4: @ehyche @@ -1018,7 +1018,7 @@ Here is a list of template creators: * TypeScript (Angular1): @mhardorf * TypeScript (Fetch): @leonyu * TypeScript (Angular2): @roni-frantchi - * TypeScript (jQuery): @bherila + * TypeScript (jQuery): @bherila * Server Stubs * C# ASP.NET5: @jimschubert * C# NancyFX: @mstefaniuk @@ -1083,7 +1083,7 @@ If you want to join the committee, please kindly apply by sending an email to wi | Android | @jaz-ah (2017/09) | | Apex | | | Bash | @frol (2017/07) @bkryza (2017/08) @kenjones-cisco (2017/09) | -| C++ | @ravinikam (2017/07) @stkrwork (2017/07) @fvarose (2017/11) | +| C++ | @ravinikam (2017/07) @stkrwork (2017/07) @fvarose (2017/11) | | C# | @mandrean (2017/08) @jimschubert (2017/09) | | Clojure | | | Dart | @ircecho (2017/07) | @@ -1103,7 +1103,7 @@ If you want to join the committee, please kindly apply by sending an email to wi | Python | @taxpon (2017/07) @frol (2017/07) @mbohlool (2017/07) @cbornet (2017/09) @kenjones-cisco (2017/11)| | R | | | Ruby | @cliffano (2017/07) @zlx (2017/09) | -| Rust | @frol (2017/07) @farcaller (2017/08) | +| Rust | @frol (2017/07) @farcaller (2017/08) | | Scala | @clasnake (2017/07) @jimschubert (2017/09) | | Swift | @jgavris (2017/07) @ehyche (2017/08) @Edubits (2017/09) @jaz-ah (2017/09) | | TypeScript | @TiFu (2017/07) @taxpon (2017/07) @sebastianhaas (2017/07) @kenisteward (2017/07) @Vrolijkx (2017/09) | diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/RustServerCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/RustServerCodegen.java index 020b78f2dd8..214cfc5c0b3 100755 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/RustServerCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/RustServerCodegen.java @@ -34,6 +34,7 @@ public class RustServerCodegen extends DefaultCodegen implements CodegenConfig { private HashMap modelXmlNames = new HashMap(); protected String apiVersion = "1.0.0"; + protected String serverHost = "localhost"; protected int serverPort = 8080; protected String projectName = "swagger-server"; protected String apiPath = "rust-server"; @@ -148,7 +149,6 @@ public class RustServerCodegen extends DefaultCodegen implements CodegenConfig { * are available in models, apis, and supporting files */ additionalProperties.put("apiVersion", apiVersion); - additionalProperties.put("serverPort", serverPort); additionalProperties.put("apiPath", apiPath); /* @@ -168,6 +168,7 @@ public class RustServerCodegen extends DefaultCodegen implements CodegenConfig { supportingFiles.add(new SupportingFile("example-server.mustache", "examples", "server.rs")); supportingFiles.add(new SupportingFile("example-client.mustache", "examples", "client.rs")); supportingFiles.add(new SupportingFile("example-server_lib.mustache", "examples/server_lib", "mod.rs")); + supportingFiles.add(new SupportingFile("example-server_server.mustache", "examples/server_lib", "server.rs")); 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")); @@ -257,6 +258,23 @@ public class RustServerCodegen extends DefaultCodegen implements CodegenConfig { versionComponents.add("0"); } info.setVersion(StringUtils.join(versionComponents, ".")); + + String host = swagger.getHost(); + if (host != null) { + String[] parts = host.split(":"); + if (parts.length > 1) { + serverHost = parts[0]; + try { + serverPort = Integer.valueOf(parts[1]); + } catch (NumberFormatException e) { + LOGGER.warn("Port of Swagger host is not an integer : " + host, e); + } + } else { + serverHost = host; + } + } + additionalProperties.put("serverHost", serverHost); + additionalProperties.put("serverPort", serverPort); } @Override @@ -512,6 +530,7 @@ public class RustServerCodegen extends DefaultCodegen implements CodegenConfig { LOGGER.debug("No consumes defined in operation. Using global consumes (" + swagger.getConsumes() + ") for " + op.operationId); } + boolean consumesPlainText = false; boolean consumesXml = false; // if "consumes" is defined (per operation or using global definition) if (consumes != null && !consumes.isEmpty()) { @@ -523,6 +542,8 @@ public class RustServerCodegen extends DefaultCodegen implements CodegenConfig { if (mimeType.startsWith("Application/Xml")) { additionalProperties.put("usesXml", true); consumesXml = true; + } else if (mimeType.startsWith("Text/Plain")) { + consumesPlainText = true; } mediaType.put("mediaType", mimeType); @@ -545,6 +566,7 @@ public class RustServerCodegen extends DefaultCodegen implements CodegenConfig { } boolean producesXml = false; + boolean producesPlainText = false; if (produces != null && !produces.isEmpty()) { List> c = new ArrayList>(); for (String key : produces) { @@ -554,6 +576,8 @@ public class RustServerCodegen extends DefaultCodegen implements CodegenConfig { if (mimeType.startsWith("Application/Xml")) { additionalProperties.put("usesXml", true); producesXml = true; + } else if (mimeType.startsWith("Text/Plain")) { + producesPlainText = true; } mediaType.put("mediaType", mimeType); @@ -572,9 +596,14 @@ public class RustServerCodegen extends DefaultCodegen implements CodegenConfig { op.bodyParam.vendorExtensions.put("model_key", key); } + // Default to consuming json op.bodyParam.vendorExtensions.put("uppercase_operation_id", underscore(op.operationId).toUpperCase()); if (consumesXml) { op.bodyParam.vendorExtensions.put("consumesXml", true); + } else if (consumesPlainText) { + op.bodyParam.vendorExtensions.put("consumesPlainText", true); + } else { + op.bodyParam.vendorExtensions.put("consumesJson", true); } } @@ -586,8 +615,13 @@ public class RustServerCodegen extends DefaultCodegen implements CodegenConfig { param.vendorExtensions.put("uppercase_operation_id", underscore(op.operationId).toUpperCase()); + // Default to producing json if nothing else is specified if (consumesXml) { param.vendorExtensions.put("consumesXml", true); + } else if (consumesPlainText) { + param.vendorExtensions.put("consumesPlainText", true); + } else { + param.vendorExtensions.put("consumesJson", true); } } for (CodegenParameter param : op.headerParams) { @@ -601,8 +635,13 @@ public class RustServerCodegen extends DefaultCodegen implements CodegenConfig { if (rsp.dataType != null) { rsp.vendorExtensions.put("uppercase_data_type", (rsp.dataType.replace("models::", "")).toUpperCase()); + // Default to producing json if nothing else is specified if (producesXml) { rsp.vendorExtensions.put("producesXml", true); + } else if (producesPlainText) { + rsp.vendorExtensions.put("producesPlainText", true); + } else { + rsp.vendorExtensions.put("producesJson", true); } // Check whether we're returning an object with a defined XML namespace. diff --git a/modules/swagger-codegen/src/main/resources/rust-server/Cargo.mustache b/modules/swagger-codegen/src/main/resources/rust-server/Cargo.mustache index 344071baca7..9e5b566b787 100644 --- a/modules/swagger-codegen/src/main/resources/rust-server/Cargo.mustache +++ b/modules/swagger-codegen/src/main/resources/rust-server/Cargo.mustache @@ -13,12 +13,19 @@ client = ["serde_json", {{#usesXml}}"serde-xml-rs", {{/usesXml}}"serde_ignored", server = ["serde_json", {{#usesXml}}"serde-xml-rs", {{/usesXml}}"serde_ignored", "hyper", "iron", "router", "bodyparser", "urlencoded", "uuid"{{#apiHasFile}}, "multipart"{{/apiHasFile}}] [dependencies] -bodyparser = {version = "0.7", optional = true} +# Required by example server. +# chrono = { version = "0.4", features = ["serde"] } futures = "0.1" hyper = {version = "0.10", optional = true} hyper-openssl = {version = "0.2", optional = true } iron = {version = "0.5", optional = true} +swagger = "0.7" + +# Not required by example server. +# +bodyparser = {version = "0.7", optional = true} +url = "1.5" lazy_static = "0.2" log = "0.3.0" multipart = {version = "0.13", optional = true} @@ -27,7 +34,6 @@ serde = "1.0" serde_derive = "1.0" serde_ignored = {version = "0.0.4", optional = true} serde_json = {version = "1.0", optional = true} -swagger = "0.7" urlencoded = {version = "0.5", optional = true} uuid = {version = "0.5", optional = true, features = ["serde", "v4"]} # ToDo: this should be updated to point at the official crate once @@ -36,3 +42,4 @@ uuid = {version = "0.5", optional = true, features = ["serde", "v4"]} [dev-dependencies] clap = "2.25" +error-chain = "0.11" diff --git a/modules/swagger-codegen/src/main/resources/rust-server/README.mustache b/modules/swagger-codegen/src/main/resources/rust-server/README.mustache index c790d7d68b0..6e220ec9c2c 100644 --- a/modules/swagger-codegen/src/main/resources/rust-server/README.mustache +++ b/modules/swagger-codegen/src/main/resources/rust-server/README.mustache @@ -20,6 +20,22 @@ To see how to make this your own, look here: For more information, please visit [{{{infoUrl}}}]({{{infoUrl}}}) {{/infoUrl}} +This autogenerated project defines an API crate `{{packageName}}` which contains: +* An `Api` trait defining the API in Rust. +* Data types representing the underlying data model. +* A `Client` type which implements `Api` and issues HTTP requests for each operation. +* A router which accepts HTTP requests and invokes the appropriate `Api` method for each operation. + +It also contains an example server and client which make use of `{{packageName}}`: +* The example server starts up a web server using the `{{packageName}}` router, + and supplies a trivial implementation of `Api` which returns failure for every operation. +* The example client provides a CLI which lets you invoke any single operation on the + `{{packageName}}` client by passing appropriate arguments on the command line. + +You can use the example server and client as a basis for your own code. +See below for [more detail on implementing a server](#writing-a-server). + + ## Examples Run examples with: @@ -57,3 +73,38 @@ cargo run --example server -- --https This will use the keys/certificates from the examples directory. Note that the server chain is signed with `CN=localhost`. + + +## Writing a server + +The server example is designed to form the basis for implementing your own server. Simply follow these steps. + +* Set up a new Rust project, e.g., with `cargo init --bin`. +* Insert `{{packageName}}` into the `members` array under [workspace] in the root `Cargo.toml`, e.g., `members = [ "{{packageName}}" ]`. +* Add `{{packageName}} = {version = "{{appVersion}}", path = "{{packageName}}"}` under `[dependencies]` in the root `Cargo.toml`. +* Copy the `[dependencies]` and `[dev-dependencies]` from `{{packageName}}/Cargo.toml` into the root `Cargo.toml`'s `[dependencies]` section. + * Copy all of the `[dev-dependencies]`, but only the `[dependencies]` that are required by the example server. These should be clearly indicated by comments. + * Remove `"optional = true"` from each of these lines if present. + +Each autogenerated API will contain an implementation stub and main entry point, which should be copied into your project the first time: +``` +cp {{packageName}}/examples/server.rs src/main.rs +cp {{packageName}}/examples/server_lib/mod.rs src/lib.rs +cp {{packageName}}/examples/server_lib/server.rs src/server.rs +``` + +Now + +* From `src/main.rs`, remove the `mod server_lib;` line, and uncomment and fill in the `extern crate` line with the name of this server crate. +* Move the block of imports "required by the service library" from `src/main.rs` to `src/lib.rs` and uncomment. +* Change the `let server = server::Server {};` line to `let server = SERVICE_NAME::server().unwrap();` where `SERVICE_NAME` is the name of the server crate. +* Run `cargo build` to check it builds. +* Run `cargo fmt` to reformat the code. +* Commit the result before making any further changes (lest format changes get confused with your own updates). + +Now replace the implementations in `src/server.rs` with your own code as required. + +## Updating your server to track API changes + +Later, if the API changes, you can copy new sections from the autogenerated API stub into your implementation. +Alternatively, implement the now-missing methods based on the compiler's error messages. diff --git a/modules/swagger-codegen/src/main/resources/rust-server/client.mustache b/modules/swagger-codegen/src/main/resources/rust-server/client.mustache index c168f502698..034318a0d69 100644 --- a/modules/swagger-codegen/src/main/resources/rust-server/client.mustache +++ b/modules/swagger-codegen/src/main/resources/rust-server/client.mustache @@ -1,6 +1,7 @@ #![allow(unused_extern_crates)] extern crate hyper_openssl; extern crate chrono; +extern crate url; {{#apiHasFile}}extern crate multipart;{{/apiHasFile}} {{#apiHasFile}}use multipart::client::lazy::Multipart;{{/apiHasFile}} @@ -11,6 +12,7 @@ use hyper::header::{Headers, ContentType}; use hyper::mime::{Mime, TopLevel, SubLevel, Attr, Value}; use hyper::Url; use self::hyper_openssl::openssl; +use self::url::percent_encoding::{utf8_percent_encode, PATH_SEGMENT_ENCODE_SET, QUERY_ENCODE_SET}; use futures; use futures::{Future, Stream}; use futures::{future, stream}; @@ -174,7 +176,11 @@ impl Api for Client { {{/required}}{{^required}} let query_{{paramName}} = param_{{paramName}}.map_or_else(String::new, |query| format!("{{baseName}}={{=<% %>=}}{<% paramName %>}<%={{ }}=%>&", {{paramName}}=query{{#isListContainer}}.join(","){{/isListContainer}}{{^isListContainer}}.to_string(){{/isListContainer}})); {{/required}}{{/queryParams}} - let url = format!("{}{{basePathWithoutHost}}{{path}}?{{#queryParams}}{{=<% %>=}}{<% paramName %>}<%={{ }}=%>{{/queryParams}}", self.base_path{{#pathParams}}, {{baseName}}=param_{{paramName}}.to_string(){{/pathParams}}{{#queryParams}}, {{paramName}}=query_{{paramName}}{{/queryParams}}); + let url = format!( + "{}{{basePathWithoutHost}}{{path}}{{#queryParams}}{{#-first}}?{{/-first}}{{=<% %>=}}{<% paramName %>}<%={{ }}=%>{{/queryParams}}", + self.base_path{{#pathParams}}, {{baseName}}=utf8_percent_encode(¶m_{{paramName}}.to_string(), PATH_SEGMENT_ENCODE_SET){{/pathParams}}{{#queryParams}}, + {{paramName}}=utf8_percent_encode(&query_{{paramName}}, QUERY_ENCODE_SET){{/queryParams}} + ); {{#vendorExtensions}}{{#hasFile}} // Form data body let mut multipart = Multipart::new();{{/hasFile}}{{/vendorExtensions}}{{#formParams}}{{#isFile}} @@ -199,22 +205,23 @@ impl Api for Client { let boundary = fields.boundary(); let multipart_header = Mime(TopLevel::Multipart, SubLevel::FormData, vec![(Attr::Boundary, Value::Ext(boundary.to_string()))]);{{/hasFile}}{{/vendorExtensions}}{{#bodyParam}}{{#-first}} // Body parameter -{{/-first}}{{#required}}{{#vendorExtensions}}{{#consumesXml}} +{{/-first}}{{#vendorExtensions}}{{#required}}{{#consumesPlainText}} let body = param_{{paramName}};{{/consumesPlainText}}{{#consumesXml}} {{^has_namespace}} let body = serde_xml_rs::to_string(¶m_{{paramName}}).expect("impossible to fail to serialize");{{/has_namespace}}{{#has_namespace}} let mut namespaces = BTreeMap::new(); // An empty string is used to indicate a global namespace in xmltree. namespaces.insert("".to_string(), models::namespaces::{{uppercase_data_type}}.clone()); - let body = serde_xml_rs::to_string_with_namespaces(¶m_{{paramName}}, namespaces).expect("impossible to fail to serialize");{{/has_namespace}}{{/consumesXml}}{{^consumesXml}} - let body = serde_json::to_string(¶m_{{paramName}}).expect("impossible to fail to serialize");{{/consumesXml}}{{/vendorExtensions}} -{{/required}}{{^required}} let body = param_{{paramName}}.map(|ref body| { -{{#vendorExtensions}}{{#consumesXml}} + let body = serde_xml_rs::to_string_with_namespaces(¶m_{{paramName}}, namespaces).expect("impossible to fail to serialize");{{/has_namespace}}{{/consumesXml}}{{#consumesJson}} + let body = serde_json::to_string(¶m_{{paramName}}).expect("impossible to fail to serialize");{{/consumesJson}} +{{/required}}{{^required}}{{#consumesPlainText}} let body = param_{{paramName}}; +{{/consumesPlainText}}{{^consumesPlainText}} let body = param_{{paramName}}.map(|ref body| { +{{#consumesXml}} {{^has_namespace}} serde_xml_rs::to_string(body).expect("impossible to fail to serialize"){{/has_namespace}}{{#has_namespace}} let mut namespaces = BTreeMap::new(); // An empty string is used to indicate a global namespace in xmltree. namespaces.insert("".to_string(), models::namespaces::{{uppercase_data_type}}.clone()); - serde_xml_rs::to_string_with_namespaces(body, namespaces).expect("impossible to fail to serialize"){{/has_namespace}}{{/consumesXml}}{{^consumesXml}} - serde_json::to_string(body).expect("impossible to fail to serialize"){{/consumesXml}}{{/vendorExtensions}} - });{{/required}}{{/bodyParam}} + serde_xml_rs::to_string_with_namespaces(body, namespaces).expect("impossible to fail to serialize"){{/has_namespace}}{{/consumesXml}}{{#consumesJson}} + serde_json::to_string(body).expect("impossible to fail to serialize"){{/consumesJson}} + });{{/consumesPlainText}}{{/required}}{{/vendorExtensions}}{{/bodyParam}} let hyper_client = (self.hyper_client)(); let request = hyper_client.request(hyper::method::Method::{{#vendorExtensions}}{{HttpMethod}}{{/vendorExtensions}}, &url); let mut custom_headers = hyper::header::Headers::new(); @@ -252,13 +259,12 @@ impl Api for Client { // ToDo: this will move to swagger-rs and become a standard From conversion trait // once https://github.com/RReverser/serde-xml-rs/pull/45 is accepted upstream let body = serde_xml_rs::from_str::<{{{dataType}}}>(&buf) - .map_err(|e| ApiError(format!("Response body did not match the schema: {}", e)))?;{{/producesXml}}{{^producesXml}} - let body = serde_json::from_str::<{{{dataType}}}>(&buf)?; -{{/producesXml}}{{/vendorExtensions}}{{/isFile}}{{#isFile}} let mut buf = Vec::new(); + .map_err(|e| ApiError(format!("Response body did not match the schema: {}", e)))?;{{/producesXml}}{{#producesPlainText}} + let body = buf;{{/producesPlainText}}{{#producesJson}} + let body = serde_json::from_str::<{{{dataType}}}>(&buf)?;{{/producesJson}}{{/vendorExtensions}}{{/isFile}}{{#isFile}} + let mut buf = Vec::new(); response.read_to_end(&mut buf).map_err(|e| ApiError(format!("Received error reading response: {}", e)))?; - let body = Box::new(stream::once(Ok(buf)));{{/isFile}} -{{/dataType}} - + let body = Box::new(stream::once(Ok(buf)));{{/isFile}}{{/dataType}} {{#headers}} header! { (Response{{nameInCamelCase}}, "{{baseName}}") => [{{{datatype}}}] } let response_{{name}} = response.headers.get::().ok_or_else(|| "Required response header {{baseName}} for response {{code}} was not found.")?; {{/headers}} diff --git a/modules/swagger-codegen/src/main/resources/rust-server/example-client.mustache b/modules/swagger-codegen/src/main/resources/rust-server/example-client.mustache index 2d76fcb47d4..e5b59e5c4f9 100644 --- a/modules/swagger-codegen/src/main/resources/rust-server/example-client.mustache +++ b/modules/swagger-codegen/src/main/resources/rust-server/example-client.mustache @@ -30,14 +30,31 @@ fn main() { .arg(Arg::with_name("https") .long("https") .help("Whether to use HTTPS or not")) + .arg(Arg::with_name("host") + .long("host") + .takes_value(true) + .default_value("{{serverHost}}") + .help("Hostname to contact")) + .arg(Arg::with_name("port") + .long("port") + .takes_value(true) + .default_value("{{serverPort}}") + .help("Port to contact")) .get_matches(); - let client = if matches.is_present("https") { + let is_https = matches.is_present("https"); + let base_url = format!("{}://{}:{}", + if is_https { "https" } else { "http" }, + matches.value_of("host").unwrap(), + matches.value_of("port").unwrap()); + let client = if is_https { // Using Simple HTTPS - {{externCrateName}}::Client::try_new_https("https://localhost:{{serverPort}}", "examples/ca.pem").expect("Failed to create HTTPS client") + {{externCrateName}}::Client::try_new_https(&base_url, "examples/ca.pem") + .expect("Failed to create HTTPS client") } else { // Using HTTP - {{externCrateName}}::Client::try_new_http("http://localhost:{{serverPort}}").expect("Failed to create HTTP client") + {{externCrateName}}::Client::try_new_http(&base_url) + .expect("Failed to create HTTP client") }; // Using a non-default `Context` is not required; this is just an example! diff --git a/modules/swagger-codegen/src/main/resources/rust-server/example-server.mustache b/modules/swagger-codegen/src/main/resources/rust-server/example-server.mustache index 7da74ee26e7..96e6ff4c48d 100644 --- a/modules/swagger-codegen/src/main/resources/rust-server/example-server.mustache +++ b/modules/swagger-codegen/src/main/resources/rust-server/example-server.mustache @@ -1,11 +1,22 @@ +//! Main binary entry point for {{externCrateName}} implementation. + #![allow(missing_docs)] +// Imports required by this file. +// extern crate ; extern crate {{externCrateName}}; +extern crate swagger; extern crate iron; -extern crate futures; extern crate hyper_openssl; extern crate clap; -extern crate swagger; + +// Imports required by server library. +// extern crate {{externCrateName}}; +// extern crate swagger; +extern crate futures; +extern crate chrono; +#[macro_use] +extern crate error_chain; use hyper_openssl::OpensslServer; use hyper_openssl::openssl::x509::X509_FILETYPE_PEM; @@ -15,7 +26,6 @@ use clap::{App, Arg}; use iron::{Iron, Chain}; use swagger::auth::AllowAllMiddleware; -// Import the module that defines the Server struct. mod server_lib; /// Builds an SSL implementation for Simple HTTPS from some hard-coded file names @@ -39,7 +49,7 @@ fn main() { .help("Whether to use HTTPS or not")) .get_matches(); - let server = server_lib::Server{}; + let server = server_lib::server().unwrap(); let router = {{externCrateName}}::router(server); let mut chain = Chain::new(router); diff --git a/modules/swagger-codegen/src/main/resources/rust-server/example-server_lib.mustache b/modules/swagger-codegen/src/main/resources/rust-server/example-server_lib.mustache index b9288a38cc8..7e85ec86199 100644 --- a/modules/swagger-codegen/src/main/resources/rust-server/example-server_lib.mustache +++ b/modules/swagger-codegen/src/main/resources/rust-server/example-server_lib.mustache @@ -1,31 +1,14 @@ -#![allow(missing_docs, unused_extern_crates)] -extern crate chrono; -extern crate swagger; +//! Main library entry point for {{externCrateName}} implementation. -use futures::{self, Future}; -{{#apiHasFile}}use futures::Stream;{{/apiHasFile}} +mod server; -#[allow(unused_imports)] -use std::collections::HashMap; -{{#apiHasFile}}use std::io::Error;{{/apiHasFile}} - -use {{externCrateName}}::{Api, ApiError, Context{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}, - {{operationId}}Response{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}} -}; -#[allow(unused_imports)] -use {{externCrateName}}::models; - -#[derive(Copy, Clone)] -pub struct Server; - -impl Api for Server { -{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}} -{{#summary}} /// {{{summary}}}{{/summary}} - fn {{#vendorExtensions}}{{operation_id}}{{/vendorExtensions}}(&self{{#allParams}}, {{paramName}}: {{^required}}{{#isFile}}Box{{#isFile}}, Error=Error> + Send>{{/isFile}}{{/required}}{{/allParams}}, context: &Context) -> Box + Send> { - let context = context.clone(); - println!("{{#vendorExtensions}}{{operation_id}}{{/vendorExtensions}}({{#allParams}}{{^isFile}}{{#vendorExtensions}}{{{formatString}}}{{/vendorExtensions}}{{#hasMore}}, {{/hasMore}}{{/isFile}}{{/allParams}}) - X-Span-ID: {:?}"{{#allParams}}{{^isFile}}, {{paramName}}{{/isFile}}{{/allParams}}, context.x_span_id.unwrap_or(String::from("")).clone());{{#allParams}}{{#isFile}} - let _ = {{paramName}}; //Suppresses unused param warning{{/isFile}}{{/allParams}} - Box::new(futures::failed("Generic failure".into())) - } -{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}} +mod errors { + error_chain!{} +} + +pub use self::errors::*; + +/// Instantiate a new server. +pub fn server() -> Result { + Ok(server::Server {}) } diff --git a/modules/swagger-codegen/src/main/resources/rust-server/example-server_server.mustache b/modules/swagger-codegen/src/main/resources/rust-server/example-server_server.mustache new file mode 100644 index 00000000000..8b8bf033927 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/rust-server/example-server_server.mustache @@ -0,0 +1,30 @@ +//! Server implementation of {{externCrateName}}. + +#![allow(unused_imports)] + +use futures::{self, Future}; +use chrono; +{{#apiHasFile}}use futures::Stream;{{/apiHasFile}} +use std::collections::HashMap; +{{#apiHasFile}}use std::io::Error;{{/apiHasFile}} +use swagger; + +use {{externCrateName}}::{Api, ApiError, Context{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}, + {{operationId}}Response{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}} +}; +use {{externCrateName}}::models; + +#[derive(Copy, Clone)] +pub struct Server; + +impl Api for Server { +{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}} +{{#summary}} /// {{{summary}}}{{/summary}} + fn {{#vendorExtensions}}{{operation_id}}{{/vendorExtensions}}(&self{{#allParams}}, {{paramName}}: {{^required}}{{#isFile}}Box{{#isFile}}, Error=Error> + Send>{{/isFile}}{{/required}}{{/allParams}}, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("{{#vendorExtensions}}{{operation_id}}{{/vendorExtensions}}({{#allParams}}{{^isFile}}{{#vendorExtensions}}{{{formatString}}}{{/vendorExtensions}}{{#hasMore}}, {{/hasMore}}{{/isFile}}{{/allParams}}) - X-Span-ID: {:?}"{{#allParams}}{{^isFile}}, {{paramName}}{{/isFile}}{{/allParams}}, context.x_span_id.unwrap_or(String::from("")).clone());{{#allParams}}{{#isFile}} + let _ = {{paramName}}; //Suppresses unused param warning{{/isFile}}{{/allParams}} + Box::new(futures::failed("Generic failure".into())) + } +{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}} +} diff --git a/modules/swagger-codegen/src/main/resources/rust-server/models.mustache b/modules/swagger-codegen/src/main/resources/rust-server/models.mustache index 4c31931dde0..51f25a2accd 100755 --- a/modules/swagger-codegen/src/main/resources/rust-server/models.mustache +++ b/modules/swagger-codegen/src/main/resources/rust-server/models.mustache @@ -16,7 +16,7 @@ use swagger; /// which helps with FFI. #[allow(non_camel_case_types)] #[repr(C)] -#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Serialize, Deserialize)]{{#xmlName}} +#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Serialize, Deserialize, Eq, Ord)]{{#xmlName}} #[serde(rename = "{{xmlName}}")]{{/xmlName}} pub enum {{classname}} { {{#allowableValues}}{{#enumVars}} #[serde(rename = {{{value}}})] diff --git a/modules/swagger-codegen/src/main/resources/rust-server/server.mustache b/modules/swagger-codegen/src/main/resources/rust-server/server.mustache index 6091527455f..8343b782ae9 100644 --- a/modules/swagger-codegen/src/main/resources/rust-server/server.mustache +++ b/modules/swagger-codegen/src/main/resources/rust-server/server.mustache @@ -33,7 +33,8 @@ use std::io::Error; #[allow(unused_imports)] use std::collections::BTreeSet; -use swagger::auth::{Authorization, AuthData, Scopes}; +pub use swagger::auth::Authorization; +use swagger::auth::{AuthData, Scopes}; use swagger::{ApiError, Context, XSpanId}; use {Api{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}, @@ -156,15 +157,15 @@ fn add_routes(router: &mut Router, api: T) where T: Api + Send + Sync + Clone // values, rather than causing a 400 response). Produce warning header and logs for // any unused fields. {{/-first}}{{#required}} - let param_{{paramName}}_raw = req.get::().map_err(|e| Response::with((status::BadRequest, format!("Couldn't parse body parameter {{baseName}} - not valid UTF-8: {}", e))))?; + let param_{{paramName}} = req.get::().map_err(|e| Response::with((status::BadRequest, format!("Couldn't parse body parameter {{baseName}} - not valid UTF-8: {}", e))))?; {{/required}}{{^required}} - let param_{{paramName}}_raw = req.get::().unwrap_or(None); -{{/required}} + let param_{{paramName}} = req.get::().unwrap_or(None); +{{/required}}{{#vendorExtensions}}{{^consumesPlainText}} let mut unused_elements = Vec::new(); - let param_{{paramName}} = if let Some(param_{{paramName}}_raw) = param_{{paramName}}_raw { {{#vendorExtensions}}{{#consumesXml}} - let deserializer = &mut serde_xml_rs::de::Deserializer::new_from_reader(param_{{paramName}}_raw.as_bytes());{{/consumesXml}}{{^consumesXml}} - let deserializer = &mut serde_json::Deserializer::from_str(¶m_{{paramName}}_raw);{{/consumesXml}}{{/vendorExtensions}} + let param_{{paramName}} = if let Some(param_{{paramName}}_raw) = param_{{paramName}} { {{#consumesXml}} + let deserializer = &mut serde_xml_rs::de::Deserializer::new_from_reader(param_{{paramName}}_raw.as_bytes());{{/consumesXml}}{{#consumesJson}} + let deserializer = &mut serde_json::Deserializer::from_str(¶m_{{paramName}}_raw);{{/consumesJson}} let param_{{paramName}}: Option<{{{dataType}}}> = serde_ignored::deserialize(deserializer, |path| { warn!("Ignoring unknown field in body: {}", path); @@ -174,7 +175,7 @@ fn add_routes(router: &mut Router, api: T) where T: Api + Send + Sync + Clone param_{{paramName}} } else { None - };{{#required}} + };{{/consumesPlainText}}{{/vendorExtensions}}{{#required}} let param_{{paramName}} = param_{{paramName}}.ok_or_else(|| Response::with((status::BadRequest, "Missing required body parameter {{baseName}}".to_string())))?{{/required}}; {{/bodyParams}} @@ -225,13 +226,14 @@ fn add_routes(router: &mut Router, api: T) where T: Api + Send + Sync + Clone {{#responses}} {{operationId}}Response::{{message}}{{#dataType}}{{^headers}}(body){{/headers}}{{#headers}}{{#-first}}{ body{{/-first}}{{/headers}}{{/dataType}}{{#headers}}{{#-first}}{{^dataType}}{ {{/dataType}}{{#dataType}}, {{/dataType}}{{/-first}}{{^-first}}, {{/-first}}{{name}}{{#-last}} }{{/-last}}{{/headers}} => { {{^isFile}} -{{#dataType}}{{#vendorExtensions}}{{#producesXml}} +{{#dataType}}{{#vendorExtensions}}{{#producesPlainText}} let body_string = body; +{{/producesPlainText}}{{#producesXml}} {{^has_namespace}} let body_string = serde_xml_rs::to_string(&body).expect("impossible to fail to serialize");{{/has_namespace}}{{#has_namespace}} let mut namespaces = BTreeMap::new(); // An empty string is used to indicate a global namespace in xmltree. namespaces.insert("".to_string(), models::namespaces::{{uppercase_data_type}}.clone()); - let body_string = serde_xml_rs::to_string_with_namespaces(&body, namespaces).expect("impossible to fail to serialize");{{/has_namespace}}{{/producesXml}}{{^producesXml}} - let body_string = serde_json::to_string(&body).expect("impossible to fail to serialize");{{/producesXml}}{{/vendorExtensions}}{{/dataType}} + let body_string = serde_xml_rs::to_string_with_namespaces(&body, namespaces).expect("impossible to fail to serialize");{{/has_namespace}}{{/producesXml}}{{#producesJson}} + let body_string = serde_json::to_string(&body).expect("impossible to fail to serialize");{{/producesJson}}{{/vendorExtensions}}{{/dataType}} let mut response = Response::with((status::Status::from_u16({{code}}){{#dataType}}, body_string{{/dataType}}));{{/isFile}}{{#isFile}} body.fold(Vec::new(), |mut body, chunk| { body.extend(chunk.iter()); @@ -257,9 +259,9 @@ fn add_routes(router: &mut Router, api: T) where T: Api + Send + Sync + Clone {{#dataType}}{{#isFile}} {{/isFile}} response.headers.set(ContentType(mimetypes::responses::{{#vendorExtensions}}{{uppercase_operation_id}}_{{uppercase_message}}{{/vendorExtensions}}.clone()));{{/dataType}} {{/-first}}{{/produces}} {{#isFile}} {{/isFile}} context.x_span_id.as_ref().map(|header| response.headers.set(XSpanId(header.clone()))); -{{#bodyParams}} if !unused_elements.is_empty() { +{{#bodyParams}}{{#vendorExtensions}}{{^consumesPlainText}} if !unused_elements.is_empty() { response.headers.set(Warning(format!("Ignoring unknown fields in body: {:?}", unused_elements))); - }{{/bodyParams}} + }{{/consumesPlainText}}{{/vendorExtensions}}{{/bodyParams}} {{^isFile}}Ok(response){{/isFile}}{{#isFile}} response }){{/isFile}} }, diff --git a/modules/swagger-codegen/src/test/resources/2_0/petstore-with-fake-endpoints-models-for-testing.yaml b/modules/swagger-codegen/src/test/resources/2_0/petstore-with-fake-endpoints-models-for-testing.yaml index c947e95d926..d84d621f687 100644 --- a/modules/swagger-codegen/src/test/resources/2_0/petstore-with-fake-endpoints-models-for-testing.yaml +++ b/modules/swagger-codegen/src/test/resources/2_0/petstore-with-fake-endpoints-models-for-testing.yaml @@ -957,7 +957,7 @@ securityDefinitions: api_key_query: type: apiKey name: api_key_query - in: query + in: query http_basic_test: type: basic definitions: diff --git a/samples/client/petstore/rust2/examples/server_lib/server.rs b/samples/client/petstore/rust2/examples/server_lib/server.rs new file mode 100644 index 00000000000..17ed199a74e --- /dev/null +++ b/samples/client/petstore/rust2/examples/server_lib/server.rs @@ -0,0 +1,294 @@ +//! Server implementation of petstore_api. + +#![allow(unused_imports)] + +use futures::{self, Future}; +use chrono; +use futures::Stream; +use std::collections::HashMap; +use std::io::Error; +use swagger; + +use petstore_api::{Api, ApiError, Context, + TestSpecialTagsResponse, + GetXmlFeaturesResponse, + PostPlainTextResponse, + PostXmlFeaturesResponse, + PutPlainTextResponse, + FakeOuterBooleanSerializeResponse, + FakeOuterCompositeSerializeResponse, + FakeOuterNumberSerializeResponse, + FakeOuterStringSerializeResponse, + TestClientModelResponse, + TestEndpointParametersResponse, + TestEnumParametersResponse, + TestJsonFormDataResponse, + TestClassnameResponse, + AddPetResponse, + DeletePetResponse, + FindPetsByStatusResponse, + FindPetsByTagsResponse, + GetPetByIdResponse, + UpdatePetResponse, + UpdatePetWithFormResponse, + UploadFileResponse, + DeleteOrderResponse, + GetInventoryResponse, + GetOrderByIdResponse, + PlaceOrderResponse, + CreateUserResponse, + CreateUsersWithArrayInputResponse, + CreateUsersWithListInputResponse, + DeleteUserResponse, + GetUserByNameResponse, + LoginUserResponse, + LogoutUserResponse, + UpdateUserResponse +}; +use petstore_api::models; + +#[derive(Copy, Clone)] +pub struct Server; + +impl Api for Server { + + /// To test special tags + fn test_special_tags(&self, body: models::Client, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("test_special_tags({:?}) - X-Span-ID: {:?}", body, context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + /// Get some XML + fn get_xml_features(&self, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("get_xml_features() - X-Span-ID: {:?}", context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + /// Post some plaintext + fn post_plain_text(&self, message: String, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("post_plain_text(\"{}\") - X-Span-ID: {:?}", message, context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + /// Post some xml + fn post_xml_features(&self, xml_object: models::XmlObject, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("post_xml_features({:?}) - X-Span-ID: {:?}", xml_object, context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + /// Put some optional plaintext + fn put_plain_text(&self, message: Option, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("put_plain_text({:?}) - X-Span-ID: {:?}", message, context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + + fn fake_outer_boolean_serialize(&self, body: Option, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("fake_outer_boolean_serialize({:?}) - X-Span-ID: {:?}", body, context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + + fn fake_outer_composite_serialize(&self, body: Option, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("fake_outer_composite_serialize({:?}) - X-Span-ID: {:?}", body, context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + + fn fake_outer_number_serialize(&self, body: Option, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("fake_outer_number_serialize({:?}) - X-Span-ID: {:?}", body, context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + + fn fake_outer_string_serialize(&self, body: Option, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("fake_outer_string_serialize({:?}) - X-Span-ID: {:?}", body, context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + /// To test \"client\" model + fn test_client_model(&self, body: models::Client, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("test_client_model({:?}) - X-Span-ID: {:?}", body, context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + /// Fake endpoint for testing various parameters 假端點 偽のエンドポイント 가짜 엔드 포인트 + fn test_endpoint_parameters(&self, number: f64, double: f64, pattern_without_delimiter: String, byte: swagger::ByteArray, integer: Option, int32: Option, int64: Option, float: Option, string: Option, binary: Option, date: Option>, date_time: Option>, password: Option, callback: Option, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("test_endpoint_parameters({}, {}, \"{}\", \"{:?}\", {:?}, {:?}, {:?}, {:?}, {:?}, {:?}, {:?}, {:?}, {:?}, {:?}) - X-Span-ID: {:?}", number, double, pattern_without_delimiter, byte, integer, int32, int64, float, string, binary, date, date_time, password, callback, context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + /// To test enum parameters + fn test_enum_parameters(&self, enum_form_string_array: Option<&Vec>, enum_form_string: Option, enum_header_string_array: Option<&Vec>, enum_header_string: Option, enum_query_string_array: Option<&Vec>, enum_query_string: Option, enum_query_integer: Option, enum_query_double: Option, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("test_enum_parameters({:?}, {:?}, {:?}, {:?}, {:?}, {:?}, {:?}, {:?}) - X-Span-ID: {:?}", enum_form_string_array, enum_form_string, enum_header_string_array, enum_header_string, enum_query_string_array, enum_query_string, enum_query_integer, enum_query_double, context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + /// test json serialization of form data + fn test_json_form_data(&self, param: String, param2: String, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("test_json_form_data(\"{}\", \"{}\") - X-Span-ID: {:?}", param, param2, context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + /// To test class name in snake case + fn test_classname(&self, body: models::Client, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("test_classname({:?}) - X-Span-ID: {:?}", body, context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + /// Add a new pet to the store + fn add_pet(&self, body: models::Pet, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("add_pet({:?}) - X-Span-ID: {:?}", body, context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + /// Deletes a pet + fn delete_pet(&self, pet_id: i64, api_key: Option, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("delete_pet({}, {:?}) - X-Span-ID: {:?}", pet_id, api_key, context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + /// Finds Pets by status + fn find_pets_by_status(&self, status: &Vec, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("find_pets_by_status({:?}) - X-Span-ID: {:?}", status, context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + /// Finds Pets by tags + fn find_pets_by_tags(&self, tags: &Vec, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("find_pets_by_tags({:?}) - X-Span-ID: {:?}", tags, context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + /// Find pet by ID + fn get_pet_by_id(&self, pet_id: i64, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("get_pet_by_id({}) - X-Span-ID: {:?}", pet_id, context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + /// Update an existing pet + fn update_pet(&self, body: models::Pet, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("update_pet({:?}) - X-Span-ID: {:?}", body, context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + /// Updates a pet in the store with form data + fn update_pet_with_form(&self, pet_id: i64, name: Option, status: Option, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("update_pet_with_form({}, {:?}, {:?}) - X-Span-ID: {:?}", pet_id, name, status, context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + /// uploads an image + fn upload_file(&self, pet_id: i64, additional_metadata: Option, file: Box, Error=Error> + Send>>, Error=Error> + Send>, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("upload_file({}, {:?}, ) - X-Span-ID: {:?}", pet_id, additional_metadata, context.x_span_id.unwrap_or(String::from("")).clone()); + let _ = file; //Suppresses unused param warning + Box::new(futures::failed("Generic failure".into())) + } + + /// Delete purchase order by ID + fn delete_order(&self, order_id: String, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("delete_order(\"{}\") - X-Span-ID: {:?}", order_id, context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + /// Returns pet inventories by status + fn get_inventory(&self, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("get_inventory() - X-Span-ID: {:?}", context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + /// Find purchase order by ID + fn get_order_by_id(&self, order_id: i64, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("get_order_by_id({}) - X-Span-ID: {:?}", order_id, context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + /// Place an order for a pet + fn place_order(&self, body: models::Order, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("place_order({:?}) - X-Span-ID: {:?}", body, context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + /// Create user + fn create_user(&self, body: models::User, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("create_user({:?}) - X-Span-ID: {:?}", body, context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + /// Creates list of users with given input array + fn create_users_with_array_input(&self, body: &Vec, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("create_users_with_array_input({:?}) - X-Span-ID: {:?}", body, context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + /// Creates list of users with given input array + fn create_users_with_list_input(&self, body: &Vec, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("create_users_with_list_input({:?}) - X-Span-ID: {:?}", body, context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + /// Delete user + fn delete_user(&self, username: String, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("delete_user(\"{}\") - X-Span-ID: {:?}", username, context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + /// Get user by user name + fn get_user_by_name(&self, username: String, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("get_user_by_name(\"{}\") - X-Span-ID: {:?}", username, context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + /// Logs user into the system + fn login_user(&self, username: String, password: String, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("login_user(\"{}\", \"{}\") - X-Span-ID: {:?}", username, password, context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + /// Logs out current logged in user session + fn logout_user(&self, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("logout_user() - X-Span-ID: {:?}", context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + /// Updated user + fn update_user(&self, username: String, body: models::User, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("update_user(\"{}\", {:?}) - X-Span-ID: {:?}", username, body, context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + +} diff --git a/samples/server/petstore/rust-server/Cargo.toml b/samples/server/petstore/rust-server/Cargo.toml index 6896d352e75..f44d3cc5916 100644 --- a/samples/server/petstore/rust-server/Cargo.toml +++ b/samples/server/petstore/rust-server/Cargo.toml @@ -11,12 +11,19 @@ client = ["serde_json", "serde-xml-rs", "serde_ignored", "hyper", "hyper-openssl server = ["serde_json", "serde-xml-rs", "serde_ignored", "hyper", "iron", "router", "bodyparser", "urlencoded", "uuid", "multipart"] [dependencies] -bodyparser = {version = "0.7", optional = true} +# Required by example server. +# chrono = { version = "0.4", features = ["serde"] } futures = "0.1" hyper = {version = "0.10", optional = true} hyper-openssl = {version = "0.2", optional = true } iron = {version = "0.5", optional = true} +swagger = "0.7" + +# Not required by example server. +# +bodyparser = {version = "0.7", optional = true} +url = "1.5" lazy_static = "0.2" log = "0.3.0" multipart = {version = "0.13", optional = true} @@ -25,7 +32,6 @@ serde = "1.0" serde_derive = "1.0" serde_ignored = {version = "0.0.4", optional = true} serde_json = {version = "1.0", optional = true} -swagger = "0.7" urlencoded = {version = "0.5", optional = true} uuid = {version = "0.5", optional = true, features = ["serde", "v4"]} # ToDo: this should be updated to point at the official crate once @@ -34,3 +40,4 @@ serde-xml-rs = {git = "git://github.com/Metaswitch/serde-xml-rs.git" , branch = [dev-dependencies] clap = "2.25" +error-chain = "0.11" diff --git a/samples/server/petstore/rust-server/README.md b/samples/server/petstore/rust-server/README.md index a660a9f9b38..5033d421bca 100644 --- a/samples/server/petstore/rust-server/README.md +++ b/samples/server/petstore/rust-server/README.md @@ -13,7 +13,23 @@ To see how to make this your own, look here: [README](https://github.com/swagger-api/swagger-codegen/blob/master/README.md) - API version: 1.0.0 -- Build date: 2017-11-30T15:18:45.444Z +- Build date: 2017-12-07T17:18:10.454Z + +This autogenerated project defines an API crate `petstore_api` which contains: +* An `Api` trait defining the API in Rust. +* Data types representing the underlying data model. +* A `Client` type which implements `Api` and issues HTTP requests for each operation. +* A router which accepts HTTP requests and invokes the appropriate `Api` method for each operation. + +It also contains an example server and client which make use of `petstore_api`: +* The example server starts up a web server using the `petstore_api` router, + and supplies a trivial implementation of `Api` which returns failure for every operation. +* The example client provides a CLI which lets you invoke any single operation on the + `petstore_api` client by passing appropriate arguments on the command line. + +You can use the example server and client as a basis for your own code. +See below for [more detail on implementing a server](#writing-a-server). + ## Examples @@ -82,3 +98,38 @@ cargo run --example server -- --https This will use the keys/certificates from the examples directory. Note that the server chain is signed with `CN=localhost`. + + +## Writing a server + +The server example is designed to form the basis for implementing your own server. Simply follow these steps. + +* Set up a new Rust project, e.g., with `cargo init --bin`. +* Insert `petstore_api` into the `members` array under [workspace] in the root `Cargo.toml`, e.g., `members = [ "petstore_api" ]`. +* Add `petstore_api = {version = "1.0.0", path = "petstore_api"}` under `[dependencies]` in the root `Cargo.toml`. +* Copy the `[dependencies]` and `[dev-dependencies]` from `petstore_api/Cargo.toml` into the root `Cargo.toml`'s `[dependencies]` section. + * Copy all of the `[dev-dependencies]`, but only the `[dependencies]` that are required by the example server. These should be clearly indicated by comments. + * Remove `"optional = true"` from each of these lines if present. + +Each autogenerated API will contain an implementation stub and main entry point, which should be copied into your project the first time: +``` +cp petstore_api/examples/server.rs src/main.rs +cp petstore_api/examples/server_lib/mod.rs src/lib.rs +cp petstore_api/examples/server_lib/server.rs src/server.rs +``` + +Now + +* From `src/main.rs`, remove the `mod server_lib;` line, and uncomment and fill in the `extern crate` line with the name of this server crate. +* Move the block of imports "required by the service library" from `src/main.rs` to `src/lib.rs` and uncomment. +* Change the `let server = server::Server {};` line to `let server = SERVICE_NAME::server().unwrap();` where `SERVICE_NAME` is the name of the server crate. +* Run `cargo build` to check it builds. +* Run `cargo fmt` to reformat the code. +* Commit the result before making any further changes (lest format changes get confused with your own updates). + +Now replace the implementations in `src/server.rs` with your own code as required. + +## Updating your server to track API changes + +Later, if the API changes, you can copy new sections from the autogenerated API stub into your implementation. +Alternatively, implement the now-missing methods based on the compiler's error messages. diff --git a/samples/server/petstore/rust-server/api/swagger.yaml b/samples/server/petstore/rust-server/api/swagger.yaml index d560546927d..f0a6ec6e6bb 100644 --- a/samples/server/petstore/rust-server/api/swagger.yaml +++ b/samples/server/petstore/rust-server/api/swagger.yaml @@ -139,11 +139,11 @@ paths: type: "array" items: type: "string" - default: "available" enum: - "available" - "pending" - "sold" + default: "available" collectionFormat: "csv" formatString: "{:?}" example: "&Vec::new()" @@ -391,6 +391,7 @@ paths: uppercase_operation_id: "UPLOAD_FILE" uppercase_message: "SUCCESSFUL_OPERATION" uppercase_data_type: "APIRESPONSE" + producesJson: true security: - petstore_auth: - "write:pets" @@ -422,6 +423,7 @@ paths: uppercase_operation_id: "GET_INVENTORY" uppercase_message: "SUCCESSFUL_OPERATION" uppercase_data_type: "HASHMAP" + producesJson: true security: - api_key: [] operation_id: "get_inventory" @@ -452,6 +454,7 @@ paths: example: "???" model_key: "OuterBoolean" uppercase_operation_id: "PLACE_ORDER" + consumesJson: true responses: 200: description: "successful operation" @@ -570,6 +573,7 @@ paths: example: "???" model_key: "OuterBoolean" uppercase_operation_id: "CREATE_USER" + consumesJson: true responses: default: description: "successful operation" @@ -604,6 +608,7 @@ paths: example: "&Vec::new()" model_key: "OuterBoolean" uppercase_operation_id: "CREATE_USERS_WITH_ARRAY_INPUT" + consumesJson: true responses: default: description: "successful operation" @@ -637,6 +642,7 @@ paths: example: "&Vec::new()" model_key: "OuterBoolean" uppercase_operation_id: "CREATE_USERS_WITH_LIST_INPUT" + consumesJson: true responses: default: description: "successful operation" @@ -789,6 +795,7 @@ paths: example: "???" model_key: "OuterBoolean" uppercase_operation_id: "UPDATE_USER" + consumesJson: true responses: 400: description: "Invalid user supplied" @@ -858,6 +865,7 @@ paths: example: "???" model_key: "OuterBoolean" uppercase_operation_id: "TEST_CLASSNAME" + consumesJson: true responses: 200: description: "successful operation" @@ -866,6 +874,7 @@ paths: uppercase_operation_id: "TEST_CLASSNAME" uppercase_message: "SUCCESSFUL_OPERATION" uppercase_data_type: "CLIENT" + producesJson: true security: - api_key_query: [] operation_id: "test_classname" @@ -893,10 +902,10 @@ paths: type: "array" items: type: "string" - default: "$" enum: - ">" - "$" + default: "$" formatString: "{:?}" example: "Some(&Vec::new())" - name: "enum_form_string" @@ -918,10 +927,10 @@ paths: type: "array" items: type: "string" - default: "$" enum: - ">" - "$" + default: "$" formatString: "{:?}" example: "Some(&Vec::new())" - name: "enum_header_string" @@ -943,10 +952,10 @@ paths: type: "array" items: type: "string" - default: "$" enum: - ">" - "$" + default: "$" formatString: "{:?}" example: "Some(&Vec::new())" - name: "enum_query_string" @@ -1171,6 +1180,7 @@ paths: example: "???" model_key: "OuterBoolean" uppercase_operation_id: "TEST_CLIENT_MODEL" + consumesJson: true responses: 200: description: "successful operation" @@ -1179,6 +1189,7 @@ paths: uppercase_operation_id: "TEST_CLIENT_MODEL" uppercase_message: "SUCCESSFUL_OPERATION" uppercase_data_type: "CLIENT" + producesJson: true operation_id: "test_client_model" uppercase_operation_id: "TEST_CLIENT_MODEL" path: "/fake" @@ -1204,6 +1215,7 @@ paths: example: "None" model_key: "OuterBoolean" uppercase_operation_id: "FAKE_OUTER_NUMBER_SERIALIZE" + consumesJson: true responses: 200: description: "Output number" @@ -1212,6 +1224,7 @@ paths: uppercase_operation_id: "FAKE_OUTER_NUMBER_SERIALIZE" uppercase_message: "OUTPUT_NUMBER" uppercase_data_type: "OUTERNUMBER" + producesJson: true operation_id: "fake_outer_number_serialize" uppercase_operation_id: "FAKE_OUTER_NUMBER_SERIALIZE" path: "/fake/outer/number" @@ -1236,6 +1249,7 @@ paths: example: "None" model_key: "OuterBoolean" uppercase_operation_id: "FAKE_OUTER_STRING_SERIALIZE" + consumesJson: true responses: 200: description: "Output string" @@ -1244,6 +1258,7 @@ paths: uppercase_operation_id: "FAKE_OUTER_STRING_SERIALIZE" uppercase_message: "OUTPUT_STRING" uppercase_data_type: "OUTERSTRING" + producesJson: true operation_id: "fake_outer_string_serialize" uppercase_operation_id: "FAKE_OUTER_STRING_SERIALIZE" path: "/fake/outer/string" @@ -1268,6 +1283,7 @@ paths: example: "None" model_key: "OuterBoolean" uppercase_operation_id: "FAKE_OUTER_BOOLEAN_SERIALIZE" + consumesJson: true responses: 200: description: "Output boolean" @@ -1276,6 +1292,7 @@ paths: uppercase_operation_id: "FAKE_OUTER_BOOLEAN_SERIALIZE" uppercase_message: "OUTPUT_BOOLEAN" uppercase_data_type: "OUTERBOOLEAN" + producesJson: true operation_id: "fake_outer_boolean_serialize" uppercase_operation_id: "FAKE_OUTER_BOOLEAN_SERIALIZE" path: "/fake/outer/boolean" @@ -1300,6 +1317,7 @@ paths: example: "None" model_key: "OuterBoolean" uppercase_operation_id: "FAKE_OUTER_COMPOSITE_SERIALIZE" + consumesJson: true responses: 200: description: "Output composite" @@ -1308,6 +1326,7 @@ paths: uppercase_operation_id: "FAKE_OUTER_COMPOSITE_SERIALIZE" uppercase_message: "OUTPUT_COMPOSITE" uppercase_data_type: "OUTERCOMPOSITE" + producesJson: true operation_id: "fake_outer_composite_serialize" uppercase_operation_id: "FAKE_OUTER_COMPOSITE_SERIALIZE" path: "/fake/outer/composite" @@ -1371,6 +1390,7 @@ paths: example: "???" model_key: "OuterBoolean" uppercase_operation_id: "TEST_INLINE_ADDITIONAL_PROPERTIES" + consumesJson: true responses: 200: description: "successful operation" @@ -1406,6 +1426,7 @@ paths: example: "???" model_key: "OuterBoolean" uppercase_operation_id: "TEST_SPECIAL_TAGS" + consumesJson: true responses: 200: description: "successful operation" @@ -1414,6 +1435,7 @@ paths: uppercase_operation_id: "TEST_SPECIAL_TAGS" uppercase_message: "SUCCESSFUL_OPERATION" uppercase_data_type: "CLIENT" + producesJson: true operation_id: "test_special_tags" uppercase_operation_id: "TEST_SPECIAL_TAGS" path: "/another-fake/dummy" diff --git a/samples/server/petstore/rust-server/examples/client.rs b/samples/server/petstore/rust-server/examples/client.rs index dde1265b213..e0bb1d1bc97 100644 --- a/samples/server/petstore/rust-server/examples/client.rs +++ b/samples/server/petstore/rust-server/examples/client.rs @@ -81,14 +81,31 @@ fn main() { .arg(Arg::with_name("https") .long("https") .help("Whether to use HTTPS or not")) + .arg(Arg::with_name("host") + .long("host") + .takes_value(true) + .default_value("petstore.swagger.io") + .help("Hostname to contact")) + .arg(Arg::with_name("port") + .long("port") + .takes_value(true) + .default_value("80") + .help("Port to contact")) .get_matches(); - let client = if matches.is_present("https") { + let is_https = matches.is_present("https"); + let base_url = format!("{}://{}:{}", + if is_https { "https" } else { "http" }, + matches.value_of("host").unwrap(), + matches.value_of("port").unwrap()); + let client = if is_https { // Using Simple HTTPS - petstore_api::Client::try_new_https("https://localhost:8080", "examples/ca.pem").expect("Failed to create HTTPS client") + petstore_api::Client::try_new_https(&base_url, "examples/ca.pem") + .expect("Failed to create HTTPS client") } else { // Using HTTP - petstore_api::Client::try_new_http("http://localhost:8080").expect("Failed to create HTTP client") + petstore_api::Client::try_new_http(&base_url) + .expect("Failed to create HTTP client") }; // Using a non-default `Context` is not required; this is just an example! diff --git a/samples/server/petstore/rust-server/examples/server.rs b/samples/server/petstore/rust-server/examples/server.rs index f3f3f50de4b..022e44a29e4 100644 --- a/samples/server/petstore/rust-server/examples/server.rs +++ b/samples/server/petstore/rust-server/examples/server.rs @@ -1,11 +1,22 @@ +//! Main binary entry point for petstore_api implementation. + #![allow(missing_docs)] +// Imports required by this file. +// extern crate ; extern crate petstore_api; +extern crate swagger; extern crate iron; -extern crate futures; extern crate hyper_openssl; extern crate clap; -extern crate swagger; + +// Imports required by server library. +// extern crate petstore_api; +// extern crate swagger; +extern crate futures; +extern crate chrono; +#[macro_use] +extern crate error_chain; use hyper_openssl::OpensslServer; use hyper_openssl::openssl::x509::X509_FILETYPE_PEM; @@ -15,7 +26,6 @@ use clap::{App, Arg}; use iron::{Iron, Chain}; use swagger::auth::AllowAllMiddleware; -// Import the module that defines the Server struct. mod server_lib; /// Builds an SSL implementation for Simple HTTPS from some hard-coded file names @@ -39,7 +49,7 @@ fn main() { .help("Whether to use HTTPS or not")) .get_matches(); - let server = server_lib::Server{}; + let server = server_lib::server().unwrap(); let router = petstore_api::router(server); let mut chain = Chain::new(router); @@ -50,9 +60,9 @@ fn main() { if matches.is_present("https") { // Using Simple HTTPS - Iron::new(chain).https("localhost:8080", ssl().expect("Failed to load SSL keys")).expect("Failed to start HTTPS server"); + Iron::new(chain).https("localhost:80", ssl().expect("Failed to load SSL keys")).expect("Failed to start HTTPS server"); } else { // Using HTTP - Iron::new(chain).http("localhost:8080").expect("Failed to start HTTP server"); + Iron::new(chain).http("localhost:80").expect("Failed to start HTTP server"); } } diff --git a/samples/server/petstore/rust-server/examples/server_lib/mod.rs b/samples/server/petstore/rust-server/examples/server_lib/mod.rs index bd4616950e5..31964faedbe 100644 --- a/samples/server/petstore/rust-server/examples/server_lib/mod.rs +++ b/samples/server/petstore/rust-server/examples/server_lib/mod.rs @@ -1,271 +1,14 @@ -#![allow(missing_docs, unused_extern_crates)] -extern crate chrono; -extern crate swagger; +//! Main library entry point for petstore_api implementation. -use futures::{self, Future}; -use futures::Stream; - -#[allow(unused_imports)] -use std::collections::HashMap; -use std::io::Error; - -use petstore_api::{Api, ApiError, Context, - TestSpecialTagsResponse, - FakeOuterBooleanSerializeResponse, - FakeOuterCompositeSerializeResponse, - FakeOuterNumberSerializeResponse, - FakeOuterStringSerializeResponse, - TestClientModelResponse, - TestEndpointParametersResponse, - TestEnumParametersResponse, - TestInlineAdditionalPropertiesResponse, - TestJsonFormDataResponse, - TestClassnameResponse, - AddPetResponse, - DeletePetResponse, - FindPetsByStatusResponse, - FindPetsByTagsResponse, - GetPetByIdResponse, - UpdatePetResponse, - UpdatePetWithFormResponse, - UploadFileResponse, - DeleteOrderResponse, - GetInventoryResponse, - GetOrderByIdResponse, - PlaceOrderResponse, - CreateUserResponse, - CreateUsersWithArrayInputResponse, - CreateUsersWithListInputResponse, - DeleteUserResponse, - GetUserByNameResponse, - LoginUserResponse, - LogoutUserResponse, - UpdateUserResponse -}; -#[allow(unused_imports)] -use petstore_api::models; - -#[derive(Copy, Clone)] -pub struct Server; - -impl Api for Server { - - /// To test special tags - fn test_special_tags(&self, body: models::Client, context: &Context) -> Box + Send> { - let context = context.clone(); - println!("test_special_tags({:?}) - X-Span-ID: {:?}", body, context.x_span_id.unwrap_or(String::from("")).clone()); - Box::new(futures::failed("Generic failure".into())) - } - - - fn fake_outer_boolean_serialize(&self, body: Option, context: &Context) -> Box + Send> { - let context = context.clone(); - println!("fake_outer_boolean_serialize({:?}) - X-Span-ID: {:?}", body, context.x_span_id.unwrap_or(String::from("")).clone()); - Box::new(futures::failed("Generic failure".into())) - } - - - fn fake_outer_composite_serialize(&self, body: Option, context: &Context) -> Box + Send> { - let context = context.clone(); - println!("fake_outer_composite_serialize({:?}) - X-Span-ID: {:?}", body, context.x_span_id.unwrap_or(String::from("")).clone()); - Box::new(futures::failed("Generic failure".into())) - } - - - fn fake_outer_number_serialize(&self, body: Option, context: &Context) -> Box + Send> { - let context = context.clone(); - println!("fake_outer_number_serialize({:?}) - X-Span-ID: {:?}", body, context.x_span_id.unwrap_or(String::from("")).clone()); - Box::new(futures::failed("Generic failure".into())) - } - - - fn fake_outer_string_serialize(&self, body: Option, context: &Context) -> Box + Send> { - let context = context.clone(); - println!("fake_outer_string_serialize({:?}) - X-Span-ID: {:?}", body, context.x_span_id.unwrap_or(String::from("")).clone()); - Box::new(futures::failed("Generic failure".into())) - } - - /// To test \"client\" model - fn test_client_model(&self, body: models::Client, context: &Context) -> Box + Send> { - let context = context.clone(); - println!("test_client_model({:?}) - X-Span-ID: {:?}", body, context.x_span_id.unwrap_or(String::from("")).clone()); - Box::new(futures::failed("Generic failure".into())) - } - - /// Fake endpoint for testing various parameters 假端點 偽のエンドポイント 가짜 엔드 포인트 - fn test_endpoint_parameters(&self, number: f64, double: f64, pattern_without_delimiter: String, byte: swagger::ByteArray, integer: Option, int32: Option, int64: Option, float: Option, string: Option, binary: Option, date: Option>, date_time: Option>, password: Option, callback: Option, context: &Context) -> Box + Send> { - let context = context.clone(); - println!("test_endpoint_parameters({}, {}, \"{}\", {:?}, {:?}, {:?}, {:?}, {:?}, {:?}, {:?}, {:?}, {:?}, {:?}, {:?}) - X-Span-ID: {:?}", number, double, pattern_without_delimiter, byte, integer, int32, int64, float, string, binary, date, date_time, password, callback, context.x_span_id.unwrap_or(String::from("")).clone()); - Box::new(futures::failed("Generic failure".into())) - } - - /// To test enum parameters - fn test_enum_parameters(&self, enum_form_string_array: Option<&Vec>, enum_form_string: Option, enum_header_string_array: Option<&Vec>, enum_header_string: Option, enum_query_string_array: Option<&Vec>, enum_query_string: Option, enum_query_integer: Option, enum_query_double: Option, context: &Context) -> Box + Send> { - let context = context.clone(); - println!("test_enum_parameters({:?}, {:?}, {:?}, {:?}, {:?}, {:?}, {:?}, {:?}) - X-Span-ID: {:?}", enum_form_string_array, enum_form_string, enum_header_string_array, enum_header_string, enum_query_string_array, enum_query_string, enum_query_integer, enum_query_double, context.x_span_id.unwrap_or(String::from("")).clone()); - Box::new(futures::failed("Generic failure".into())) - } - - /// test inline additionalProperties - fn test_inline_additional_properties(&self, param: object, context: &Context) -> Box + Send> { - let context = context.clone(); - println!("test_inline_additional_properties({:?}) - X-Span-ID: {:?}", param, context.x_span_id.unwrap_or(String::from("")).clone()); - Box::new(futures::failed("Generic failure".into())) - } - - /// test json serialization of form data - fn test_json_form_data(&self, param: String, param2: String, context: &Context) -> Box + Send> { - let context = context.clone(); - println!("test_json_form_data(\"{}\", \"{}\") - X-Span-ID: {:?}", param, param2, context.x_span_id.unwrap_or(String::from("")).clone()); - Box::new(futures::failed("Generic failure".into())) - } - - /// To test class name in snake case - fn test_classname(&self, body: models::Client, context: &Context) -> Box + Send> { - let context = context.clone(); - println!("test_classname({:?}) - X-Span-ID: {:?}", body, context.x_span_id.unwrap_or(String::from("")).clone()); - Box::new(futures::failed("Generic failure".into())) - } - - /// Add a new pet to the store - fn add_pet(&self, body: models::Pet, context: &Context) -> Box + Send> { - let context = context.clone(); - println!("add_pet({:?}) - X-Span-ID: {:?}", body, context.x_span_id.unwrap_or(String::from("")).clone()); - Box::new(futures::failed("Generic failure".into())) - } - - /// Deletes a pet - fn delete_pet(&self, pet_id: i64, api_key: Option, context: &Context) -> Box + Send> { - let context = context.clone(); - println!("delete_pet({}, {:?}) - X-Span-ID: {:?}", pet_id, api_key, context.x_span_id.unwrap_or(String::from("")).clone()); - Box::new(futures::failed("Generic failure".into())) - } - - /// Finds Pets by status - fn find_pets_by_status(&self, status: &Vec, context: &Context) -> Box + Send> { - let context = context.clone(); - println!("find_pets_by_status({:?}) - X-Span-ID: {:?}", status, context.x_span_id.unwrap_or(String::from("")).clone()); - Box::new(futures::failed("Generic failure".into())) - } - - /// Finds Pets by tags - fn find_pets_by_tags(&self, tags: &Vec, context: &Context) -> Box + Send> { - let context = context.clone(); - println!("find_pets_by_tags({:?}) - X-Span-ID: {:?}", tags, context.x_span_id.unwrap_or(String::from("")).clone()); - Box::new(futures::failed("Generic failure".into())) - } - - /// Find pet by ID - fn get_pet_by_id(&self, pet_id: i64, context: &Context) -> Box + Send> { - let context = context.clone(); - println!("get_pet_by_id({}) - X-Span-ID: {:?}", pet_id, context.x_span_id.unwrap_or(String::from("")).clone()); - Box::new(futures::failed("Generic failure".into())) - } - - /// Update an existing pet - fn update_pet(&self, body: models::Pet, context: &Context) -> Box + Send> { - let context = context.clone(); - println!("update_pet({:?}) - X-Span-ID: {:?}", body, context.x_span_id.unwrap_or(String::from("")).clone()); - Box::new(futures::failed("Generic failure".into())) - } - - /// Updates a pet in the store with form data - fn update_pet_with_form(&self, pet_id: i64, name: Option, status: Option, context: &Context) -> Box + Send> { - let context = context.clone(); - println!("update_pet_with_form({}, {:?}, {:?}) - X-Span-ID: {:?}", pet_id, name, status, context.x_span_id.unwrap_or(String::from("")).clone()); - Box::new(futures::failed("Generic failure".into())) - } - - /// uploads an image - fn upload_file(&self, pet_id: i64, additional_metadata: Option, file: Box, Error=Error> + Send>>, Error=Error> + Send>, context: &Context) -> Box + Send> { - let context = context.clone(); - println!("upload_file({}, {:?}, ) - X-Span-ID: {:?}", pet_id, additional_metadata, context.x_span_id.unwrap_or(String::from("")).clone()); - let _ = file; //Suppresses unused param warning - Box::new(futures::failed("Generic failure".into())) - } - - /// Delete purchase order by ID - fn delete_order(&self, order_id: String, context: &Context) -> Box + Send> { - let context = context.clone(); - println!("delete_order(\"{}\") - X-Span-ID: {:?}", order_id, context.x_span_id.unwrap_or(String::from("")).clone()); - Box::new(futures::failed("Generic failure".into())) - } - - /// Returns pet inventories by status - fn get_inventory(&self, context: &Context) -> Box + Send> { - let context = context.clone(); - println!("get_inventory() - X-Span-ID: {:?}", context.x_span_id.unwrap_or(String::from("")).clone()); - Box::new(futures::failed("Generic failure".into())) - } - - /// Find purchase order by ID - fn get_order_by_id(&self, order_id: i64, context: &Context) -> Box + Send> { - let context = context.clone(); - println!("get_order_by_id({}) - X-Span-ID: {:?}", order_id, context.x_span_id.unwrap_or(String::from("")).clone()); - Box::new(futures::failed("Generic failure".into())) - } - - /// Place an order for a pet - fn place_order(&self, body: models::Order, context: &Context) -> Box + Send> { - let context = context.clone(); - println!("place_order({:?}) - X-Span-ID: {:?}", body, context.x_span_id.unwrap_or(String::from("")).clone()); - Box::new(futures::failed("Generic failure".into())) - } - - /// Create user - fn create_user(&self, body: models::User, context: &Context) -> Box + Send> { - let context = context.clone(); - println!("create_user({:?}) - X-Span-ID: {:?}", body, context.x_span_id.unwrap_or(String::from("")).clone()); - Box::new(futures::failed("Generic failure".into())) - } - - /// Creates list of users with given input array - fn create_users_with_array_input(&self, body: &Vec, context: &Context) -> Box + Send> { - let context = context.clone(); - println!("create_users_with_array_input({:?}) - X-Span-ID: {:?}", body, context.x_span_id.unwrap_or(String::from("")).clone()); - Box::new(futures::failed("Generic failure".into())) - } - - /// Creates list of users with given input array - fn create_users_with_list_input(&self, body: &Vec, context: &Context) -> Box + Send> { - let context = context.clone(); - println!("create_users_with_list_input({:?}) - X-Span-ID: {:?}", body, context.x_span_id.unwrap_or(String::from("")).clone()); - Box::new(futures::failed("Generic failure".into())) - } - - /// Delete user - fn delete_user(&self, username: String, context: &Context) -> Box + Send> { - let context = context.clone(); - println!("delete_user(\"{}\") - X-Span-ID: {:?}", username, context.x_span_id.unwrap_or(String::from("")).clone()); - Box::new(futures::failed("Generic failure".into())) - } - - /// Get user by user name - fn get_user_by_name(&self, username: String, context: &Context) -> Box + Send> { - let context = context.clone(); - println!("get_user_by_name(\"{}\") - X-Span-ID: {:?}", username, context.x_span_id.unwrap_or(String::from("")).clone()); - Box::new(futures::failed("Generic failure".into())) - } - - /// Logs user into the system - fn login_user(&self, username: String, password: String, context: &Context) -> Box + Send> { - let context = context.clone(); - println!("login_user(\"{}\", \"{}\") - X-Span-ID: {:?}", username, password, context.x_span_id.unwrap_or(String::from("")).clone()); - Box::new(futures::failed("Generic failure".into())) - } - - /// Logs out current logged in user session - fn logout_user(&self, context: &Context) -> Box + Send> { - let context = context.clone(); - println!("logout_user() - X-Span-ID: {:?}", context.x_span_id.unwrap_or(String::from("")).clone()); - Box::new(futures::failed("Generic failure".into())) - } - - /// Updated user - fn update_user(&self, username: String, body: models::User, context: &Context) -> Box + Send> { - let context = context.clone(); - println!("update_user(\"{}\", {:?}) - X-Span-ID: {:?}", username, body, context.x_span_id.unwrap_or(String::from("")).clone()); - Box::new(futures::failed("Generic failure".into())) - } +mod server; +mod errors { + error_chain!{} +} + +pub use self::errors::*; + +/// Instantiate a new server. +pub fn server() -> Result { + Ok(server::Server {}) } diff --git a/samples/server/petstore/rust-server/examples/server_lib/server.rs b/samples/server/petstore/rust-server/examples/server_lib/server.rs new file mode 100644 index 00000000000..54b01504c61 --- /dev/null +++ b/samples/server/petstore/rust-server/examples/server_lib/server.rs @@ -0,0 +1,270 @@ +//! Server implementation of petstore_api. + +#![allow(unused_imports)] + +use futures::{self, Future}; +use chrono; +use futures::Stream; +use std::collections::HashMap; +use std::io::Error; +use swagger; + +use petstore_api::{Api, ApiError, Context, + TestSpecialTagsResponse, + FakeOuterBooleanSerializeResponse, + FakeOuterCompositeSerializeResponse, + FakeOuterNumberSerializeResponse, + FakeOuterStringSerializeResponse, + TestClientModelResponse, + TestEndpointParametersResponse, + TestEnumParametersResponse, + TestInlineAdditionalPropertiesResponse, + TestJsonFormDataResponse, + TestClassnameResponse, + AddPetResponse, + DeletePetResponse, + FindPetsByStatusResponse, + FindPetsByTagsResponse, + GetPetByIdResponse, + UpdatePetResponse, + UpdatePetWithFormResponse, + UploadFileResponse, + DeleteOrderResponse, + GetInventoryResponse, + GetOrderByIdResponse, + PlaceOrderResponse, + CreateUserResponse, + CreateUsersWithArrayInputResponse, + CreateUsersWithListInputResponse, + DeleteUserResponse, + GetUserByNameResponse, + LoginUserResponse, + LogoutUserResponse, + UpdateUserResponse +}; +use petstore_api::models; + +#[derive(Copy, Clone)] +pub struct Server; + +impl Api for Server { + + /// To test special tags + fn test_special_tags(&self, body: models::Client, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("test_special_tags({:?}) - X-Span-ID: {:?}", body, context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + + fn fake_outer_boolean_serialize(&self, body: Option, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("fake_outer_boolean_serialize({:?}) - X-Span-ID: {:?}", body, context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + + fn fake_outer_composite_serialize(&self, body: Option, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("fake_outer_composite_serialize({:?}) - X-Span-ID: {:?}", body, context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + + fn fake_outer_number_serialize(&self, body: Option, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("fake_outer_number_serialize({:?}) - X-Span-ID: {:?}", body, context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + + fn fake_outer_string_serialize(&self, body: Option, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("fake_outer_string_serialize({:?}) - X-Span-ID: {:?}", body, context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + /// To test \"client\" model + fn test_client_model(&self, body: models::Client, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("test_client_model({:?}) - X-Span-ID: {:?}", body, context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + /// Fake endpoint for testing various parameters 假端點 偽のエンドポイント 가짜 엔드 포인트 + fn test_endpoint_parameters(&self, number: f64, double: f64, pattern_without_delimiter: String, byte: swagger::ByteArray, integer: Option, int32: Option, int64: Option, float: Option, string: Option, binary: Option, date: Option>, date_time: Option>, password: Option, callback: Option, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("test_endpoint_parameters({}, {}, \"{}\", {:?}, {:?}, {:?}, {:?}, {:?}, {:?}, {:?}, {:?}, {:?}, {:?}, {:?}) - X-Span-ID: {:?}", number, double, pattern_without_delimiter, byte, integer, int32, int64, float, string, binary, date, date_time, password, callback, context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + /// To test enum parameters + fn test_enum_parameters(&self, enum_form_string_array: Option<&Vec>, enum_form_string: Option, enum_header_string_array: Option<&Vec>, enum_header_string: Option, enum_query_string_array: Option<&Vec>, enum_query_string: Option, enum_query_integer: Option, enum_query_double: Option, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("test_enum_parameters({:?}, {:?}, {:?}, {:?}, {:?}, {:?}, {:?}, {:?}) - X-Span-ID: {:?}", enum_form_string_array, enum_form_string, enum_header_string_array, enum_header_string, enum_query_string_array, enum_query_string, enum_query_integer, enum_query_double, context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + /// test inline additionalProperties + fn test_inline_additional_properties(&self, param: object, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("test_inline_additional_properties({:?}) - X-Span-ID: {:?}", param, context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + /// test json serialization of form data + fn test_json_form_data(&self, param: String, param2: String, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("test_json_form_data(\"{}\", \"{}\") - X-Span-ID: {:?}", param, param2, context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + /// To test class name in snake case + fn test_classname(&self, body: models::Client, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("test_classname({:?}) - X-Span-ID: {:?}", body, context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + /// Add a new pet to the store + fn add_pet(&self, body: models::Pet, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("add_pet({:?}) - X-Span-ID: {:?}", body, context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + /// Deletes a pet + fn delete_pet(&self, pet_id: i64, api_key: Option, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("delete_pet({}, {:?}) - X-Span-ID: {:?}", pet_id, api_key, context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + /// Finds Pets by status + fn find_pets_by_status(&self, status: &Vec, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("find_pets_by_status({:?}) - X-Span-ID: {:?}", status, context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + /// Finds Pets by tags + fn find_pets_by_tags(&self, tags: &Vec, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("find_pets_by_tags({:?}) - X-Span-ID: {:?}", tags, context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + /// Find pet by ID + fn get_pet_by_id(&self, pet_id: i64, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("get_pet_by_id({}) - X-Span-ID: {:?}", pet_id, context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + /// Update an existing pet + fn update_pet(&self, body: models::Pet, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("update_pet({:?}) - X-Span-ID: {:?}", body, context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + /// Updates a pet in the store with form data + fn update_pet_with_form(&self, pet_id: i64, name: Option, status: Option, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("update_pet_with_form({}, {:?}, {:?}) - X-Span-ID: {:?}", pet_id, name, status, context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + /// uploads an image + fn upload_file(&self, pet_id: i64, additional_metadata: Option, file: Box, Error=Error> + Send>>, Error=Error> + Send>, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("upload_file({}, {:?}, ) - X-Span-ID: {:?}", pet_id, additional_metadata, context.x_span_id.unwrap_or(String::from("")).clone()); + let _ = file; //Suppresses unused param warning + Box::new(futures::failed("Generic failure".into())) + } + + /// Delete purchase order by ID + fn delete_order(&self, order_id: String, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("delete_order(\"{}\") - X-Span-ID: {:?}", order_id, context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + /// Returns pet inventories by status + fn get_inventory(&self, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("get_inventory() - X-Span-ID: {:?}", context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + /// Find purchase order by ID + fn get_order_by_id(&self, order_id: i64, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("get_order_by_id({}) - X-Span-ID: {:?}", order_id, context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + /// Place an order for a pet + fn place_order(&self, body: models::Order, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("place_order({:?}) - X-Span-ID: {:?}", body, context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + /// Create user + fn create_user(&self, body: models::User, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("create_user({:?}) - X-Span-ID: {:?}", body, context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + /// Creates list of users with given input array + fn create_users_with_array_input(&self, body: &Vec, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("create_users_with_array_input({:?}) - X-Span-ID: {:?}", body, context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + /// Creates list of users with given input array + fn create_users_with_list_input(&self, body: &Vec, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("create_users_with_list_input({:?}) - X-Span-ID: {:?}", body, context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + /// Delete user + fn delete_user(&self, username: String, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("delete_user(\"{}\") - X-Span-ID: {:?}", username, context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + /// Get user by user name + fn get_user_by_name(&self, username: String, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("get_user_by_name(\"{}\") - X-Span-ID: {:?}", username, context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + /// Logs user into the system + fn login_user(&self, username: String, password: String, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("login_user(\"{}\", \"{}\") - X-Span-ID: {:?}", username, password, context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + /// Logs out current logged in user session + fn logout_user(&self, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("logout_user() - X-Span-ID: {:?}", context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + + /// Updated user + fn update_user(&self, username: String, body: models::User, context: &Context) -> Box + Send> { + let context = context.clone(); + println!("update_user(\"{}\", {:?}) - X-Span-ID: {:?}", username, body, context.x_span_id.unwrap_or(String::from("")).clone()); + Box::new(futures::failed("Generic failure".into())) + } + +} diff --git a/samples/server/petstore/rust-server/src/client.rs b/samples/server/petstore/rust-server/src/client.rs index be7653886cb..3f8e45745a1 100644 --- a/samples/server/petstore/rust-server/src/client.rs +++ b/samples/server/petstore/rust-server/src/client.rs @@ -1,6 +1,7 @@ #![allow(unused_extern_crates)] extern crate hyper_openssl; extern crate chrono; +extern crate url; extern crate multipart; use multipart::client::lazy::Multipart; @@ -11,6 +12,7 @@ use hyper::header::{Headers, ContentType}; use hyper::mime::{Mime, TopLevel, SubLevel, Attr, Value}; use hyper::Url; use self::hyper_openssl::openssl; +use self::url::percent_encoding::{utf8_percent_encode, PATH_SEGMENT_ENCODE_SET, QUERY_ENCODE_SET}; use futures; use futures::{Future, Stream}; use futures::{future, stream}; @@ -200,7 +202,10 @@ impl Api for Client { fn test_special_tags(&self, param_body: models::Client, context: &Context) -> Box + Send> { - let url = format!("{}/v2/another-fake/dummy?", self.base_path); + let url = format!( + "{}/v2/another-fake/dummy", + self.base_path + ); let body = serde_json::to_string(¶m_body).expect("impossible to fail to serialize"); @@ -225,8 +230,6 @@ impl Api for Client { response.read_to_string(&mut buf).map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?; let body = serde_json::from_str::(&buf)?; - - Ok(TestSpecialTagsResponse::SuccessfulOperation(body)) }, code => { @@ -253,7 +256,10 @@ impl Api for Client { fn fake_outer_boolean_serialize(&self, param_body: Option, context: &Context) -> Box + Send> { - let url = format!("{}/v2/fake/outer/boolean?", self.base_path); + let url = format!( + "{}/v2/fake/outer/boolean", + self.base_path + ); let body = param_body.map(|ref body| { @@ -282,8 +288,6 @@ impl Api for Client { response.read_to_string(&mut buf).map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?; let body = serde_json::from_str::(&buf)?; - - Ok(FakeOuterBooleanSerializeResponse::OutputBoolean(body)) }, code => { @@ -310,7 +314,10 @@ impl Api for Client { fn fake_outer_composite_serialize(&self, param_body: Option, context: &Context) -> Box + Send> { - let url = format!("{}/v2/fake/outer/composite?", self.base_path); + let url = format!( + "{}/v2/fake/outer/composite", + self.base_path + ); let body = param_body.map(|ref body| { @@ -339,8 +346,6 @@ impl Api for Client { response.read_to_string(&mut buf).map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?; let body = serde_json::from_str::(&buf)?; - - Ok(FakeOuterCompositeSerializeResponse::OutputComposite(body)) }, code => { @@ -367,7 +372,10 @@ impl Api for Client { fn fake_outer_number_serialize(&self, param_body: Option, context: &Context) -> Box + Send> { - let url = format!("{}/v2/fake/outer/number?", self.base_path); + let url = format!( + "{}/v2/fake/outer/number", + self.base_path + ); let body = param_body.map(|ref body| { @@ -396,8 +404,6 @@ impl Api for Client { response.read_to_string(&mut buf).map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?; let body = serde_json::from_str::(&buf)?; - - Ok(FakeOuterNumberSerializeResponse::OutputNumber(body)) }, code => { @@ -424,7 +430,10 @@ impl Api for Client { fn fake_outer_string_serialize(&self, param_body: Option, context: &Context) -> Box + Send> { - let url = format!("{}/v2/fake/outer/string?", self.base_path); + let url = format!( + "{}/v2/fake/outer/string", + self.base_path + ); let body = param_body.map(|ref body| { @@ -453,8 +462,6 @@ impl Api for Client { response.read_to_string(&mut buf).map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?; let body = serde_json::from_str::(&buf)?; - - Ok(FakeOuterStringSerializeResponse::OutputString(body)) }, code => { @@ -481,7 +488,10 @@ impl Api for Client { fn test_client_model(&self, param_body: models::Client, context: &Context) -> Box + Send> { - let url = format!("{}/v2/fake?", self.base_path); + let url = format!( + "{}/v2/fake", + self.base_path + ); let body = serde_json::to_string(¶m_body).expect("impossible to fail to serialize"); @@ -506,8 +516,6 @@ impl Api for Client { response.read_to_string(&mut buf).map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?; let body = serde_json::from_str::(&buf)?; - - Ok(TestClientModelResponse::SuccessfulOperation(body)) }, code => { @@ -534,7 +542,10 @@ impl Api for Client { fn test_endpoint_parameters(&self, param_number: f64, param_double: f64, param_pattern_without_delimiter: String, param_byte: swagger::ByteArray, param_integer: Option, param_int32: Option, param_int64: Option, param_float: Option, param_string: Option, param_binary: Option, param_date: Option>, param_date_time: Option>, param_password: Option, param_callback: Option, context: &Context) -> Box + Send> { - let url = format!("{}/v2/fake?", self.base_path); + let url = format!( + "{}/v2/fake", + self.base_path + ); let hyper_client = (self.hyper_client)(); @@ -588,7 +599,13 @@ impl Api for Client { let query_enum_query_integer = param_enum_query_integer.map_or_else(String::new, |query| format!("enum_query_integer={enum_query_integer}&", enum_query_integer=query.to_string())); - let url = format!("{}/v2/fake?{enum_query_string_array}{enum_query_string}{enum_query_integer}", self.base_path, enum_query_string_array=query_enum_query_string_array, enum_query_string=query_enum_query_string, enum_query_integer=query_enum_query_integer); + let url = format!( + "{}/v2/fake?{enum_query_string_array}{enum_query_string}{enum_query_integer}", + self.base_path, + enum_query_string_array=utf8_percent_encode(&query_enum_query_string_array, QUERY_ENCODE_SET), + enum_query_string=utf8_percent_encode(&query_enum_query_string, QUERY_ENCODE_SET), + enum_query_integer=utf8_percent_encode(&query_enum_query_integer, QUERY_ENCODE_SET) + ); let hyper_client = (self.hyper_client)(); @@ -643,7 +660,10 @@ impl Api for Client { fn test_inline_additional_properties(&self, param_param: object, context: &Context) -> Box + Send> { - let url = format!("{}/v2/fake/inline-additionalProperties?", self.base_path); + let url = format!( + "{}/v2/fake/inline-additionalProperties", + self.base_path + ); let body = serde_json::to_string(¶m_param).expect("impossible to fail to serialize"); @@ -692,7 +712,10 @@ impl Api for Client { fn test_json_form_data(&self, param_param: String, param_param2: String, context: &Context) -> Box + Send> { - let url = format!("{}/v2/fake/jsonFormData?", self.base_path); + let url = format!( + "{}/v2/fake/jsonFormData", + self.base_path + ); let hyper_client = (self.hyper_client)(); @@ -736,7 +759,10 @@ impl Api for Client { fn test_classname(&self, param_body: models::Client, context: &Context) -> Box + Send> { - let url = format!("{}/v2/fake_classname_test?", self.base_path); + let url = format!( + "{}/v2/fake_classname_test", + self.base_path + ); let body = serde_json::to_string(¶m_body).expect("impossible to fail to serialize"); @@ -761,8 +787,6 @@ impl Api for Client { response.read_to_string(&mut buf).map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?; let body = serde_json::from_str::(&buf)?; - - Ok(TestClassnameResponse::SuccessfulOperation(body)) }, code => { @@ -789,7 +813,10 @@ impl Api for Client { fn add_pet(&self, param_body: models::Pet, context: &Context) -> Box + Send> { - let url = format!("{}/v2/pet?", self.base_path); + let url = format!( + "{}/v2/pet", + self.base_path + ); let body = serde_xml_rs::to_string(¶m_body).expect("impossible to fail to serialize"); @@ -838,7 +865,10 @@ impl Api for Client { fn delete_pet(&self, param_pet_id: i64, param_api_key: Option, context: &Context) -> Box + Send> { - let url = format!("{}/v2/pet/{petId}?", self.base_path, petId=param_pet_id.to_string()); + let url = format!( + "{}/v2/pet/{petId}", + self.base_path, petId=utf8_percent_encode(¶m_pet_id.to_string(), PATH_SEGMENT_ENCODE_SET) + ); let hyper_client = (self.hyper_client)(); @@ -889,7 +919,11 @@ impl Api for Client { let query_status = format!("status={status}&", status=param_status.join(",")); - let url = format!("{}/v2/pet/findByStatus?{status}", self.base_path, status=query_status); + let url = format!( + "{}/v2/pet/findByStatus?{status}", + self.base_path, + status=utf8_percent_encode(&query_status, QUERY_ENCODE_SET) + ); let hyper_client = (self.hyper_client)(); @@ -912,7 +946,6 @@ impl Api for Client { let body = serde_xml_rs::from_str::>(&buf) .map_err(|e| ApiError(format!("Response body did not match the schema: {}", e)))?; - Ok(FindPetsByStatusResponse::SuccessfulOperation(body)) }, 400 => { @@ -947,7 +980,11 @@ impl Api for Client { let query_tags = format!("tags={tags}&", tags=param_tags.join(",")); - let url = format!("{}/v2/pet/findByTags?{tags}", self.base_path, tags=query_tags); + let url = format!( + "{}/v2/pet/findByTags?{tags}", + self.base_path, + tags=utf8_percent_encode(&query_tags, QUERY_ENCODE_SET) + ); let hyper_client = (self.hyper_client)(); @@ -970,7 +1007,6 @@ impl Api for Client { let body = serde_xml_rs::from_str::>(&buf) .map_err(|e| ApiError(format!("Response body did not match the schema: {}", e)))?; - Ok(FindPetsByTagsResponse::SuccessfulOperation(body)) }, 400 => { @@ -1002,7 +1038,10 @@ impl Api for Client { fn get_pet_by_id(&self, param_pet_id: i64, context: &Context) -> Box + Send> { - let url = format!("{}/v2/pet/{petId}?", self.base_path, petId=param_pet_id.to_string()); + let url = format!( + "{}/v2/pet/{petId}", + self.base_path, petId=utf8_percent_encode(¶m_pet_id.to_string(), PATH_SEGMENT_ENCODE_SET) + ); let hyper_client = (self.hyper_client)(); @@ -1025,7 +1064,6 @@ impl Api for Client { let body = serde_xml_rs::from_str::(&buf) .map_err(|e| ApiError(format!("Response body did not match the schema: {}", e)))?; - Ok(GetPetByIdResponse::SuccessfulOperation(body)) }, 400 => { @@ -1062,7 +1100,10 @@ impl Api for Client { fn update_pet(&self, param_body: models::Pet, context: &Context) -> Box + Send> { - let url = format!("{}/v2/pet?", self.base_path); + let url = format!( + "{}/v2/pet", + self.base_path + ); let body = serde_xml_rs::to_string(¶m_body).expect("impossible to fail to serialize"); @@ -1121,7 +1162,10 @@ impl Api for Client { fn update_pet_with_form(&self, param_pet_id: i64, param_name: Option, param_status: Option, context: &Context) -> Box + Send> { - let url = format!("{}/v2/pet/{petId}?", self.base_path, petId=param_pet_id.to_string()); + let url = format!( + "{}/v2/pet/{petId}", + self.base_path, petId=utf8_percent_encode(¶m_pet_id.to_string(), PATH_SEGMENT_ENCODE_SET) + ); let hyper_client = (self.hyper_client)(); @@ -1165,7 +1209,10 @@ impl Api for Client { fn upload_file(&self, param_pet_id: i64, param_additional_metadata: Option, param_file: Box, Error=Error> + Send>>, Error=Error> + Send>, context: &Context) -> Box + Send> { - let url = format!("{}/v2/pet/{petId}/uploadImage?", self.base_path, petId=param_pet_id.to_string()); + let url = format!( + "{}/v2/pet/{petId}/uploadImage", + self.base_path, petId=utf8_percent_encode(¶m_pet_id.to_string(), PATH_SEGMENT_ENCODE_SET) + ); // Form data body let mut multipart = Multipart::new(); @@ -1209,8 +1256,6 @@ impl Api for Client { response.read_to_string(&mut buf).map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?; let body = serde_json::from_str::(&buf)?; - - Ok(UploadFileResponse::SuccessfulOperation(body)) }, code => { @@ -1249,7 +1294,10 @@ impl Api for Client { fn delete_order(&self, param_order_id: String, context: &Context) -> Box + Send> { - let url = format!("{}/v2/store/order/{order_id}?", self.base_path, order_id=param_order_id.to_string()); + let url = format!( + "{}/v2/store/order/{order_id}", + self.base_path, order_id=utf8_percent_encode(¶m_order_id.to_string(), PATH_SEGMENT_ENCODE_SET) + ); let hyper_client = (self.hyper_client)(); @@ -1298,7 +1346,10 @@ impl Api for Client { fn get_inventory(&self, context: &Context) -> Box + Send> { - let url = format!("{}/v2/store/inventory?", self.base_path); + let url = format!( + "{}/v2/store/inventory", + self.base_path + ); let hyper_client = (self.hyper_client)(); @@ -1318,8 +1369,6 @@ impl Api for Client { response.read_to_string(&mut buf).map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?; let body = serde_json::from_str::>(&buf)?; - - Ok(GetInventoryResponse::SuccessfulOperation(body)) }, code => { @@ -1346,7 +1395,10 @@ impl Api for Client { fn get_order_by_id(&self, param_order_id: i64, context: &Context) -> Box + Send> { - let url = format!("{}/v2/store/order/{order_id}?", self.base_path, order_id=param_order_id.to_string()); + let url = format!( + "{}/v2/store/order/{order_id}", + self.base_path, order_id=utf8_percent_encode(¶m_order_id.to_string(), PATH_SEGMENT_ENCODE_SET) + ); let hyper_client = (self.hyper_client)(); @@ -1369,7 +1421,6 @@ impl Api for Client { let body = serde_xml_rs::from_str::(&buf) .map_err(|e| ApiError(format!("Response body did not match the schema: {}", e)))?; - Ok(GetOrderByIdResponse::SuccessfulOperation(body)) }, 400 => { @@ -1406,7 +1457,10 @@ impl Api for Client { fn place_order(&self, param_body: models::Order, context: &Context) -> Box + Send> { - let url = format!("{}/v2/store/order?", self.base_path); + let url = format!( + "{}/v2/store/order", + self.base_path + ); let body = serde_json::to_string(¶m_body).expect("impossible to fail to serialize"); @@ -1434,7 +1488,6 @@ impl Api for Client { let body = serde_xml_rs::from_str::(&buf) .map_err(|e| ApiError(format!("Response body did not match the schema: {}", e)))?; - Ok(PlaceOrderResponse::SuccessfulOperation(body)) }, 400 => { @@ -1466,7 +1519,10 @@ impl Api for Client { fn create_user(&self, param_body: models::User, context: &Context) -> Box + Send> { - let url = format!("{}/v2/user?", self.base_path); + let url = format!( + "{}/v2/user", + self.base_path + ); let body = serde_json::to_string(¶m_body).expect("impossible to fail to serialize"); @@ -1515,7 +1571,10 @@ impl Api for Client { fn create_users_with_array_input(&self, param_body: &Vec, context: &Context) -> Box + Send> { - let url = format!("{}/v2/user/createWithArray?", self.base_path); + let url = format!( + "{}/v2/user/createWithArray", + self.base_path + ); let body = serde_json::to_string(¶m_body).expect("impossible to fail to serialize"); @@ -1564,7 +1623,10 @@ impl Api for Client { fn create_users_with_list_input(&self, param_body: &Vec, context: &Context) -> Box + Send> { - let url = format!("{}/v2/user/createWithList?", self.base_path); + let url = format!( + "{}/v2/user/createWithList", + self.base_path + ); let body = serde_json::to_string(¶m_body).expect("impossible to fail to serialize"); @@ -1613,7 +1675,10 @@ impl Api for Client { fn delete_user(&self, param_username: String, context: &Context) -> Box + Send> { - let url = format!("{}/v2/user/{username}?", self.base_path, username=param_username.to_string()); + let url = format!( + "{}/v2/user/{username}", + self.base_path, username=utf8_percent_encode(¶m_username.to_string(), PATH_SEGMENT_ENCODE_SET) + ); let hyper_client = (self.hyper_client)(); @@ -1662,7 +1727,10 @@ impl Api for Client { fn get_user_by_name(&self, param_username: String, context: &Context) -> Box + Send> { - let url = format!("{}/v2/user/{username}?", self.base_path, username=param_username.to_string()); + let url = format!( + "{}/v2/user/{username}", + self.base_path, username=utf8_percent_encode(¶m_username.to_string(), PATH_SEGMENT_ENCODE_SET) + ); let hyper_client = (self.hyper_client)(); @@ -1685,7 +1753,6 @@ impl Api for Client { let body = serde_xml_rs::from_str::(&buf) .map_err(|e| ApiError(format!("Response body did not match the schema: {}", e)))?; - Ok(GetUserByNameResponse::SuccessfulOperation(body)) }, 400 => { @@ -1726,7 +1793,12 @@ impl Api for Client { let query_password = format!("password={password}&", password=param_password.to_string()); - let url = format!("{}/v2/user/login?{username}{password}", self.base_path, username=query_username, password=query_password); + let url = format!( + "{}/v2/user/login?{username}{password}", + self.base_path, + username=utf8_percent_encode(&query_username, QUERY_ENCODE_SET), + password=utf8_percent_encode(&query_password, QUERY_ENCODE_SET) + ); let hyper_client = (self.hyper_client)(); @@ -1748,7 +1820,6 @@ impl Api for Client { // once https://github.com/RReverser/serde-xml-rs/pull/45 is accepted upstream let body = serde_xml_rs::from_str::(&buf) .map_err(|e| ApiError(format!("Response body did not match the schema: {}", e)))?; - header! { (ResponseXRateLimit, "X-Rate-Limit") => [i32] } let response_x_rate_limit = response.headers.get::().ok_or_else(|| "Required response header X-Rate-Limit for response 200 was not found.")?; header! { (ResponseXExpiresAfter, "X-Expires-After") => [chrono::DateTime] } @@ -1785,7 +1856,10 @@ impl Api for Client { fn logout_user(&self, context: &Context) -> Box + Send> { - let url = format!("{}/v2/user/logout?", self.base_path); + let url = format!( + "{}/v2/user/logout", + self.base_path + ); let hyper_client = (self.hyper_client)(); @@ -1829,7 +1903,10 @@ impl Api for Client { fn update_user(&self, param_username: String, param_body: models::User, context: &Context) -> Box + Send> { - let url = format!("{}/v2/user/{username}?", self.base_path, username=param_username.to_string()); + let url = format!( + "{}/v2/user/{username}", + self.base_path, username=utf8_percent_encode(¶m_username.to_string(), PATH_SEGMENT_ENCODE_SET) + ); let body = serde_json::to_string(¶m_body).expect("impossible to fail to serialize"); diff --git a/samples/server/petstore/rust-server/src/models.rs b/samples/server/petstore/rust-server/src/models.rs index e956aae2bb9..98e973eec70 100644 --- a/samples/server/petstore/rust-server/src/models.rs +++ b/samples/server/petstore/rust-server/src/models.rs @@ -322,7 +322,7 @@ impl EnumArrays { /// which helps with FFI. #[allow(non_camel_case_types)] #[repr(C)] -#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Serialize, Deserialize)] +#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Serialize, Deserialize, Eq, Ord)] pub enum EnumClass { #[serde(rename = "_abc")] _ABC, @@ -739,7 +739,7 @@ impl OuterComposite { /// which helps with FFI. #[allow(non_camel_case_types)] #[repr(C)] -#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Serialize, Deserialize)] +#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Serialize, Deserialize, Eq, Ord)] pub enum OuterEnum { #[serde(rename = "placed")] PLACED, diff --git a/samples/server/petstore/rust-server/src/server.rs b/samples/server/petstore/rust-server/src/server.rs index 2adfcb01b22..675dae8ef56 100644 --- a/samples/server/petstore/rust-server/src/server.rs +++ b/samples/server/petstore/rust-server/src/server.rs @@ -33,7 +33,8 @@ use std::io::Error; #[allow(unused_imports)] use std::collections::BTreeSet; -use swagger::auth::{Authorization, AuthData, Scopes}; +pub use swagger::auth::Authorization; +use swagger::auth::{AuthData, Scopes}; use swagger::{ApiError, Context, XSpanId}; use {Api, @@ -129,10 +130,11 @@ fn add_routes(router: &mut Router, api: T) where T: Api + Send + Sync + Clone // values, rather than causing a 400 response). Produce warning header and logs for // any unused fields. - let param_body_raw = req.get::().map_err(|e| Response::with((status::BadRequest, format!("Couldn't parse body parameter body - not valid UTF-8: {}", e))))?; + let param_body = req.get::().map_err(|e| Response::with((status::BadRequest, format!("Couldn't parse body parameter body - not valid UTF-8: {}", e))))?; + let mut unused_elements = Vec::new(); - let param_body = if let Some(param_body_raw) = param_body_raw { + let param_body = if let Some(param_body_raw) = param_body { let deserializer = &mut serde_json::Deserializer::from_str(¶m_body_raw); let param_body: Option = serde_ignored::deserialize(deserializer, |path| { @@ -198,10 +200,11 @@ fn add_routes(router: &mut Router, api: T) where T: Api + Send + Sync + Clone // values, rather than causing a 400 response). Produce warning header and logs for // any unused fields. - let param_body_raw = req.get::().unwrap_or(None); + let param_body = req.get::().unwrap_or(None); + let mut unused_elements = Vec::new(); - let param_body = if let Some(param_body_raw) = param_body_raw { + let param_body = if let Some(param_body_raw) = param_body { let deserializer = &mut serde_json::Deserializer::from_str(¶m_body_raw); let param_body: Option = serde_ignored::deserialize(deserializer, |path| { @@ -264,10 +267,11 @@ fn add_routes(router: &mut Router, api: T) where T: Api + Send + Sync + Clone // values, rather than causing a 400 response). Produce warning header and logs for // any unused fields. - let param_body_raw = req.get::().unwrap_or(None); + let param_body = req.get::().unwrap_or(None); + let mut unused_elements = Vec::new(); - let param_body = if let Some(param_body_raw) = param_body_raw { + let param_body = if let Some(param_body_raw) = param_body { let deserializer = &mut serde_json::Deserializer::from_str(¶m_body_raw); let param_body: Option = serde_ignored::deserialize(deserializer, |path| { @@ -330,10 +334,11 @@ fn add_routes(router: &mut Router, api: T) where T: Api + Send + Sync + Clone // values, rather than causing a 400 response). Produce warning header and logs for // any unused fields. - let param_body_raw = req.get::().unwrap_or(None); + let param_body = req.get::().unwrap_or(None); + let mut unused_elements = Vec::new(); - let param_body = if let Some(param_body_raw) = param_body_raw { + let param_body = if let Some(param_body_raw) = param_body { let deserializer = &mut serde_json::Deserializer::from_str(¶m_body_raw); let param_body: Option = serde_ignored::deserialize(deserializer, |path| { @@ -396,10 +401,11 @@ fn add_routes(router: &mut Router, api: T) where T: Api + Send + Sync + Clone // values, rather than causing a 400 response). Produce warning header and logs for // any unused fields. - let param_body_raw = req.get::().unwrap_or(None); + let param_body = req.get::().unwrap_or(None); + let mut unused_elements = Vec::new(); - let param_body = if let Some(param_body_raw) = param_body_raw { + let param_body = if let Some(param_body_raw) = param_body { let deserializer = &mut serde_json::Deserializer::from_str(¶m_body_raw); let param_body: Option = serde_ignored::deserialize(deserializer, |path| { @@ -462,10 +468,11 @@ fn add_routes(router: &mut Router, api: T) where T: Api + Send + Sync + Clone // values, rather than causing a 400 response). Produce warning header and logs for // any unused fields. - let param_body_raw = req.get::().map_err(|e| Response::with((status::BadRequest, format!("Couldn't parse body parameter body - not valid UTF-8: {}", e))))?; + let param_body = req.get::().map_err(|e| Response::with((status::BadRequest, format!("Couldn't parse body parameter body - not valid UTF-8: {}", e))))?; + let mut unused_elements = Vec::new(); - let param_body = if let Some(param_body_raw) = param_body_raw { + let param_body = if let Some(param_body_raw) = param_body { let deserializer = &mut serde_json::Deserializer::from_str(¶m_body_raw); let param_body: Option = serde_ignored::deserialize(deserializer, |path| { @@ -682,10 +689,11 @@ fn add_routes(router: &mut Router, api: T) where T: Api + Send + Sync + Clone // values, rather than causing a 400 response). Produce warning header and logs for // any unused fields. - let param_param_raw = req.get::().map_err(|e| Response::with((status::BadRequest, format!("Couldn't parse body parameter param - not valid UTF-8: {}", e))))?; + let param_param = req.get::().map_err(|e| Response::with((status::BadRequest, format!("Couldn't parse body parameter param - not valid UTF-8: {}", e))))?; + let mut unused_elements = Vec::new(); - let param_param = if let Some(param_param_raw) = param_param_raw { + let param_param = if let Some(param_param_raw) = param_param { let deserializer = &mut serde_json::Deserializer::from_str(¶m_param_raw); let param_param: Option = serde_ignored::deserialize(deserializer, |path| { @@ -801,10 +809,11 @@ fn add_routes(router: &mut Router, api: T) where T: Api + Send + Sync + Clone // values, rather than causing a 400 response). Produce warning header and logs for // any unused fields. - let param_body_raw = req.get::().map_err(|e| Response::with((status::BadRequest, format!("Couldn't parse body parameter body - not valid UTF-8: {}", e))))?; + let param_body = req.get::().map_err(|e| Response::with((status::BadRequest, format!("Couldn't parse body parameter body - not valid UTF-8: {}", e))))?; + let mut unused_elements = Vec::new(); - let param_body = if let Some(param_body_raw) = param_body_raw { + let param_body = if let Some(param_body_raw) = param_body { let deserializer = &mut serde_json::Deserializer::from_str(¶m_body_raw); let param_body: Option = serde_ignored::deserialize(deserializer, |path| { @@ -895,10 +904,11 @@ fn add_routes(router: &mut Router, api: T) where T: Api + Send + Sync + Clone // values, rather than causing a 400 response). Produce warning header and logs for // any unused fields. - let param_body_raw = req.get::().map_err(|e| Response::with((status::BadRequest, format!("Couldn't parse body parameter body - not valid UTF-8: {}", e))))?; + let param_body = req.get::().map_err(|e| Response::with((status::BadRequest, format!("Couldn't parse body parameter body - not valid UTF-8: {}", e))))?; + let mut unused_elements = Vec::new(); - let param_body = if let Some(param_body_raw) = param_body_raw { + let param_body = if let Some(param_body_raw) = param_body { let deserializer = &mut serde_xml_rs::de::Deserializer::new_from_reader(param_body_raw.as_bytes()); let param_body: Option = serde_ignored::deserialize(deserializer, |path| { @@ -1327,10 +1337,11 @@ fn add_routes(router: &mut Router, api: T) where T: Api + Send + Sync + Clone // values, rather than causing a 400 response). Produce warning header and logs for // any unused fields. - let param_body_raw = req.get::().map_err(|e| Response::with((status::BadRequest, format!("Couldn't parse body parameter body - not valid UTF-8: {}", e))))?; + let param_body = req.get::().map_err(|e| Response::with((status::BadRequest, format!("Couldn't parse body parameter body - not valid UTF-8: {}", e))))?; + let mut unused_elements = Vec::new(); - let param_body = if let Some(param_body_raw) = param_body_raw { + let param_body = if let Some(param_body_raw) = param_body { let deserializer = &mut serde_xml_rs::de::Deserializer::new_from_reader(param_body_raw.as_bytes()); let param_body: Option = serde_ignored::deserialize(deserializer, |path| { @@ -1810,10 +1821,11 @@ fn add_routes(router: &mut Router, api: T) where T: Api + Send + Sync + Clone // values, rather than causing a 400 response). Produce warning header and logs for // any unused fields. - let param_body_raw = req.get::().map_err(|e| Response::with((status::BadRequest, format!("Couldn't parse body parameter body - not valid UTF-8: {}", e))))?; + let param_body = req.get::().map_err(|e| Response::with((status::BadRequest, format!("Couldn't parse body parameter body - not valid UTF-8: {}", e))))?; + let mut unused_elements = Vec::new(); - let param_body = if let Some(param_body_raw) = param_body_raw { + let param_body = if let Some(param_body_raw) = param_body { let deserializer = &mut serde_json::Deserializer::from_str(¶m_body_raw); let param_body: Option = serde_ignored::deserialize(deserializer, |path| { @@ -1891,10 +1903,11 @@ fn add_routes(router: &mut Router, api: T) where T: Api + Send + Sync + Clone // values, rather than causing a 400 response). Produce warning header and logs for // any unused fields. - let param_body_raw = req.get::().map_err(|e| Response::with((status::BadRequest, format!("Couldn't parse body parameter body - not valid UTF-8: {}", e))))?; + let param_body = req.get::().map_err(|e| Response::with((status::BadRequest, format!("Couldn't parse body parameter body - not valid UTF-8: {}", e))))?; + let mut unused_elements = Vec::new(); - let param_body = if let Some(param_body_raw) = param_body_raw { + let param_body = if let Some(param_body_raw) = param_body { let deserializer = &mut serde_json::Deserializer::from_str(¶m_body_raw); let param_body: Option = serde_ignored::deserialize(deserializer, |path| { @@ -1959,10 +1972,11 @@ fn add_routes(router: &mut Router, api: T) where T: Api + Send + Sync + Clone // values, rather than causing a 400 response). Produce warning header and logs for // any unused fields. - let param_body_raw = req.get::().map_err(|e| Response::with((status::BadRequest, format!("Couldn't parse body parameter body - not valid UTF-8: {}", e))))?; + let param_body = req.get::().map_err(|e| Response::with((status::BadRequest, format!("Couldn't parse body parameter body - not valid UTF-8: {}", e))))?; + let mut unused_elements = Vec::new(); - let param_body = if let Some(param_body_raw) = param_body_raw { + let param_body = if let Some(param_body_raw) = param_body { let deserializer = &mut serde_json::Deserializer::from_str(¶m_body_raw); let param_body: Option> = serde_ignored::deserialize(deserializer, |path| { @@ -2027,10 +2041,11 @@ fn add_routes(router: &mut Router, api: T) where T: Api + Send + Sync + Clone // values, rather than causing a 400 response). Produce warning header and logs for // any unused fields. - let param_body_raw = req.get::().map_err(|e| Response::with((status::BadRequest, format!("Couldn't parse body parameter body - not valid UTF-8: {}", e))))?; + let param_body = req.get::().map_err(|e| Response::with((status::BadRequest, format!("Couldn't parse body parameter body - not valid UTF-8: {}", e))))?; + let mut unused_elements = Vec::new(); - let param_body = if let Some(param_body_raw) = param_body_raw { + let param_body = if let Some(param_body_raw) = param_body { let deserializer = &mut serde_json::Deserializer::from_str(¶m_body_raw); let param_body: Option> = serde_ignored::deserialize(deserializer, |path| { @@ -2359,10 +2374,11 @@ fn add_routes(router: &mut Router, api: T) where T: Api + Send + Sync + Clone // values, rather than causing a 400 response). Produce warning header and logs for // any unused fields. - let param_body_raw = req.get::().map_err(|e| Response::with((status::BadRequest, format!("Couldn't parse body parameter body - not valid UTF-8: {}", e))))?; + let param_body = req.get::().map_err(|e| Response::with((status::BadRequest, format!("Couldn't parse body parameter body - not valid UTF-8: {}", e))))?; + let mut unused_elements = Vec::new(); - let param_body = if let Some(param_body_raw) = param_body_raw { + let param_body = if let Some(param_body_raw) = param_body { let deserializer = &mut serde_json::Deserializer::from_str(¶m_body_raw); let param_body: Option = serde_ignored::deserialize(deserializer, |path| {