forked from loafle/openapi-generator-original
[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:
parent
b0520a346d
commit
be983b5212
@ -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();
|
||||
|
||||
|
@ -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'
|
@ -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();
|
||||
|
||||
|
@ -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
|
||||
]
|
2
samples/server/petstore/rust-server/output/no-example-v3/.gitignore
vendored
Normal file
2
samples/server/petstore/rust-server/output/no-example-v3/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
target
|
||||
Cargo.lock
|
@ -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
|
@ -0,0 +1 @@
|
||||
5.0.0-SNAPSHOT
|
@ -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"]
|
@ -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
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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-----
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
@ -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-----
|
@ -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-----
|
@ -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")));
|
||||
}
|
@ -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()))
|
||||
}
|
||||
|
||||
}
|
@ -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(¶m_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>
|
||||
}
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
}
|
@ -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)))
|
||||
}
|
||||
}
|
@ -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()
|
||||
}
|
||||
}
|
@ -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;
|
@ -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";
|
||||
|
||||
}
|
@ -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(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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(()),
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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();
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user