[Rust Server] Build APIs which have no available examples (#5561)

* [Rust Server] Build APIs which have no examples

* [Rust Server] Add test for APIs with no examples

* Update samples
This commit is contained in:
Richard Whitehouse 2020-03-22 19:54:48 +00:00 committed by GitHub
parent b0520a346d
commit be983b5212
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 1811 additions and 0 deletions

View File

@ -3,6 +3,9 @@ extern crate {{{externCrateName}}};
extern crate clap;
extern crate env_logger;
extern crate futures;
// log may be unused if there are no examples
#[allow(unused_imports)]
#[macro_use]
extern crate log;
#[macro_use]
@ -33,8 +36,13 @@ use {{{externCrateName}}}::{Api, ApiNoContext, Client, ContextWrapperExt,
{{{operationId}}}Response{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}}
};
use clap::{App, Arg};
// swagger::Has may be unused if there are no examples
#[allow(unused_imports)]
use swagger::{ContextBuilder, EmptyContext, XSpanIdString, Has, Push, AuthData};
// rt may be unused if there are no examples
#[allow(unused_mut)]
fn main() {
env_logger::init();

View File

@ -0,0 +1,23 @@
# Test for an API where we can't generate any examples
openapi: 3.0.1
info:
title: Regression test for an API which doesn't have any example
version: 0.0.1
paths:
/op:
get:
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
propery:
type: string
required:
- property
responses:
'200':
description: 'OK'

View File

@ -3,6 +3,9 @@ extern crate multipart_v3;
extern crate clap;
extern crate env_logger;
extern crate futures;
// log may be unused if there are no examples
#[allow(unused_imports)]
#[macro_use]
extern crate log;
#[macro_use]
@ -19,8 +22,13 @@ use multipart_v3::{Api, ApiNoContext, Client, ContextWrapperExt,
MultipleIdenticalMimeTypesPostResponse
};
use clap::{App, Arg};
// swagger::Has may be unused if there are no examples
#[allow(unused_imports)]
use swagger::{ContextBuilder, EmptyContext, XSpanIdString, Has, Push, AuthData};
// rt may be unused if there are no examples
#[allow(unused_mut)]
fn main() {
env_logger::init();

View File

@ -0,0 +1,18 @@
[build]
rustflags = [
"-W", "missing_docs", # detects missing documentation for public members
"-W", "trivial_casts", # detects trivial casts which could be removed
"-W", "trivial_numeric_casts", # detects trivial casts of numeric types which could be removed
"-W", "unsafe_code", # usage of `unsafe` code
"-W", "unused_qualifications", # detects unnecessarily qualified names
"-W", "unused_extern_crates", # extern crates that are never used
"-W", "unused_import_braces", # unnecessary braces around an imported item
"-D", "warnings", # all warnings should be denied
]

View File

@ -0,0 +1,2 @@
target
Cargo.lock

View File

@ -0,0 +1,23 @@
# OpenAPI Generator Ignore
# Generated by openapi-generator https://github.com/openapitools/openapi-generator
# Use this file to prevent files from being overwritten by the generator.
# The patterns follow closely to .gitignore or .dockerignore.
# As an example, the C# client generator defines ApiClient.cs.
# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:
#ApiClient.cs
# You can match any string of characters against a directory, file or extension with a single asterisk (*):
#foo/*/qux
# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux
# You can recursively match patterns against a directory, file or extension with a double asterisk (**):
#foo/**/qux
# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux
# You can also negate patterns with an exclamation (!).
# For example, you can ignore all files in a docs folder with the file extension .md:
#docs/*.md
# Then explicitly reverse the ignore rule for a single file:
#!docs/README.md

View File

@ -0,0 +1,75 @@
[package]
name = "no-example-v3"
version = "0.0.1"
authors = []
description = "No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)"
license = "Unlicense"
[features]
default = ["client", "server"]
client = [
"serde_json", "serde_ignored", "hyper", "hyper-openssl", "native-tls", "openssl", "tokio", "url"
]
server = [
"serde_json", "serde_ignored", "hyper", "tokio", "regex", "percent-encoding", "url", "lazy_static"
]
conversion = ["frunk", "frunk_derives", "frunk_core", "frunk-enum-core", "frunk-enum-derive"]
[target.'cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))'.dependencies]
native-tls = { version = "0.2", optional = true }
[target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dependencies]
hyper-openssl = { version = "0.7.1", optional = true }
openssl = {version = "0.10", optional = true }
[dependencies]
# Common
chrono = { version = "0.4", features = ["serde"] }
futures = "0.1"
swagger = "4.0"
log = "0.3.0"
mime = "0.3"
serde = "1.0"
serde_derive = "1.0"
# Crates included if required by the API definition
# Common between server and client features
hyper = {version = "0.12", optional = true}
serde_json = {version = "1.0", optional = true}
serde_ignored = {version = "0.0.4", optional = true}
tokio = {version = "0.1.17", optional = true}
url = {version = "1.5", optional = true}
# Client-specific
# Server, and client callback-specific
lazy_static = { version = "1.4", optional = true }
percent-encoding = {version = "1.0.0", optional = true}
regex = {version = "0.2", optional = true}
# Conversion
frunk = { version = "0.3.0", optional = true }
frunk_derives = { version = "0.3.0", optional = true }
frunk_core = { version = "0.3.0", optional = true }
frunk-enum-derive = { version = "0.2.0", optional = true }
frunk-enum-core = { version = "0.2.0", optional = true }
[dev-dependencies]
clap = "2.25"
error-chain = "0.12"
env_logger = "0.6"
uuid = {version = "0.7", features = ["serde", "v4"]}
[target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dev-dependencies]
tokio-openssl = "0.3"
openssl = "0.10"
[[example]]
name = "client"
required-features = ["client"]
[[example]]
name = "server"
required-features = ["server"]

View File

@ -0,0 +1,112 @@
# Rust API for no-example-v3
No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
## Overview
This client/server was generated by the [openapi-generator]
(https://openapi-generator.tech) project. By using the
[OpenAPI-Spec](https://github.com/OAI/OpenAPI-Specification) from a remote
server, you can easily generate a server stub.
To see how to make this your own, look here:
[README]((https://openapi-generator.tech))
- API version: 0.0.1
This autogenerated project defines an API crate `no-example-v3` which contains:
* An `Api` trait defining the API in Rust.
* Data types representing the underlying data model.
* A `Client` type which implements `Api` and issues HTTP requests for each operation.
* A router which accepts HTTP requests and invokes the appropriate `Api` method for each operation.
It also contains an example server and client which make use of `no-example-v3`:
* The example server starts up a web server using the `no-example-v3`
router, and supplies a trivial implementation of `Api` which returns failure
for every operation.
* The example client provides a CLI which lets you invoke
any single operation on the `no-example-v3` client by passing appropriate
arguments on the command line.
You can use the example server and client as a basis for your own code.
See below for [more detail on implementing a server](#writing-a-server).
## Examples
Run examples with:
```
cargo run --example <example-name>
```
To pass in arguments to the examples, put them after `--`, for example:
```
cargo run --example client -- --help
```
### Running the example server
To run the server, follow these simple steps:
```
cargo run --example server
```
### Running the example client
To run a client, follow one of the following simple steps:
```
```
### HTTPS
The examples can be run in HTTPS mode by passing in the flag `--https`, for example:
```
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`.
## Using the generated library
The generated library has a few optional features that can be activated through Cargo.
* `server`
* This defaults to enabled and creates the basic skeleton of a server implementation based on hyper
* To create the server stack you'll need to provide an implementation of the API trait to provide the server function.
* `client`
* This defaults to enabled and creates the basic skeleton of a client implementation based on hyper
* The constructed client implements the API trait by making remote API call.
* `conversions`
* This defaults to disabled and creates extra derives on models to allow "transmogrification" between objects of structurally similar types.
See https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section for how to use features in your `Cargo.toml`.
## Documentation for API Endpoints
All URIs are relative to *http://localhost*
Method | HTTP request | Description
------------- | ------------- | -------------
[****](docs/default_api.md#) | **GET** /op |
## Documentation For Models
- [InlineObject](docs/InlineObject.md)
## Documentation For Authorization
Endpoints do not require authorization.
## Author

View File

@ -0,0 +1,41 @@
openapi: 3.0.1
info:
title: Regression test for an API which doesn't have any example
version: 0.0.1
servers:
- url: /
paths:
/op:
get:
requestBody:
$ref: '#/components/requestBodies/inline_object'
content:
application/json:
schema:
properties:
propery:
type: string
required:
- property
type: object
required: true
responses:
"200":
description: OK
components:
requestBodies:
inline_object:
content:
application/json:
schema:
$ref: '#/components/schemas/inline_object'
required: true
schemas:
inline_object:
properties:
propery:
type: string
required:
- property
type: object

View File

@ -0,0 +1,10 @@
# InlineObject
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**propery** | **String** | | [optional] [default to None]
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

View File

@ -0,0 +1,34 @@
# default_api
All URIs are relative to *http://localhost*
Method | HTTP request | Description
------------- | ------------- | -------------
****](default_api.md#) | **GET** /op |
# ****
> (inline_object)
### Required Parameters
Name | Type | Description | Notes
------------- | ------------- | ------------- | -------------
**inline_object** | [**InlineObject**](InlineObject.md)| |
### Return type
(empty response body)
### Authorization
No authorization required
### HTTP request headers
- **Content-Type**: application/json
- **Accept**: Not defined
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)

View File

@ -0,0 +1,17 @@
-----BEGIN CERTIFICATE-----
MIICtjCCAZ4CCQDpKecRERZ0xDANBgkqhkiG9w0BAQsFADAdMQswCQYDVQQGEwJV
UzEOMAwGA1UEAxMFTXkgQ0EwHhcNMTcwNTIzMTYwMDIzWhcNMTcwNjIyMTYwMDIz
WjAdMQswCQYDVQQGEwJVUzEOMAwGA1UEAxMFTXkgQ0EwggEiMA0GCSqGSIb3DQEB
AQUAA4IBDwAwggEKAoIBAQCt66py3x7sCSASRF2D05L5wkNDxAUjQKYx23W8Gbwv
GMGykk89BIdU5LX1JB1cKiUOkoIxfwAYuWc2V/wzTvVV7+11besnk3uX1c9KiqUF
LIX7kn/z5hzS4aelhKvH+MJlSZCSlp1ytpZbwo5GB5Pi2SGH56jDBiBoDRNBVdWL
z4wH7TdrQjqWwNxIZumD5OGMtcfJyuX08iPiEOaslOeoMqzObhvjc9aUgjVjhqyA
FkJGTXsi0oaD7oml+NE+mTNfEeZvEJQpLSjBY0OvQHzuHkyGBShBnfu/9x7/NRwd
WaqsLiF7/re9KDGYdJwP7Cu6uxYfKAyWarp6h2mG/GIdAgMBAAEwDQYJKoZIhvcN
AQELBQADggEBAGIl/VVIafeq/AJOQ9r7TzzB2ABJYr7NZa6bTu5O1jSp1Fonac15
SZ8gvRxODgH22ZYSqghPG4xzq4J3hkytlQqm57ZEt2I2M3OqIp17Ndcc1xDYzpLl
tA0FrVn6crQTM8vQkTDtGesaCWX+7Fir5dK7HnYWzfpSmsOpST07PfbNisEXKOxG
Dj4lBL1OnhTjsJeymVS1pFvkKkrcEJO+IxFiHL3CDsWjcXB0Z+E1zBtPoYyYsNsO
rBrjUxcZewF4xqWZhpW90Mt61fY2nRgU0uUwHcvDQUqvmzKcsqYa4mPKzfBI5mxo
01Ta96cDD6pS5Y1hOflZ0g84f2g/7xBLLDA=
-----END CERTIFICATE-----

View File

@ -0,0 +1,92 @@
#![allow(missing_docs, unused_variables, trivial_casts)]
extern crate no_example_v3;
extern crate clap;
extern crate env_logger;
extern crate futures;
// log may be unused if there are no examples
#[allow(unused_imports)]
#[macro_use]
extern crate log;
#[macro_use]
extern crate swagger;
extern crate tokio;
#[allow(unused_imports)]
use futures::{Future, future, Stream, stream};
#[allow(unused_imports)]
use no_example_v3::{Api, ApiNoContext, Client, ContextWrapperExt,
ApiError,
OpGetResponse
};
use clap::{App, Arg};
// swagger::Has may be unused if there are no examples
#[allow(unused_imports)]
use swagger::{ContextBuilder, EmptyContext, XSpanIdString, Has, Push, AuthData};
// rt may be unused if there are no examples
#[allow(unused_mut)]
fn main() {
env_logger::init();
let matches = App::new("client")
.arg(Arg::with_name("operation")
.help("Sets the operation to run")
.possible_values(&[
])
.required(true)
.index(1))
.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("localhost")
.help("Hostname to contact"))
.arg(Arg::with_name("port")
.long("port")
.takes_value(true)
.default_value("80")
.help("Port to contact"))
.get_matches();
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 matches.is_present("https") {
// Using Simple HTTPS
Client::try_new_https(&base_url)
.expect("Failed to create HTTPS client")
} else {
// Using HTTP
Client::try_new_http(
&base_url)
.expect("Failed to create HTTP client")
};
let context: make_context_ty!(ContextBuilder, EmptyContext, Option<AuthData>, XSpanIdString) =
make_context!(ContextBuilder, EmptyContext, None as Option<AuthData>, XSpanIdString::default());
let client = client.with_context(context);
let mut rt = tokio::runtime::Runtime::new().unwrap();
match matches.value_of("operation") {
/* Disabled because there's no example.
Some("OpGet") => {
let result = rt.block_on(client.op_get(
???
));
info!("{:?} (X-Span-ID: {:?})", result, (client.context() as &Has<XSpanIdString>).get().clone());
},
*/
_ => {
panic!("Invalid operation provided")
}
}
}

View File

@ -0,0 +1,66 @@
Certificate:
Data:
Version: 1 (0x0)
Serial Number: 4096 (0x1000)
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=US, CN=My CA
Validity
Not Before: May 23 16:00:23 2017 GMT
Not After : Apr 29 16:00:23 2117 GMT
Subject: CN=localhost, C=US
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
00:c9:d4:43:60:50:fc:d6:0f:38:4d:5d:5e:aa:7c:
c0:5e:a9:ec:d9:93:78:d3:93:72:28:41:f5:08:a5:
ea:ac:67:07:d7:1f:f7:7d:74:69:7e:46:89:20:4b:
7a:2d:9b:02:08:e7:6f:0f:1d:0c:0f:c7:60:69:19:
4b:df:7e:ca:75:94:0b:49:71:e3:6d:f2:e8:79:fd:
ed:0a:94:67:55:f3:ca:6b:61:ba:58:b7:2e:dd:7b:
ca:b9:02:9f:24:36:ac:26:8f:04:8f:81:c8:35:10:
f4:aa:33:b2:24:16:f8:f7:1e:ea:f7:16:fe:fa:34:
c3:dd:bb:2c:ba:7a:df:4d:e2:da:1e:e5:d2:28:44:
6e:c8:96:e0:fd:09:0c:14:0c:31:dc:e0:ca:c1:a7:
9b:bf:16:8c:f7:36:3f:1b:2e:dd:90:eb:45:78:51:
bf:59:22:1e:c6:8c:0a:69:88:e5:03:5e:73:b7:fc:
93:7f:1b:46:1b:97:68:c5:c0:8b:35:1f:bb:1e:67:
7f:55:b7:3b:55:3f:ea:f2:ca:db:cc:52:cd:16:89:
db:15:47:bd:f2:cd:6c:7a:d7:b4:1a:ac:c8:15:6c:
6a:fb:77:c4:e9:f2:30:e0:14:24:66:65:6f:2a:e5:
2d:cc:f6:81:ae:57:c8:d1:9b:38:90:dc:60:93:02:
5e:cb
Exponent: 65537 (0x10001)
Signature Algorithm: sha256WithRSAEncryption
1c:7c:39:e8:3d:49:b2:09:1e:68:5a:2f:74:18:f4:63:b5:8c:
f6:e6:a1:e3:4d:95:90:99:ef:32:5c:34:40:e8:55:13:0e:e0:
1c:be:cd:ab:3f:64:38:99:5e:2b:c1:81:53:a0:18:a8:f6:ee:
6a:33:73:6c:9a:73:9d:86:08:5d:c7:11:38:46:4c:cd:a0:47:
37:8f:fe:a6:50:a9:02:21:99:42:86:5e:47:fe:65:56:60:1d:
16:53:86:bd:e4:63:c5:69:cf:fa:30:51:ab:a1:c3:50:53:cc:
66:1c:4c:ff:3f:2a:39:4d:a2:8f:9d:d1:a7:8b:22:e4:78:69:
24:06:83:4d:cc:0a:c0:87:69:9b:bc:80:a9:d2:b7:a5:23:84:
7e:a2:32:26:7c:78:0e:bd:db:cd:3b:69:18:33:b8:44:ef:96:
b4:99:86:ee:06:bd:51:1c:c7:a1:a4:0c:c4:4c:51:a0:df:ac:
14:07:88:8e:d7:39:45:fe:52:e0:a3:4c:db:5d:7a:ab:4d:e4:
ca:06:e8:bd:74:6f:46:e7:93:4a:4f:1b:67:e7:a5:9f:ef:9c:
02:49:d1:f2:d5:e9:53:ee:09:21:ac:08:c8:15:f7:af:35:b9:
4f:11:0f:43:ae:46:8e:fd:5b:8d:a3:4e:a7:2c:b7:25:ed:e4:
e5:94:1d:e3
-----BEGIN CERTIFICATE-----
MIICtTCCAZ0CAhAAMA0GCSqGSIb3DQEBCwUAMB0xCzAJBgNVBAYTAlVTMQ4wDAYD
VQQDEwVNeSBDQTAgFw0xNzA1MjMxNjAwMjNaGA8yMTE3MDQyOTE2MDAyM1owITES
MBAGA1UEAxMJbG9jYWxob3N0MQswCQYDVQQGEwJVUzCCASIwDQYJKoZIhvcNAQEB
BQADggEPADCCAQoCggEBAMnUQ2BQ/NYPOE1dXqp8wF6p7NmTeNOTcihB9Qil6qxn
B9cf9310aX5GiSBLei2bAgjnbw8dDA/HYGkZS99+ynWUC0lx423y6Hn97QqUZ1Xz
ymthuli3Lt17yrkCnyQ2rCaPBI+ByDUQ9KozsiQW+Pce6vcW/vo0w927LLp6303i
2h7l0ihEbsiW4P0JDBQMMdzgysGnm78WjPc2Pxsu3ZDrRXhRv1kiHsaMCmmI5QNe
c7f8k38bRhuXaMXAizUfux5nf1W3O1U/6vLK28xSzRaJ2xVHvfLNbHrXtBqsyBVs
avt3xOnyMOAUJGZlbyrlLcz2ga5XyNGbOJDcYJMCXssCAwEAATANBgkqhkiG9w0B
AQsFAAOCAQEAHHw56D1JsgkeaFovdBj0Y7WM9uah402VkJnvMlw0QOhVEw7gHL7N
qz9kOJleK8GBU6AYqPbuajNzbJpznYYIXccROEZMzaBHN4/+plCpAiGZQoZeR/5l
VmAdFlOGveRjxWnP+jBRq6HDUFPMZhxM/z8qOU2ij53Rp4si5HhpJAaDTcwKwIdp
m7yAqdK3pSOEfqIyJnx4Dr3bzTtpGDO4RO+WtJmG7ga9URzHoaQMxExRoN+sFAeI
jtc5Rf5S4KNM2116q03kygbovXRvRueTSk8bZ+eln++cAknR8tXpU+4JIawIyBX3
rzW5TxEPQ65Gjv1bjaNOpyy3Je3k5ZQd4w==
-----END CERTIFICATE-----

View File

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDJ1ENgUPzWDzhN
XV6qfMBeqezZk3jTk3IoQfUIpeqsZwfXH/d9dGl+RokgS3otmwII528PHQwPx2Bp
GUvffsp1lAtJceNt8uh5/e0KlGdV88prYbpYty7de8q5Ap8kNqwmjwSPgcg1EPSq
M7IkFvj3Hur3Fv76NMPduyy6et9N4toe5dIoRG7IluD9CQwUDDHc4MrBp5u/Foz3
Nj8bLt2Q60V4Ub9ZIh7GjAppiOUDXnO3/JN/G0Ybl2jFwIs1H7seZ39VtztVP+ry
ytvMUs0WidsVR73yzWx617QarMgVbGr7d8Tp8jDgFCRmZW8q5S3M9oGuV8jRmziQ
3GCTAl7LAgMBAAECggEBAKEd1q9j14KWYc64s6KLthGbutyxsinMMbxbct11fdIk
6YhdF3fJ35ETg9IJDr6rWEN9ZRX+jStncNpVfFEs6ThVd3Eo/nI+EEGaaIkikR93
X2a7fEPn7/yVHu70XdBN6L1bPDvHUeiy4W2hmRrgT90OjGm1rNRWHOm7yugOwIZu
HclzbR9Ca7EInFnotUiDQm9sw9VKHbJHqWx6OORdZrxR2ytYs0Qkq0XpGMvti2HW
7WAmKTg5QM8myXW7+/4iqb/u68wVBR2BBalShKmIf7lim9O3W2a1RjDdsvm/wNe9
I+D+Iq825vpqkKXcrxYlpVg7hYiaQaW/MNsEb7lQRjECgYEA/RJYby0POW+/k0Jn
jO8UmJVEMiuGa8WIUu/JJWMOmzRCukjSRNQOkt7niQrZPJYE8W6clM6RJTolWf9L
IL6mIb+mRaoudUk8SHGDq7ho1iMg9GK8lhYxvKh1Q6uv8EyVSkgLknAEY0NANKC1
zNdU5Dhven9aRX2gq9vP4XwMz2MCgYEAzCogQ7IFk+gkp3k491dOZnrGRoRCfuzo
4CJtyKFgOSd7BjmpcKkj0IPfVBjw6GjMIxfQRMTQmxAjjWevH45vG8l0Iiwz/gSp
81b5nsDEX5uv2Olcmcz5zxRFy36jOZ9ihMWinxcIlT2oDbyCdbruDKZq9ieJ9S8g
4qGx0OkwE3kCgYEA7CmAiU89U9YqqttfEq/RQoqY91CSwmO10d+ej9seuEtOsdRf
FIfnibulycdr7hP5TOxyBpO1802NqayJiWcgVYIpQf2MGTtcnCYCP+95NcvWZvj1
EAJqK6nwtFO1fcOZ1ZXh5qfOEGujsPkAbsXLnKXlsiTCMvMHSxl3pu5Cbg0CgYBf
JjbZNctRrjv+7Qj2hPLd4dQsIxGWc7ToWENP4J2mpVa5hQAJqFovoHXhjKohtk2F
AWEn243Y5oGbMjo0e74edhmwn2cvuF64MM2vBem/ISCn98IXT6cQskMA3qkVfsl8
VVs/x41ReGWs2TD3y0GMFbb9t1mdMfSiincDhNnKCQKBgGfeT4jKyYeCoCw4OLI1
G75Gd0METt/IkppwODPpNwj3Rp9I5jctWZFA/3wCX/zk0HgBeou5AFNS4nQZ/X/L
L9axbSdR7UJTGkT1r4gu3rLkPV4Tk+8XM03/JT2cofMlzQBuhvl1Pn4SgKowz7hl
lS76ECw4Av3T0S34VW9Z5oye
-----END PRIVATE KEY-----

View File

@ -0,0 +1,48 @@
//! Main binary entry point for no_example_v3 implementation.
#![allow(missing_docs)]
// Imports required by this file.
// extern crate <name of this crate>;
extern crate no_example_v3;
extern crate clap;
extern crate env_logger;
extern crate hyper;
#[macro_use]
extern crate log;
extern crate swagger;
// Imports required by server library.
// extern crate no_example_v3;
extern crate chrono;
#[macro_use]
extern crate error_chain;
extern crate futures;
// extern crate swagger;
extern crate tokio;
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
extern crate openssl;
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
extern crate tokio_openssl;
use clap::{App, Arg};
mod server;
/// Create custom server, wire it to the autogenerated router,
/// and pass it to the web server.
fn main() {
env_logger::init();
let matches = App::new("server")
.arg(Arg::with_name("https")
.long("https")
.help("Whether to use HTTPS or not"))
.get_matches();
let addr = "127.0.0.1:80";
hyper::rt::run(server::create(addr, matches.is_present("https")));
}

View File

@ -0,0 +1,124 @@
//! Main library entry point for no_example_v3 implementation.
#![allow(unused_imports)]
mod errors {
error_chain!{}
}
pub use self::errors::*;
use chrono;
use futures::{future, Future, Stream};
use hyper::server::conn::Http;
use hyper::service::MakeService as _;
use openssl::ssl::SslAcceptorBuilder;
use std::collections::HashMap;
use std::marker::PhantomData;
use std::net::SocketAddr;
use std::sync::{Arc, Mutex};
use swagger;
use swagger::{Has, XSpanIdString};
use swagger::auth::MakeAllowAllAuthenticator;
use swagger::EmptyContext;
use tokio::net::TcpListener;
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
use tokio_openssl::SslAcceptorExt;
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod};
use no_example_v3::models;
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
/// Builds an SSL implementation for Simple HTTPS from some hard-coded file names
pub fn create(addr: &str, https: bool) -> Box<dyn Future<Item = (), Error = ()> + Send> {
let addr = addr.parse().expect("Failed to parse bind address");
let server = Server::new();
let service_fn = MakeService::new(server);
let service_fn = MakeAllowAllAuthenticator::new(service_fn, "cosmo");
let service_fn =
no_example_v3::server::context::MakeAddContext::<_, EmptyContext>::new(
service_fn
);
if https {
#[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))]
{
unimplemented!("SSL is not implemented for the examples on MacOS, Windows or iOS");
}
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
{
let mut ssl = SslAcceptor::mozilla_intermediate_v5(SslMethod::tls()).expect("Failed to create SSL Acceptor");
// Server authentication
ssl.set_private_key_file("examples/server-key.pem", SslFiletype::PEM).expect("Failed to set private key");
ssl.set_certificate_chain_file("examples/server-chain.pem").expect("Failed to set cerificate chain");
ssl.check_private_key().expect("Failed to check private key");
let tls_acceptor = ssl.build();
let service_fn = Arc::new(Mutex::new(service_fn));
let tls_listener = TcpListener::bind(&addr).unwrap().incoming().for_each(move |tcp| {
let addr = tcp.peer_addr().expect("Unable to get remote address");
let service_fn = service_fn.clone();
hyper::rt::spawn(tls_acceptor.accept_async(tcp).map_err(|_| ()).and_then(move |tls| {
let ms = {
let mut service_fn = service_fn.lock().unwrap();
service_fn.make_service(&addr)
};
ms.and_then(move |service| {
Http::new().serve_connection(tls, service)
}).map_err(|_| ())
}));
Ok(())
}).map_err(|_| ());
Box::new(tls_listener)
}
} else {
// Using HTTP
Box::new(hyper::server::Server::bind(&addr).serve(service_fn).map_err(|e| panic!("{:?}", e)))
}
}
#[derive(Copy, Clone)]
pub struct Server<C> {
marker: PhantomData<C>,
}
impl<C> Server<C> {
pub fn new() -> Self {
Server{marker: PhantomData}
}
}
use no_example_v3::{
Api,
ApiError,
OpGetResponse,
};
use no_example_v3::server::MakeService;
impl<C> Api<C> for Server<C> where C: Has<XSpanIdString>{
fn op_get(
&self,
inline_object: models::InlineObject,
context: &C) -> Box<Future<Item=OpGetResponse, Error=ApiError> + Send>
{
let context = context.clone();
info!("op_get({:?}) - X-Span-ID: {:?}", inline_object, context.get().0.clone());
Box::new(future::err("Generic failure".into()))
}
}

View File

@ -0,0 +1,339 @@
use futures;
use futures::{Future, Stream, future, stream};
use hyper;
use hyper::client::HttpConnector;
use hyper::header::{HeaderName, HeaderValue, CONTENT_TYPE};
use hyper::{Body, Uri, Response};
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
use hyper_openssl::HttpsConnector;
use serde_json;
use std::borrow::Cow;
#[allow(unused_imports)]
use std::collections::{HashMap, BTreeMap};
use std::io::{Read, Error, ErrorKind};
use std::error;
use std::fmt;
use std::path::Path;
use std::sync::Arc;
use std::str;
use std::str::FromStr;
use std::string::ToString;
use swagger;
use swagger::{ApiError, Connector, client::Service, XSpanIdString, Has, AuthData};
use url::form_urlencoded;
use url::percent_encoding::{utf8_percent_encode, PATH_SEGMENT_ENCODE_SET, QUERY_ENCODE_SET};
use mimetypes;
use models;
use header;
define_encode_set! {
/// This encode set is used for object IDs
///
/// Aside from the special characters defined in the `PATH_SEGMENT_ENCODE_SET`,
/// the vertical bar (|) is encoded.
pub ID_ENCODE_SET = [PATH_SEGMENT_ENCODE_SET] | {'|'}
}
use {Api,
OpGetResponse
};
/// Convert input into a base path, e.g. "http://example:123". Also checks the scheme as it goes.
fn into_base_path(input: &str, correct_scheme: Option<&'static str>) -> Result<String, ClientInitError> {
// First convert to Uri, since a base path is a subset of Uri.
let uri = Uri::from_str(input)?;
let scheme = uri.scheme_part().ok_or(ClientInitError::InvalidScheme)?;
// Check the scheme if necessary
if let Some(correct_scheme) = correct_scheme {
if scheme != correct_scheme {
return Err(ClientInitError::InvalidScheme);
}
}
let host = uri.host().ok_or_else(|| ClientInitError::MissingHost)?;
let port = uri.port_part().map(|x| format!(":{}", x)).unwrap_or_default();
Ok(format!("{}://{}{}", scheme, host, port))
}
/// A client that implements the API by making HTTP calls out to a server.
pub struct Client<F>
{
/// Inner service
client_service: Arc<Box<dyn Service<ReqBody=Body, Future=F> + Send + Sync>>,
/// Base path of the API
base_path: String,
}
impl<F> fmt::Debug for Client<F>
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Client {{ base_path: {} }}", self.base_path)
}
}
impl<F> Clone for Client<F>
{
fn clone(&self) -> Self {
Client {
client_service: self.client_service.clone(),
base_path: self.base_path.clone(),
}
}
}
impl Client<hyper::client::ResponseFuture>
{
/// Create a client with a custom implementation of hyper::client::Connect.
///
/// Intended for use with custom implementations of connect for e.g. protocol logging
/// or similar functionality which requires wrapping the transport layer. When wrapping a TCP connection,
/// this function should be used in conjunction with `swagger::Connector::builder()`.
///
/// For ordinary tcp connections, prefer the use of `try_new_http`, `try_new_https`
/// and `try_new_https_mutual`, to avoid introducing a dependency on the underlying transport layer.
///
/// # Arguments
///
/// * `base_path` - base path of the client API, i.e. "www.my-api-implementation.com"
/// * `protocol` - Which protocol to use when constructing the request url, e.g. `Some("http")`
/// * `connector` - Implementation of `hyper::client::Connect` to use for the client
pub fn try_new_with_connector<C>(
base_path: &str,
protocol: Option<&'static str>,
connector: C,
) -> Result<Self, ClientInitError> where
C: hyper::client::connect::Connect + 'static,
C::Transport: 'static,
C::Future: 'static,
{
let client_service = Box::new(hyper::client::Client::builder().build(connector));
Ok(Client {
client_service: Arc::new(client_service),
base_path: into_base_path(base_path, protocol)?,
})
}
/// Create an HTTP client.
///
/// # Arguments
/// * `base_path` - base path of the client API, i.e. "www.my-api-implementation.com"
pub fn try_new_http(
base_path: &str,
) -> Result<Self, ClientInitError> {
let http_connector = Connector::builder().build();
Self::try_new_with_connector(base_path, Some("http"), http_connector)
}
/// Create a client with a TLS connection to the server
///
/// # Arguments
/// * `base_path` - base path of the client API, i.e. "www.my-api-implementation.com"
pub fn try_new_https(base_path: &str) -> Result<Self, ClientInitError>
{
let https_connector = Connector::builder()
.https()
.build()
.map_err(|e| ClientInitError::SslError(e))?;
Self::try_new_with_connector(base_path, Some("https"), https_connector)
}
/// Create a client with a TLS connection to the server using a pinned certificate
///
/// # Arguments
/// * `base_path` - base path of the client API, i.e. "www.my-api-implementation.com"
/// * `ca_certificate` - Path to CA certificate used to authenticate the server
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
pub fn try_new_https_pinned<CA>(
base_path: &str,
ca_certificate: CA,
) -> Result<Self, ClientInitError>
where
CA: AsRef<Path>,
{
let https_connector = Connector::builder()
.https()
.pin_server_certificate(ca_certificate)
.build()
.map_err(|e| ClientInitError::SslError(e))?;
Self::try_new_with_connector(base_path, Some("https"), https_connector)
}
/// Create a client with a mutually authenticated TLS connection to the server.
///
/// # Arguments
/// * `base_path` - base path of the client API, i.e. "www.my-api-implementation.com"
/// * `ca_certificate` - Path to CA certificate used to authenticate the server
/// * `client_key` - Path to the client private key
/// * `client_certificate` - Path to the client's public certificate associated with the private key
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
pub fn try_new_https_mutual<CA, K, D>(
base_path: &str,
ca_certificate: CA,
client_key: K,
client_certificate: D,
) -> Result<Self, ClientInitError>
where
CA: AsRef<Path>,
K: AsRef<Path>,
D: AsRef<Path>,
{
let https_connector = Connector::builder()
.https()
.pin_server_certificate(ca_certificate)
.client_authentication(client_key, client_certificate)
.build()
.map_err(|e| ClientInitError::SslError(e))?;
Self::try_new_with_connector(base_path, Some("https"), https_connector)
}
}
impl<F> Client<F>
{
/// Constructor for creating a `Client` by passing in a pre-made `swagger::Service`
///
/// This allows adding custom wrappers around the underlying transport, for example for logging.
pub fn try_new_with_client_service(
client_service: Arc<Box<dyn Service<ReqBody=Body, Future=F> + Send + Sync>>,
base_path: &str,
) -> Result<Self, ClientInitError> {
Ok(Client {
client_service: client_service,
base_path: into_base_path(base_path, None)?,
})
}
}
/// Error type failing to create a Client
#[derive(Debug)]
pub enum ClientInitError {
/// Invalid URL Scheme
InvalidScheme,
/// Invalid URI
InvalidUri(hyper::http::uri::InvalidUri),
/// Missing Hostname
MissingHost,
/// SSL Connection Error
#[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))]
SslError(native_tls::Error),
/// SSL Connection Error
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
SslError(openssl::error::ErrorStack),
}
impl From<hyper::http::uri::InvalidUri> for ClientInitError {
fn from(err: hyper::http::uri::InvalidUri) -> ClientInitError {
ClientInitError::InvalidUri(err)
}
}
impl fmt::Display for ClientInitError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let s: &dyn fmt::Debug = self;
s.fmt(f)
}
}
impl error::Error for ClientInitError {
fn description(&self) -> &str {
"Failed to produce a hyper client."
}
}
impl<C, F> Api<C> for Client<F> where
C: Has<XSpanIdString> ,
F: Future<Item=Response<Body>, Error=hyper::Error> + Send + 'static
{
fn op_get(
&self,
param_inline_object: models::InlineObject,
context: &C) -> Box<dyn Future<Item=OpGetResponse, Error=ApiError> + Send>
{
let mut uri = format!(
"{}/op",
self.base_path
);
// Query parameters
let mut query_string = url::form_urlencoded::Serializer::new("".to_owned());
let query_string_str = query_string.finish();
if !query_string_str.is_empty() {
uri += "?";
uri += &query_string_str;
}
let uri = match Uri::from_str(&uri) {
Ok(uri) => uri,
Err(err) => return Box::new(future::err(ApiError(format!("Unable to build URI: {}", err)))),
};
let mut request = match hyper::Request::builder()
.method("GET")
.uri(uri)
.body(Body::empty()) {
Ok(req) => req,
Err(e) => return Box::new(future::err(ApiError(format!("Unable to create request: {}", e))))
};
// Body parameter
let body = serde_json::to_string(&param_inline_object).expect("impossible to fail to serialize");
*request.body_mut() = Body::from(body);
let header = &mimetypes::requests::OP_GET;
request.headers_mut().insert(CONTENT_TYPE, match HeaderValue::from_str(header) {
Ok(h) => h,
Err(e) => return Box::new(future::err(ApiError(format!("Unable to create header: {} - {}", header, e))))
});
let header = HeaderValue::from_str((context as &dyn Has<XSpanIdString>).get().0.clone().to_string().as_str());
request.headers_mut().insert(HeaderName::from_static("x-span-id"), match header {
Ok(h) => h,
Err(e) => return Box::new(future::err(ApiError(format!("Unable to create X-Span ID header value: {}", e))))
});
Box::new(self.client_service.request(request)
.map_err(|e| ApiError(format!("No response received: {}", e)))
.and_then(|mut response| {
match response.status().as_u16() {
200 => {
let body = response.into_body();
Box::new(
future::ok(
OpGetResponse::OK
)
) as Box<dyn Future<Item=_, Error=_> + Send>
},
code => {
let headers = response.headers().clone();
Box::new(response.into_body()
.take(100)
.concat2()
.then(move |body|
future::err(ApiError(format!("Unexpected response code {}:\n{:?}\n\n{}",
code,
headers,
match body {
Ok(ref body) => match str::from_utf8(body) {
Ok(body) => Cow::from(body),
Err(e) => Cow::from(format!("<Body was not UTF8: {:?}>", e)),
},
Err(e) => Cow::from(format!("<Failed to read body: {}>", e)),
})))
)
) as Box<dyn Future<Item=_, Error=_> + Send>
}
}
}))
}
}

View File

@ -0,0 +1,122 @@
use futures::Future;
use hyper;
use hyper::header::HeaderName;
use hyper::{Error, Request, Response, StatusCode, service::Service, body::Payload};
use url::form_urlencoded;
use std::default::Default;
use std::io;
use std::marker::PhantomData;
use swagger::auth::{AuthData, Authorization, Bearer, Scopes};
use swagger::context::ContextualPayload;
use swagger::{EmptyContext, Has, Pop, Push, XSpanIdString};
use Api;
pub struct MakeAddContext<T, A> {
inner: T,
marker: PhantomData<A>,
}
impl<T, A, B, C, D> MakeAddContext<T, A>
where
A: Default + Push<XSpanIdString, Result = B>,
B: Push<Option<AuthData>, Result = C>,
C: Push<Option<Authorization>, Result = D>,
{
pub fn new(inner: T) -> MakeAddContext<T, A> {
MakeAddContext {
inner,
marker: PhantomData,
}
}
}
// Make a service that adds context.
impl<'a, T, SC, A, B, C, D, E, ME, S, OB, F> hyper::service::MakeService<&'a SC> for
MakeAddContext<T, A>
where
A: Default + Push<XSpanIdString, Result = B>,
B: Push<Option<AuthData>, Result = C>,
C: Push<Option<Authorization>, Result = D>,
D: Send + 'static,
T: hyper::service::MakeService<
&'a SC,
Error = E,
MakeError = ME,
Service = S,
ReqBody = ContextualPayload<hyper::Body, D>,
ResBody = OB,
Future = F
>,
S: Service<
Error = E,
ReqBody = ContextualPayload<hyper::Body, D>,
ResBody = OB> + 'static,
ME: swagger::ErrorBound,
E: swagger::ErrorBound,
F: Future<Item=S, Error=ME> + Send + 'static,
S::Future: Send,
OB: Payload,
{
type ReqBody = hyper::Body;
type ResBody = OB;
type Error = E;
type MakeError = ME;
type Service = AddContext<S, A>;
type Future = Box<dyn Future<Item = Self::Service, Error = ME> + Send + 'static>;
fn make_service(&mut self, ctx: &'a SC) -> Self::Future {
Box::new(self.inner.make_service(ctx).map(|s| AddContext::new(s)))
}
}
/// Middleware to extract authentication data from request
pub struct AddContext<T, A> {
inner: T,
marker: PhantomData<A>,
}
impl<T, A, B, C, D> AddContext<T, A>
where
A: Default + Push<XSpanIdString, Result = B>,
B: Push<Option<AuthData>, Result = C>,
C: Push<Option<Authorization>, Result = D>,
T: Service,
{
pub fn new(inner: T) -> AddContext<T, A> {
AddContext {
inner,
marker: PhantomData,
}
}
}
impl<T, A, B, C, D> Service for AddContext<T, A>
where
A: Default + Push<XSpanIdString, Result=B>,
B: Push<Option<AuthData>, Result=C>,
C: Push<Option<Authorization>, Result=D>,
D: Send + 'static,
T: Service<ReqBody = ContextualPayload<hyper::Body, D>>,
T::Future: Future<Item=Response<T::ResBody>, Error=T::Error> + Send + 'static
{
type ReqBody = hyper::Body;
type ResBody = T::ResBody;
type Error = T::Error;
type Future = Box<dyn Future<Item=Response<T::ResBody>, Error=T::Error> + Send + 'static>;
fn call(&mut self, req: Request<Self::ReqBody>) -> Self::Future {
let context = A::default().push(XSpanIdString::get_or_generate(&req));
let (head, body) = req.into_parts();
let headers = head.headers.clone();
let context = context.push(None::<AuthData>);
let context = context.push(None::<Authorization>);
let body = ContextualPayload {
inner: body,
context: context,
};
Box::new(self.inner.call(hyper::Request::from_parts(head, body)))
}
}

View File

@ -0,0 +1,99 @@
use chrono::{DateTime, Utc};
use hyper::header::HeaderValue;
use std::fmt;
use std::ops::Deref;
/// A struct to allow homogeneous conversion into a HeaderValue. We can't
/// implement the From/Into trait on HeaderValue because we don't own
/// either of the types.
#[derive(Debug, Clone)]
pub struct IntoHeaderValue<T>(pub T);
// Generic implementations
impl<T> Deref for IntoHeaderValue<T> {
type Target = T;
fn deref(&self) -> &T {
&self.0
}
}
// Derive for each From<T> in hyper::header::HeaderValue
macro_rules! ihv_generate {
($t:ident) => {
impl From<HeaderValue> for IntoHeaderValue<$t> {
fn from(hdr_value: HeaderValue) -> Self {
IntoHeaderValue(hdr_value.to_str().unwrap().parse::<$t>().unwrap())
}
}
impl From<IntoHeaderValue<$t>> for HeaderValue {
fn from(hdr_value: IntoHeaderValue<$t>) -> Self {
hdr_value.0.into()
}
}
};
}
ihv_generate!(u64);
ihv_generate!(i64);
ihv_generate!(i16);
ihv_generate!(u16);
ihv_generate!(u32);
ihv_generate!(usize);
ihv_generate!(isize);
ihv_generate!(i32);
// Custom derivations
impl From<HeaderValue> for IntoHeaderValue<Vec<String>> {
fn from(hdr_value: HeaderValue) -> Self {
IntoHeaderValue(
hdr_value
.to_str()
.unwrap()
.split(',')
.filter_map(|x| match x.trim() {
"" => None,
y => Some(y.to_string()),
})
.collect(),
)
}
}
impl From<IntoHeaderValue<Vec<String>>> for HeaderValue {
fn from(hdr_value: IntoHeaderValue<Vec<String>>) -> Self {
HeaderValue::from_str(&hdr_value.0.join(", ")).unwrap()
}
}
impl From<HeaderValue> for IntoHeaderValue<String> {
fn from(hdr_value: HeaderValue) -> Self {
IntoHeaderValue(hdr_value.to_str().unwrap().to_string())
}
}
impl From<IntoHeaderValue<String>> for HeaderValue {
fn from(hdr_value: IntoHeaderValue<String>) -> Self {
HeaderValue::from_str(&hdr_value.0).unwrap()
}
}
impl From<HeaderValue> for IntoHeaderValue<DateTime<Utc>> {
fn from(hdr_value: HeaderValue) -> Self {
IntoHeaderValue(
DateTime::parse_from_rfc3339(hdr_value.to_str().unwrap())
.unwrap()
.with_timezone(&Utc),
)
}
}
impl From<IntoHeaderValue<DateTime<Utc>>> for HeaderValue {
fn from(hdr_value: IntoHeaderValue<DateTime<Utc>>) -> Self {
HeaderValue::from_str(hdr_value.0.to_rfc3339().as_str()).unwrap()
}
}

View File

@ -0,0 +1,138 @@
#![allow(missing_docs, trivial_casts, unused_variables, unused_mut, unused_imports, unused_extern_crates, non_camel_case_types)]
// Crates with macros
#[macro_use]
extern crate serde_derive;
#[cfg(feature = "server")]
#[macro_use]
extern crate lazy_static;
#[cfg(any(feature = "client", feature = "server"))]
#[macro_use]
extern crate url;
#[macro_use]
extern crate log;
// Crates for conversion support
#[cfg(feature = "conversion")]
#[macro_use]
extern crate frunk_derives;
#[cfg(feature = "conversion")]
#[macro_use]
extern crate frunk_enum_derive;
#[cfg(feature = "conversion")]
extern crate frunk_core;
extern crate mime;
extern crate serde;
extern crate futures;
extern crate chrono;
extern crate swagger;
#[cfg(any(feature = "client", feature = "server"))]
extern crate hyper;
#[cfg(feature = "server")]
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
extern crate hyper_openssl;
#[cfg(feature = "server")]
extern crate percent_encoding;
#[cfg(any(feature = "client", feature = "server"))]
extern crate serde_json;
#[cfg(any(feature = "client", feature = "server"))]
extern crate serde_ignored;
#[cfg(any(feature = "client", feature = "server"))]
extern crate tokio;
#[cfg(any(feature = "client", feature = "server"))]
#[cfg(any(feature = "client", feature = "server"))]
use hyper::header::HeaderValue;
use futures::Stream;
use std::io::Error;
#[allow(unused_imports)]
use std::collections::HashMap;
#[cfg(any(feature = "client", feature = "server"))]
mod mimetypes;
#[deprecated(note = "Import swagger-rs directly")]
pub use swagger::{ApiError, ContextWrapper};
#[deprecated(note = "Import futures directly")]
pub use futures::Future;
pub const BASE_PATH: &'static str = "";
pub const API_VERSION: &'static str = "0.0.1";
#[derive(Debug, PartialEq)]
pub enum OpGetResponse {
/// OK
OK
}
/// API
pub trait Api<C> {
fn op_get(
&self,
inline_object: models::InlineObject,
context: &C) -> Box<dyn Future<Item=OpGetResponse, Error=ApiError> + Send>;
}
/// API without a `Context`
pub trait ApiNoContext {
fn op_get(
&self,
inline_object: models::InlineObject,
) -> Box<dyn Future<Item=OpGetResponse, Error=ApiError> + Send>;
}
/// Trait to extend an API to make it easy to bind it to a context.
pub trait ContextWrapperExt<'a, C> where Self: Sized {
/// Binds this API to a context.
fn with_context(self: &'a Self, context: C) -> ContextWrapper<'a, Self, C>;
}
impl<'a, T: Api<C> + Sized, C> ContextWrapperExt<'a, C> for T {
fn with_context(self: &'a T, context: C) -> ContextWrapper<'a, T, C> {
ContextWrapper::<T, C>::new(self, context)
}
}
impl<'a, T: Api<C>, C> ApiNoContext for ContextWrapper<'a, T, C> {
fn op_get(
&self,
inline_object: models::InlineObject,
) -> Box<dyn Future<Item=OpGetResponse, Error=ApiError> + Send>
{
self.api().op_get(inline_object, &self.context())
}
}
#[cfg(feature = "client")]
pub mod client;
// Re-export Client as a top-level name
#[cfg(feature = "client")]
pub use client::Client;
#[cfg(feature = "server")]
pub mod server;
// Re-export router() as a top-level name
#[cfg(feature = "server")]
pub use self::server::Service;
#[cfg(feature = "server")]
pub mod context;
pub mod models;
pub mod header;

View File

@ -0,0 +1,11 @@
/// mime types for requests and responses
pub mod responses {
}
pub mod requests {
/// Create &str objects for the request content types for OpGet
pub static OP_GET: &str = "application/json";
}

View File

@ -0,0 +1,105 @@
#![allow(unused_imports, unused_qualifications)]
use serde::ser::Serializer;
use std::collections::HashMap;
use models;
use swagger;
use hyper::header::HeaderValue;
use std::string::ParseError;
use std::str::FromStr;
use header::IntoHeaderValue;
// Methods for converting between IntoHeaderValue<InlineObject> and HeaderValue
impl From<IntoHeaderValue<InlineObject>> for HeaderValue {
fn from(hdr_value: IntoHeaderValue<InlineObject>) -> Self {
HeaderValue::from_str(&hdr_value.to_string()).unwrap()
}
}
impl From<HeaderValue> for IntoHeaderValue<InlineObject> {
fn from(hdr_value: HeaderValue) -> Self {
IntoHeaderValue(InlineObject::from_str(hdr_value.to_str().unwrap()).unwrap())
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "conversion", derive(LabelledGeneric))]
pub struct InlineObject {
#[serde(rename = "propery")]
#[serde(skip_serializing_if="Option::is_none")]
pub propery: Option<String>,
}
impl InlineObject {
pub fn new() -> InlineObject {
InlineObject {
propery: None,
}
}
}
/// Converts the InlineObject value to the Query Parameters representation (style=form, explode=false)
/// specified in https://swagger.io/docs/specification/serialization/
/// Should be implemented in a serde serializer
impl ::std::string::ToString for InlineObject {
fn to_string(&self) -> String {
let mut params: Vec<String> = vec![];
if let Some(ref propery) = self.propery {
params.push("propery".to_string());
params.push(propery.to_string());
}
params.join(",").to_string()
}
}
/// Converts Query Parameters representation (style=form, explode=false) to a InlineObject value
/// as specified in https://swagger.io/docs/specification/serialization/
/// Should be implemented in a serde deserializer
impl ::std::str::FromStr for InlineObject {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
#[derive(Default)]
// An intermediate representation of the struct to use for parsing.
struct IntermediateRep {
pub propery: Vec<String>,
}
let mut intermediate_rep = IntermediateRep::default();
// Parse into intermediate representation
let mut string_iter = s.split(',').into_iter();
let mut key_result = string_iter.next();
while key_result.is_some() {
let val = match string_iter.next() {
Some(x) => x,
None => return Err("Missing value while parsing InlineObject".to_string())
};
if let Some(key) = key_result {
match key {
"propery" => intermediate_rep.propery.push(String::from_str(val).map_err(|x| format!("{}", x))?),
_ => return Err("Unexpected key while parsing InlineObject".to_string())
}
}
// Get the next key
key_result = string_iter.next();
}
// Use the intermediate representation to return the struct
Ok(InlineObject {
propery: intermediate_rep.propery.into_iter().next(),
})
}
}

View File

@ -0,0 +1,235 @@
#[allow(unused_imports)]
use std::collections::{HashMap, BTreeMap, BTreeSet};
use std::marker::PhantomData;
use futures::{Future, future, Stream, stream};
use hyper;
use hyper::{Request, Response, Error, StatusCode, Body, HeaderMap};
use hyper::header::{HeaderName, HeaderValue, CONTENT_TYPE};
use url::form_urlencoded;
use mimetypes;
use serde_json;
use std::io;
#[allow(unused_imports)]
use swagger;
use swagger::{ApiError, XSpanIdString, Has, RequestParser};
pub use swagger::auth::Authorization;
use swagger::auth::Scopes;
use swagger::context::ContextualPayload;
#[allow(unused_imports)]
use models;
use header;
pub use crate::context;
use {Api,
OpGetResponse
};
mod paths {
extern crate regex;
lazy_static! {
pub static ref GLOBAL_REGEX_SET: regex::RegexSet = regex::RegexSet::new(vec![
r"^/op$"
])
.expect("Unable to create global regex set");
}
pub static ID_OP: usize = 0;
}
pub struct MakeService<T, RC> {
api_impl: T,
marker: PhantomData<RC>,
}
impl<T, RC> MakeService<T, RC>
where
T: Api<RC> + Clone + Send + 'static,
RC: Has<XSpanIdString> + 'static
{
pub fn new(api_impl: T) -> Self {
MakeService {
api_impl,
marker: PhantomData
}
}
}
impl<'a, T, SC, RC> hyper::service::MakeService<&'a SC> for MakeService<T, RC>
where
T: Api<RC> + Clone + Send + 'static,
RC: Has<XSpanIdString> + 'static + Send
{
type ReqBody = ContextualPayload<Body, RC>;
type ResBody = Body;
type Error = Error;
type Service = Service<T, RC>;
type Future = future::FutureResult<Self::Service, Self::MakeError>;
type MakeError = Error;
fn make_service(&mut self, _ctx: &'a SC) -> Self::Future {
future::FutureResult::from(Ok(Service::new(
self.api_impl.clone(),
)))
}
}
type ServiceFuture = Box<dyn Future<Item = Response<Body>, Error = Error> + Send>;
fn method_not_allowed() -> ServiceFuture {
Box::new(future::ok(
Response::builder().status(StatusCode::METHOD_NOT_ALLOWED)
.body(Body::empty())
.expect("Unable to create Method Not Allowed response")
))
}
pub struct Service<T, RC> {
api_impl: T,
marker: PhantomData<RC>,
}
impl<T, RC> Service<T, RC>
where
T: Api<RC> + Clone + Send + 'static,
RC: Has<XSpanIdString> + 'static {
pub fn new(api_impl: T) -> Self {
Service {
api_impl: api_impl,
marker: PhantomData
}
}
}
impl<T, C> hyper::service::Service for Service<T, C>
where
T: Api<C> + Clone + Send + 'static,
C: Has<XSpanIdString> + 'static + Send
{
type ReqBody = ContextualPayload<Body, C>;
type ResBody = Body;
type Error = Error;
type Future = ServiceFuture;
fn call(&mut self, req: Request<Self::ReqBody>) -> Self::Future {
let api_impl = self.api_impl.clone();
let (parts, body) = req.into_parts();
let (method, uri, headers) = (parts.method, parts.uri, parts.headers);
let path = paths::GLOBAL_REGEX_SET.matches(uri.path());
let mut context = body.context;
let body = body.inner;
match &method {
// OpGet - GET /op
&hyper::Method::GET if path.matched(paths::ID_OP) => {
// Body parameters (note that non-required body parameters will ignore garbage
// values, rather than causing a 400 response). Produce warning header and logs for
// any unused fields.
Box::new(body.concat2()
.then(move |result| -> Self::Future {
match result {
Ok(body) => {
let mut unused_elements = Vec::new();
let param_inline_object: Option<models::InlineObject> = if !body.is_empty() {
let deserializer = &mut serde_json::Deserializer::from_slice(&*body);
match serde_ignored::deserialize(deserializer, |path| {
warn!("Ignoring unknown field in body: {}", path);
unused_elements.push(path.to_string());
}) {
Ok(param_inline_object) => param_inline_object,
Err(e) => return Box::new(future::ok(Response::builder()
.status(StatusCode::BAD_REQUEST)
.body(Body::from(format!("Couldn't parse body parameter InlineObject - doesn't match schema: {}", e)))
.expect("Unable to create Bad Request response for invalid body parameter InlineObject due to schema"))),
}
} else {
None
};
let param_inline_object = match param_inline_object {
Some(param_inline_object) => param_inline_object,
None => return Box::new(future::ok(Response::builder()
.status(StatusCode::BAD_REQUEST)
.body(Body::from("Missing required body parameter InlineObject"))
.expect("Unable to create Bad Request response for missing body parameter InlineObject"))),
};
Box::new(
api_impl.op_get(
param_inline_object,
&context
).then(move |result| {
let mut response = Response::new(Body::empty());
response.headers_mut().insert(
HeaderName::from_static("x-span-id"),
HeaderValue::from_str((&context as &dyn Has<XSpanIdString>).get().0.clone().to_string().as_str())
.expect("Unable to create X-Span-ID header value"));
if !unused_elements.is_empty() {
response.headers_mut().insert(
HeaderName::from_static("warning"),
HeaderValue::from_str(format!("Ignoring unknown fields in body: {:?}", unused_elements).as_str())
.expect("Unable to create Warning header value"));
}
match result {
Ok(rsp) => match rsp {
OpGetResponse::OK
=> {
*response.status_mut() = StatusCode::from_u16(200).expect("Unable to turn 200 into a StatusCode");
},
},
Err(_) => {
// Application code returned an error. This should not happen, as the implementation should
// return a valid response.
*response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR;
*response.body_mut() = Body::from("An internal error occurred");
},
}
future::ok(response)
}
))
},
Err(e) => Box::new(future::ok(Response::builder()
.status(StatusCode::BAD_REQUEST)
.body(Body::from(format!("Couldn't read body parameter InlineObject: {}", e)))
.expect("Unable to create Bad Request response due to unable to read body parameter InlineObject"))),
}
})
) as Self::Future
},
_ if path.matched(paths::ID_OP) => method_not_allowed(),
_ => Box::new(future::ok(
Response::builder().status(StatusCode::NOT_FOUND)
.body(Body::empty())
.expect("Unable to create Not Found response")
)) as Self::Future
}
}
}
impl<T, C> Clone for Service<T, C> where T: Clone
{
fn clone(&self) -> Self {
Service {
api_impl: self.api_impl.clone(),
marker: self.marker.clone(),
}
}
}
/// Request parser for `Api`.
pub struct ApiRequestParser;
impl<T> RequestParser<T> for ApiRequestParser {
fn parse_operation_id(request: &Request<T>) -> Result<&'static str, ()> {
let path = paths::GLOBAL_REGEX_SET.matches(request.uri().path());
match request.method() {
// OpGet - GET /op
&hyper::Method::GET if path.matched(paths::ID_OP) => Ok("OpGet"),
_ => Err(()),
}
}
}

View File

@ -3,6 +3,9 @@ extern crate openapi_v3;
extern crate clap;
extern crate env_logger;
extern crate futures;
// log may be unused if there are no examples
#[allow(unused_imports)]
#[macro_use]
extern crate log;
#[macro_use]
@ -47,8 +50,13 @@ use openapi_v3::{Api, ApiNoContext, Client, ContextWrapperExt,
XmlPutResponse
};
use clap::{App, Arg};
// swagger::Has may be unused if there are no examples
#[allow(unused_imports)]
use swagger::{ContextBuilder, EmptyContext, XSpanIdString, Has, Push, AuthData};
// rt may be unused if there are no examples
#[allow(unused_mut)]
fn main() {
env_logger::init();

View File

@ -3,6 +3,9 @@ extern crate ops_v3;
extern crate clap;
extern crate env_logger;
extern crate futures;
// log may be unused if there are no examples
#[allow(unused_imports)]
#[macro_use]
extern crate log;
#[macro_use]
@ -53,8 +56,13 @@ use ops_v3::{Api, ApiNoContext, Client, ContextWrapperExt,
Op9GetResponse
};
use clap::{App, Arg};
// swagger::Has may be unused if there are no examples
#[allow(unused_imports)]
use swagger::{ContextBuilder, EmptyContext, XSpanIdString, Has, Push, AuthData};
// rt may be unused if there are no examples
#[allow(unused_mut)]
fn main() {
env_logger::init();

View File

@ -3,6 +3,9 @@ extern crate petstore_with_fake_endpoints_models_for_testing;
extern crate clap;
extern crate env_logger;
extern crate futures;
// log may be unused if there are no examples
#[allow(unused_imports)]
#[macro_use]
extern crate log;
#[macro_use]
@ -51,8 +54,13 @@ use petstore_with_fake_endpoints_models_for_testing::{Api, ApiNoContext, Client,
UpdateUserResponse
};
use clap::{App, Arg};
// swagger::Has may be unused if there are no examples
#[allow(unused_imports)]
use swagger::{ContextBuilder, EmptyContext, XSpanIdString, Has, Push, AuthData};
// rt may be unused if there are no examples
#[allow(unused_mut)]
fn main() {
env_logger::init();

View File

@ -3,6 +3,9 @@ extern crate rust_server_test;
extern crate clap;
extern crate env_logger;
extern crate futures;
// log may be unused if there are no examples
#[allow(unused_imports)]
#[macro_use]
extern crate log;
#[macro_use]
@ -25,8 +28,13 @@ use rust_server_test::{Api, ApiNoContext, Client, ContextWrapperExt,
SoloObjectPostResponse
};
use clap::{App, Arg};
// swagger::Has may be unused if there are no examples
#[allow(unused_imports)]
use swagger::{ContextBuilder, EmptyContext, XSpanIdString, Has, Push, AuthData};
// rt may be unused if there are no examples
#[allow(unused_mut)]
fn main() {
env_logger::init();