diff options
Diffstat (limited to 'rust')
25 files changed, 10449 insertions, 39 deletions
diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 083434d1..42759107 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -15,6 +15,19 @@ dependencies = [ ] [[package]] +name = "arrayvec" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ascii" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] name = "atty" version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -25,6 +38,27 @@ dependencies = [ ] [[package]] +name = "backtrace" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-demangle 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "backtrace-sys" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "base64" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -42,22 +76,68 @@ dependencies = [ ] [[package]] +name = "base64" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bitflags" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] name = "bitflags" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] +name = "buf_redux" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "byteorder" version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] +name = "bytes" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cc" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] name = "cfg-if" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "chrono" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "chrono" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ @@ -77,6 +157,11 @@ dependencies = [ ] [[package]] +name = "chunked_transfer" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] name = "clap" version = "2.31.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -99,6 +184,53 @@ dependencies = [ ] [[package]] +name = "core-foundation" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "core-foundation-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "core-foundation-sys" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-deque" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-epoch 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-utils" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "diesel" version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -133,6 +265,63 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] +name = "encoding" +version = "0.2.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "encoding-index-japanese 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)", + "encoding-index-korean 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)", + "encoding-index-simpchinese 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)", + "encoding-index-singlebyte 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)", + "encoding-index-tradchinese 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "encoding-index-japanese" +version = "1.20141219.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "encoding_index_tests 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "encoding-index-korean" +version = "1.20141219.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "encoding_index_tests 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "encoding-index-simpchinese" +version = "1.20141219.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "encoding_index_tests 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "encoding-index-singlebyte" +version = "1.20141219.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "encoding_index_tests 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "encoding-index-tradchinese" +version = "1.20141219.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "encoding_index_tests 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "encoding_index_tests" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] name = "error" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -142,6 +331,14 @@ dependencies = [ ] [[package]] +name = "error-chain" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "fatcat" version = "0.1.0" dependencies = [ @@ -149,15 +346,63 @@ dependencies = [ "clap 2.31.2 (registry+https://github.com/rust-lang/crates.io-index)", "diesel 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "dotenv 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "fatcat-api 0.1.0", "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.11.26 (registry+https://github.com/rust-lang/crates.io-index)", "iron 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "iron-slog 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "iron-test 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "swagger 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "swagger 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-proto 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] +name = "fatcat-api" +version = "0.1.0" +dependencies = [ + "chrono 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "clap 2.31.2 (registry+https://github.com/rust-lang/crates.io-index)", + "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.11.26 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper-tls 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "mime 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "multipart 0.13.6 (registry+https://github.com/rust-lang/crates.io-index)", + "native-tls 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl 0.9.24 (registry+https://github.com/rust-lang/crates.io-index)", + "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_ignored 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_urlencoded 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "swagger 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-proto 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-tls 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "uuid 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] name = "fuchsia-zircon" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -177,6 +422,15 @@ version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] +name = "futures-cpupool" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "httparse" version = "1.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -200,6 +454,45 @@ dependencies = [ ] [[package]] +name = "hyper" +version = "0.11.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "base64 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "httparse 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "mime 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)", + "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "relay 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-proto 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicase 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "hyper-tls" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.11.26 (registry+https://github.com/rust-lang/crates.io-index)", + "native-tls 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-tls 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "idna" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -210,6 +503,15 @@ dependencies = [ ] [[package]] +name = "iovec" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "iron" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -265,10 +567,24 @@ dependencies = [ [[package]] name = "itoa" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "itoa" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "language-tags" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -284,6 +600,11 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] +name = "lazycell" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] name = "libc" version = "0.2.40" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -311,6 +632,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "memchr" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "memchr" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ @@ -318,6 +647,11 @@ dependencies = [ ] [[package]] +name = "memoffset" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] name = "mime" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -326,6 +660,14 @@ dependencies = [ ] [[package]] +name = "mime" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicase 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "mime_guess" version = "1.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -337,11 +679,88 @@ dependencies = [ ] [[package]] +name = "mio" +version = "0.6.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazycell 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "miow" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "modifier" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] +name = "multipart" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "buf_redux 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "httparse 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)", + "iron 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "mime_guess 1.8.4 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", + "safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "tiny_http 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)", + "twoway 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "native-tls" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl 0.9.24 (registry+https://github.com/rust-lang/crates.io-index)", + "schannel 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", + "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "net2" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "nodrop" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] name = "num" version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -382,6 +801,29 @@ dependencies = [ ] [[package]] +name = "openssl" +version = "0.9.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", + "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.30 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "openssl-sys" +version = "0.9.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)", + "vcpkg 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "percent-encoding" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -422,6 +864,11 @@ dependencies = [ ] [[package]] +name = "pkg-config" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] name = "plugin" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -523,6 +970,27 @@ dependencies = [ ] [[package]] +name = "relay" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "remove_dir_all" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] name = "rustc-serialize" version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -533,6 +1001,45 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] +name = "schannel" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "scoped-tls" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "scopeguard" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "security-framework" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "core-foundation 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "security-framework-sys" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "core-foundation-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "serde" version = "1.0.55" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -548,6 +1055,14 @@ dependencies = [ ] [[package]] +name = "serde_ignored" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "serde_json" version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -558,31 +1073,62 @@ dependencies = [ ] [[package]] +name = "serde_urlencoded" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "siphasher" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] +name = "slab" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "slab" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] name = "slog" version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] +name = "smallvec" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] name = "strsim" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "swagger" -version = "0.7.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "base64 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)", - "iron 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.11.26 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper-tls 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "native-tls 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl 0.9.24 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", + "slog 2.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -606,6 +1152,20 @@ dependencies = [ ] [[package]] +name = "take" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "tempdir" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "termion" version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -643,11 +1203,193 @@ dependencies = [ ] [[package]] +name = "tiny_http" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ascii 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "chrono 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)", + "chunked_transfer 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "encoding 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "url 0.2.38 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-fs 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-tcp 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-threadpool 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-timer 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-udp 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-core" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)", + "scoped-tls 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-timer 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-executor" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-fs" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-threadpool 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-io" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-proto" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "take 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-reactor" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-service" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-tcp" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-threadpool" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-deque 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-timer" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-tls" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "native-tls 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-proto 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-udp" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "traitobject" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] +name = "twoway" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "typeable" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -674,6 +1416,14 @@ dependencies = [ ] [[package]] +name = "unicase" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "unicode-bidi" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -714,6 +1464,16 @@ dependencies = [ [[package]] name = "url" +version = "0.2.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", + "uuid 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "url" version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ @@ -729,10 +1489,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "uuid" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "uuid" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -757,6 +1527,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ @@ -765,6 +1540,11 @@ dependencies = [ ] [[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -774,56 +1554,110 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "ws2_32-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [metadata] "checksum aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d6531d44de723825aa81398a6415283229725a00fa30713812ab9323faa82fc4" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +"checksum arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef" +"checksum ascii 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3ae7d751998c189c1d4468cf0a39bb2eae052a9c58d50ebb3b9591ee3813ad50" "checksum atty 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "2fc4a1aa4c24c0718a250f0681885c1af91419d242f29eb8f2ab28502d80dbd1" +"checksum backtrace 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea58cd16fd6c9d120b5bcb01d63883ae4cc7ba2aed35c1841b862a3c7ef6639" +"checksum backtrace-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "44585761d6161b0f57afc49482ab6bd067e4edef48c12a152c237eb0203f7661" "checksum base64 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "30e93c03064e7590d0466209155251b90c22e37fab1daf2771582598b5827557" "checksum base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "96434f987501f0ed4eb336a411e0631ecd1afa11574fe148587adc4ff96143c9" +"checksum base64 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9263aa6a38da271eec5c91a83ce1e800f093c8535788d403d626d8d5c3f8f007" +"checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5" "checksum bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d0c54bb8f454c567f21197eefcdbf5679d0bd99f2ddbe52e84c77061952e6789" +"checksum buf_redux 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b9279646319ff816b05fb5897883ece50d7d854d12b59992683d4f8a71b0f949" "checksum byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "73b5bdfe7ee3ad0b99c9801d58807a9dbc9e09196365b0203853b99889ab3c87" +"checksum bytes 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "2f1d50c876fb7545f5f289cd8b2aee3f359d073ae819eed5d6373638e2c61e59" +"checksum cc 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)" = "0ebb87d1116151416c0cf66a0e3fb6430cccd120fd6300794b4dfaa050ac40ba" "checksum cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "405216fd8fe65f718daa7102ea808a946b6ce40c742998fbfd3463645552de18" +"checksum chrono 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)" = "9213f7cd7c27e95c2b57c49f0e69b1ea65b27138da84a170133fd21b07659c00" "checksum chrono 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "158b0bd7d75cbb6bf9c25967a48a2e9f77da95876b858eadfabaa99cd069de6e" "checksum chrono 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1cce36c92cb605414e9b824f866f5babe0a0368e39ea07393b9b63cf3844c0e6" +"checksum chunked_transfer 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "498d20a7aaf62625b9bf26e637cf7736417cde1d0c99f1d04d1170229a85cf87" "checksum clap 2.31.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f0f16b89cbb9ee36d87483dc939fe9f1e13c05898d56d7b230a0d4dff033a536" "checksum conduit-mime-types 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "95ca30253581af809925ef68c2641cc140d6183f43e12e0af4992d53768bd7b8" +"checksum core-foundation 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "25bfd746d203017f7d5cbd31ee5d8e17f94b6521c7af77ece6c9e4b2d4b16c67" +"checksum core-foundation-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "065a5d7ffdcbc8fa145d6f0746f3555025b9097a9e9cda59f7467abae670c78d" +"checksum crossbeam-deque 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fe8153ef04a7594ded05b427ffad46ddeaf22e63fd48d42b3e1e3bb4db07cae7" +"checksum crossbeam-epoch 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9b4e2817eb773f770dcb294127c011e22771899c21d18fce7dd739c0b9832e81" +"checksum crossbeam-utils 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d636a8b3bcc1b409d7ffd3facef8f21dcb4009626adbd0c5e6c4305c07253c7b" "checksum diesel 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "24815a0c2094f2c8dafe74ab3b9e975892f44acbb94b4d4b4898025a7615efa4" "checksum diesel_derives 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6471a2b637b414d3ee1504cf230409a550381c79204282f8fe06c527e4ae56be" "checksum dotenv 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "400b347fe65ccfbd8f545c9d9a75d04b0caf23fec49aaa838a9a05398f94c019" "checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab" +"checksum encoding 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "6b0d943856b990d12d3b55b359144ff341533e516d94098b1d3fc1ac666d36ec" +"checksum encoding-index-japanese 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)" = "04e8b2ff42e9a05335dbf8b5c6f7567e5591d0d916ccef4e0b1710d32a0d0c91" +"checksum encoding-index-korean 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)" = "4dc33fb8e6bcba213fe2f14275f0963fd16f0a02c878e3095ecfdf5bee529d81" +"checksum encoding-index-simpchinese 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d87a7194909b9118fc707194baa434a4e3b0fb6a5a757c73c3adb07aa25031f7" +"checksum encoding-index-singlebyte 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3351d5acffb224af9ca265f435b859c7c01537c0849754d3db3fdf2bfe2ae84a" +"checksum encoding-index-tradchinese 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fd0e20d5688ce3cab59eb3ef3a2083a5c77bf496cb798dc6fcdb75f323890c18" +"checksum encoding_index_tests 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a246d82be1c9d791c5dfde9a2bd045fc3cbba3fa2b11ad558f27d01712f00569" "checksum error 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "a6e606f14042bb87cc02ef6a14db6c90ab92ed6f62d87e69377bc759fd7987cc" +"checksum error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff511d5dc435d703f4971bc399647c9bc38e20cb41452e3b9feb4765419ed3f3" +"checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +"checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" "checksum futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)" = "1a70b146671de62ec8c8ed572219ca5d594d9b06c0b364d5e67b722fc559b48c" +"checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" "checksum httparse 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c2f407128745b78abc95c0ffbe4e5d37427fdc0d45470710cfef8c44522a2e37" "checksum hyper 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)" = "368cb56b2740ebf4230520e2b90ebb0461e69034d85d1945febd9b3971426db2" +"checksum hyper 0.11.26 (registry+https://github.com/rust-lang/crates.io-index)" = "66b16eb6213713f3c72d0ed14ce56423ae84dced8df73d2a2c8675f0495ae7ea" +"checksum hyper-tls 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a5aa51f6ae9842239b0fac14af5f22123b8432b4cc774a44ff059fcba0f675ca" "checksum idna 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "014b298351066f1512874135335d62a789ffe78a9974f94b43ed5621951eaf7d" +"checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08" "checksum iron 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2440ae846e7a8c7f9b401db8f6e31b4ea5e7d3688b91761337da7e054520c75b" "checksum iron 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d8e17268922834707e1c29e8badbf9c712c9c43378e1b6a3388946baff10be2" "checksum iron-slog 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eb0cc34306c77baf5089b3af209a0547c0ce36cdeaee2424a9fa0e45c042f4a0" "checksum iron-test 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b1944bcf30f8b3f51ebf01e715517dd9755e9480934778d6de70179a41d283c1" +"checksum itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8324a32baf01e2ae060e9de58ed0bc2320c9a2833491ee36cd3b4c414de4db8c" "checksum itoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c069bbec61e1ca5a596166e55dfe4773ff745c3d16b700013bcaff9a6df2c682" +"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" "checksum lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73" "checksum lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c8f31047daa365f19be14b47c29df4f7c3b581832407daabe6ae77397619237d" +"checksum lazycell 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a6f08839bc70ef4a3fe1d566d5350f519c5912ea86be0df1740a7d247c7fc0ef" "checksum libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)" = "6fd41f331ac7c5b8ac259b8bf82c75c0fb2e469bbf37d2becbba9a6a2221965b" "checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" "checksum log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "89f010e843f2b1a31dbd316b3b8d443758bc634bed37aabade59c686d644e0a2" "checksum matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "100aabe6b8ff4e4a7e32c1c13523379802df0772b82466207ac25b013f193376" +"checksum memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "148fab2e51b4f1cfc66da2a7c32981d1d3c083a803978268bb11fe4b86925e7a" "checksum memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "796fba70e76612589ed2ce7f45282f5af869e0fdd7cc6199fa1aa1f1d591ba9d" +"checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3" "checksum mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ba626b8a6de5da682e1caa06bdb42a335aee5a84db8e5046a3e8ab17ba0a3ae0" +"checksum mime 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "0b28683d0b09bbc20be1c9b3f6f24854efb1356ffcffee08ea3f6e65596e85fa" "checksum mime_guess 1.8.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b7e2b09d08313f84e0fb82d13a4d859109a17543fe9af3b6d941dc1431f7de79" +"checksum mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)" = "6d771e3ef92d58a8da8df7d6976bfca9371ed1de6619d9d5a5ce5b1f29b85bfe" +"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" "checksum modifier 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "41f5c9112cb662acd3b204077e0de5bc66305fa8df65c8019d5adb10e9ab6e58" +"checksum multipart 0.13.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92f54eb45230c3aa20864ccf0c277eeaeadcf5e437e91731db498dbf7fbe0ec6" +"checksum native-tls 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f74dbadc8b43df7864539cedb7bc91345e532fdd913cfdc23ad94f4d2d40fbc0" +"checksum net2 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)" = "9044faf1413a1057267be51b5afba8eb1090bd2231c693664aa1db716fe1eae0" +"checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2" "checksum num 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "4703ad64153382334aa8db57c637364c322d3372e097840c72000dabdcf6156e" "checksum num-integer 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)" = "6ac0ea58d64a89d9d6b7688031b3be9358d6c919badcf7fbb0527ccfd891ee45" "checksum num-iter 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "af3fdbbc3291a5464dc57b03860ec37ca6bf915ed6ee385e7c6c052c422b2124" "checksum num-traits 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "775393e285254d2f5004596d69bb8bc1149754570dcc08cf30cabeba67955e28" "checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30" +"checksum openssl 0.9.24 (registry+https://github.com/rust-lang/crates.io-index)" = "a3605c298474a3aa69de92d21139fb5e2a81688d308262359d85cdd0d12a7985" +"checksum openssl-sys 0.9.30 (registry+https://github.com/rust-lang/crates.io-index)" = "73ae718c3562989cd3a0a5c26610feca02f8116822f6f195e6cf4887481e57f5" "checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" "checksum phf 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)" = "7d37a244c75a9748e049225155f56dbcb98fe71b192fd25fd23cb914b5ad62f2" "checksum phf_codegen 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)" = "4e4048fe7dd7a06b8127ecd6d3803149126e9b33c7558879846da3a63f734f2b" "checksum phf_generator 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)" = "05a079dd052e7b674d21cb31cbb6c05efd56a2cd2827db7692e2f1a507ebd998" "checksum phf_shared 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)" = "c2261d544c2bb6aa3b10022b0be371b9c7c64f762ef28c6f5d4f1ef6d97b5930" +"checksum pkg-config 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)" = "110d5ee3593dbb73f56294327fe5668bcc997897097cbc76b51e7aed3f52452f" "checksum plugin 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "1a6a0dc3910bc8db877ffed8e457763b317cf880df4ae19109b9f77d277cf6e0" "checksum pq-sys 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "9323a6ce484fc41174d40f80ba87af6247f86a7ba57856af68d3aa0c8642d2f0" "checksum proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cd07deb3c6d1d9ff827999c7f9b04cdfd66b1b17ae508e14fe47b620f2282ae0" @@ -836,39 +1670,75 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" "checksum regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9329abc99e39129fcceabd24cf5d85b4671ef7c29c50e972bc5afe32438ec384" "checksum regex-syntax 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7d707a4fa2637f2dca2ef9fd02225ec7661fe01a53623c1e6515b6916511f7a7" +"checksum relay 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1576e382688d7e9deecea24417e350d3062d97e32e45d70b1cde65994ff1489a" +"checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5" +"checksum rustc-demangle 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "76d7ba1feafada44f2d38eed812bd2489a03c0f5abb975799251518b68848649" "checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" "checksum safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e27a8b19b835f7aea908818e871f5cc3a5a186550c30773be987e155e8163d8f" +"checksum schannel 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "85fd9df495640643ad2d00443b3d78aae69802ad488debab4f1dd52fc1806ade" +"checksum scoped-tls 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "332ffa32bf586782a3efaeb58f127980944bbc8c4d6913a86107ac2a5ab24b28" +"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" +"checksum security-framework 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "dfa44ee9c54ce5eecc9de7d5acbad112ee58755239381f687e564004ba4a2332" +"checksum security-framework-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "5421621e836278a0b139268f36eee0dc7e389b784dc3f79d8f11aabadf41bead" "checksum serde 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)" = "97f6a6c3caba0cf8f883b53331791036404ce3c1bd895961cf8bb2f8cecfd84b" "checksum serde_derive 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)" = "f51b0ef935cf8a41a77bce553da1f8751a739b7ad82dd73669475a22e6ecedb0" +"checksum serde_ignored 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "190e9765dcedb56be63b6e0993a006c7e3b071a016a304736e4a315dc01fb142" "checksum serde_json 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)" = "f3ad6d546e765177cf3dded3c2e424a8040f870083a0e64064746b958ece9cb1" +"checksum serde_urlencoded 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ce0fd303af908732989354c6f02e05e2e6d597152870f2c6990efb0577137480" "checksum siphasher 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0df90a788073e8d0235a67e50441d47db7c8ad9debd91cbf43736a2a92d36537" +"checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23" +"checksum slab 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fdeff4cd9ecff59ec7e3744cbca73dfe5ac35c2aedb2cfba8a1c715a18912e9d" "checksum slog 2.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2f7bfce6405155042d42ec0e645efe43eddedd7be280063ce0623b120014e7f9" +"checksum smallvec 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4c8cbcd6df1e117c2210e13ab5109635ad68a929fcbb8964dc965b76cb5ee013" "checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" -"checksum swagger 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "190ef0c6327759d0beb76d969b236fa3cc42469f9e107f626bbcc152727b4d12" +"checksum swagger 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "97dbc25f067d3341f3c8fc6d77471891878b1585a614018c7f542d6b5cf1dc61" "checksum syn 0.12.15 (registry+https://github.com/rust-lang/crates.io-index)" = "c97c05b8ebc34ddd6b967994d5c6e9852fa92f8b82b3858c39451f97346dcce5" "checksum syn 0.13.10 (registry+https://github.com/rust-lang/crates.io-index)" = "77961dcdac942fa8bc033c16f3a790b311c8a27d00811b878ebd8cf9b7ba39d5" +"checksum take 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b157868d8ac1f56b64604539990685fa7611d8fa9e5476cf0c02cf34d32917c5" +"checksum tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" "checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" "checksum textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0b59b6b4b44d867f1370ef1bd91bfb262bf07bf0ae65c202ea2fbc16153b693" "checksum thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "279ef31c19ededf577bfd12dfae728040a21f635b06a24cd670ff510edd38963" "checksum time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "d825be0eb33fda1a7e68012d51e9c7f451dc1a69391e7fdc197060bb8c56667b" +"checksum tiny_http 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)" = "2f4d55c9a213880d1f0c89ded183f209c6e45b912ca6c7df6f93c163773572e1" +"checksum tokio 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7d00555353b013e170ed8bc4e13f648a317d1fd12157dbcae13f7013f6cf29f5" +"checksum tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "aeeffbbb94209023feaef3c196a41cbcdafa06b4a6f893f68779bb5e53796f71" +"checksum tokio-executor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8cac2a7883ff3567e9d66bb09100d09b33d90311feca0206c7ca034bc0c55113" +"checksum tokio-fs 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "76766830bbf9a2d5bfb50c95350d56a2e79e2c80f675967fff448bc615899708" +"checksum tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "6af9eb326f64b2d6b68438e1953341e00ab3cf54de7e35d92bfc73af8555313a" +"checksum tokio-proto 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8fbb47ae81353c63c487030659494b295f6cb6576242f907f203473b191b0389" +"checksum tokio-reactor 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3cedc8e5af5131dc3423ffa4f877cce78ad25259a9a62de0613735a13ebc64b" +"checksum tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "24da22d077e0f15f55162bdbdc661228c1581892f52074fb242678d015b45162" +"checksum tokio-tcp 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ec9b094851aadd2caf83ba3ad8e8c4ce65a42104f7b94d9e6550023f0407853f" +"checksum tokio-threadpool 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "5783254b10c7c84a56f62c74766ef7e5b83d1f13053218c7cab8d3f2c826fa0e" +"checksum tokio-timer 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535fed0ccee189f3d48447587697ba3fd234b3dbbb091f0ec4613ddfec0a7c4c" +"checksum tokio-tls 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "772f4b04e560117fe3b0a53e490c16ddc8ba6ec437015d91fa385564996ed913" +"checksum tokio-udp 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "137bda266504893ac4774e0ec4c2108f7ccdbcb7ac8dced6305fe9e4e0b5041a" "checksum traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079" +"checksum twoway 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "59b11b2b5241ba34be09c3cc85a36e56e48f9888862e19cedf23336d35316ed1" "checksum typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1410f6f91f21d1612654e7cc69193b0334f909dcf2c790c4826254fbb86f8887" "checksum typemap 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "653be63c80a3296da5551e1bfd2cca35227e13cdd08c6668903ae2f4f77aa1f6" "checksum ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd2be2d6639d0f8fe6cdda291ad456e23629558d466e2789d2c3e9892bda285d" "checksum unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33" +"checksum unicase 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "284b6d3db520d67fbe88fd778c21510d1b0ba4a551e5d0fbb023d33405f6de8a" "checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" "checksum unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6a0180bc61fc5a987082bfa111f4cc95c4caff7f9799f3e46df09163a937aa25" "checksum unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3a113775714a22dcb774d8ea3655c53a32debae63a063acc00a91cc586245f" "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" "checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" "checksum unsafe-any 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f30360d7979f5e9c6e6cea48af192ea8fab4afb3cf72597154b8f08935bc9c7f" +"checksum url 0.2.38 (registry+https://github.com/rust-lang/crates.io-index)" = "cbaa8377a162d88e7d15db0cf110c8523453edcbc5bc66d2b6fffccffa34a068" "checksum url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f808aadd8cfec6ef90e4a14eb46f24511824d1ac596b9682703c87056c8678b7" "checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122" +"checksum uuid 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "78c590b5bd79ed10aad8fb75f078a59d8db445af6c743e55c4a53227fc01c13f" "checksum uuid 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bcc7e3b898aa6f6c08e5295b6c89258d1331e9ac578cc992fb818759951bdc22" "checksum vcpkg 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7ed0f6789c8a85ca41bbc1c9d175422116a9869bd1cf31bb08e1493ecce60380" "checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" "checksum version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6b772017e347561807c1aa192438c5fd74242a670a6cffacc40f2defd1dc069d" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" +"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" "checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3" +"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 94a9a091..f35490e5 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -3,20 +3,27 @@ name = "fatcat" version = "0.1.0" authors = ["Bryan Newbold <bnewbold@archive.org>"] +[workspace] +members = ["fatcat-api"] + [dependencies] +fatcat-api = {version = "*", path = "fatcat-api"} diesel = { version = "1.0.0", features = ["postgres"] } dotenv = "0.9.0" clap = "*" +error-chain = "0.11" # API server chrono = { version = "0.4", features = ["serde"] } futures = "0.1" -hyper = "0.10" -#hyper-openssl = "0.2" +hyper = "0.11" +#hyper-tls = {version = "0.1.2", optional = true} iron = "0.5" iron-slog = "*" iron-test = "*" -swagger = "0.7" +swagger = "0.10" +tokio-core = "0.1.6" +tokio-proto = "0.1.1" # TODO #sentry = "*" diff --git a/rust/fatcat-api/.gitignore b/rust/fatcat-api/.gitignore new file mode 100644 index 00000000..a9d37c56 --- /dev/null +++ b/rust/fatcat-api/.gitignore @@ -0,0 +1,2 @@ +target +Cargo.lock diff --git a/rust/fatcat-api/Cargo.toml b/rust/fatcat-api/Cargo.toml new file mode 100644 index 00000000..8eb4279d --- /dev/null +++ b/rust/fatcat-api/Cargo.toml @@ -0,0 +1,48 @@ +[package] +name = "fatcat-api" +version = "0.1.0" +authors = [] +description = "A scalable, versioned, API-oriented catalog of bibliographic entities and file metadata" +license = "Unlicense" + +[features] +default = ["client", "server"] +client = ["serde_json", "serde_ignored", "hyper", "hyper-tls", "native-tls", "openssl", "tokio-core", "url", "uuid"] +server = ["serde_json", "serde_ignored", "hyper", "hyper-tls", "native-tls", "openssl", "tokio-core", "tokio-proto", "tokio-tls", "regex", "percent-encoding", "url", "uuid"] + +[dependencies] +# Required by example server. +# +chrono = { version = "0.4", features = ["serde"] } +futures = "0.1" +hyper = {version = "0.11", optional = true} +hyper-tls = {version = "0.1.2", optional = true} +swagger = "0.10.0" + +# Not required by example server. +# +lazy_static = "0.2" +log = "0.3.0" +mime = "0.3.3" +multipart = {version = "0.13.3", optional = true} +native-tls = {version = "0.1.4", optional = true} +openssl = {version = "0.9.14", optional = true} +percent-encoding = {version = "1.0.0", optional = true} +regex = {version = "0.2", optional = true} +serde = "1.0" +serde_derive = "1.0" +serde_ignored = {version = "0.0.4", optional = true} +serde_json = {version = "1.0", optional = true} +serde_urlencoded = {version = "0.5.1", optional = true} +tokio-core = {version = "0.1.6", optional = true} +tokio-proto = {version = "0.1.1", optional = true} +tokio-tls = {version = "0.1.3", optional = true, features = ["tokio-proto"]} +url = {version = "1.5", optional = true} +uuid = {version = "0.5", optional = true, features = ["serde", "v4"]} +# ToDo: this should be updated to point at the official crate once +# https://github.com/RReverser/serde-xml-rs/pull/45 is accepted upstream + + +[dev-dependencies] +clap = "2.25" +error-chain = "0.11" diff --git a/rust/fatcat-api/README.md b/rust/fatcat-api/README.md new file mode 100644 index 00000000..5a24942b --- /dev/null +++ b/rust/fatcat-api/README.md @@ -0,0 +1,123 @@ +# Rust API for fatcat + +A scalable, versioned, API-oriented catalog of bibliographic entities and file metadata + +## Overview +This client/server was generated by the [swagger-codegen] +(https://github.com/swagger-api/swagger-codegen) 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://github.com/swagger-api/swagger-codegen/blob/master/README.md) + +- API version: 0.1.0 +- Build date: 2018-05-15T05:47:34.699Z + +This autogenerated project defines an API crate `fatcat` 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 `fatcat`: +* The example server starts up a web server using the `fatcat` 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 + `fatcat` 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 server +To run the server, follow these simple steps: + +``` +cargo run --example server +``` + +### Running a client +To run a client, follow one of the following simple steps: + +``` +cargo run --example client ContainerIdGet +cargo run --example client ContainerLookupGet +cargo run --example client ContainerPost +cargo run --example client CreatorIdGet +cargo run --example client CreatorLookupGet +cargo run --example client CreatorPost +cargo run --example client EditgroupIdAcceptPost +cargo run --example client EditgroupIdGet +cargo run --example client EditgroupPost +cargo run --example client EditorUsernameChangelogGet +cargo run --example client EditorUsernameGet +cargo run --example client FileIdGet +cargo run --example client FileLookupGet +cargo run --example client FilePost +cargo run --example client ReleaseIdGet +cargo run --example client ReleaseLookupGet +cargo run --example client ReleasePost +cargo run --example client WorkIdGet +cargo run --example client WorkPost +``` + +### 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`. + + +## Writing a server + +The server example is designed to form the basis for implementing your own server. Simply follow these steps. + +* Set up a new Rust project, e.g., with `cargo init --bin`. +* Insert `fatcat` into the `members` array under [workspace] in the root `Cargo.toml`, e.g., `members = [ "fatcat" ]`. +* Add `fatcat = {version = "0.1.0", path = "fatcat"}` under `[dependencies]` in the root `Cargo.toml`. +* Copy the `[dependencies]` and `[dev-dependencies]` from `fatcat/Cargo.toml` into the root `Cargo.toml`'s `[dependencies]` section. + * Copy all of the `[dev-dependencies]`, but only the `[dependencies]` that are required by the example server. These should be clearly indicated by comments. + * Remove `"optional = true"` from each of these lines if present. + +Each autogenerated API will contain an implementation stub and main entry point, which should be copied into your project the first time: +``` +cp fatcat/examples/server.rs src/main.rs +cp fatcat/examples/server_lib/mod.rs src/lib.rs +cp fatcat/examples/server_lib/server.rs src/server.rs +``` + +Now + +* From `src/main.rs`, remove the `mod server_lib;` line, and uncomment and fill in the `extern crate` line with the name of this server crate. +* Move the block of imports "required by the service library" from `src/main.rs` to `src/lib.rs` and uncomment. +* Change the `let server = server::Server {};` line to `let server = SERVICE_NAME::server().unwrap();` where `SERVICE_NAME` is the name of the server crate. +* Run `cargo build` to check it builds. +* Run `cargo fmt` to reformat the code. +* Commit the result before making any further changes (lest format changes get confused with your own updates). + +Now replace the implementations in `src/server.rs` with your own code as required. + +## Updating your server to track API changes + +Later, if the API changes, you can copy new sections from the autogenerated API stub into your implementation. +Alternatively, implement the now-missing methods based on the compiler's error messages. diff --git a/rust/fatcat-api/api.yaml b/rust/fatcat-api/api.yaml new file mode 100644 index 00000000..badb17ab --- /dev/null +++ b/rust/fatcat-api/api.yaml @@ -0,0 +1,464 @@ +--- +swagger: "2.0" +info: + title: fatcat + description: A scalable, versioned, API-oriented catalog of bibliographic entities + and file metadata + version: 0.1.0 +schemes: [http] +basePath: /v0 +host: api.fatcat.wiki +consumes: + - application/json +produces: + - application/json + + +# Common properties across entities +x-entity-props: &ENTITYPROPS + state: + type: string + enum: ["wip", "active", "redirect", "deleted"] + ident: + type: string + #format: uuid + revision: + type: integer + redirect: + type: string + #format: uuid + editgroup: + type: integer +x-entity-edit-props: &ENTITYEDITPROPS + id: + type: integer + ident: + type: string + revision: + type: integer + editgroup_id: + type: integer + + +definitions: + error: + type: object + required: + - message + properties: + message: + type: string + success: + type: object + required: + - message + properties: + message: + type: string + creator_entity: + type: object + properties: + <<: *ENTITYPROPS + name: + type: string + orcid: + type: string + #format: custom + container_entity: + type: object + properties: + <<: *ENTITYPROPS + name: + type: string + parent: + type: string + publisher: + type: string + issn: + type: string + #format: custom + file_entity: + type: object + properties: + <<: *ENTITYPROPS + size: + type: integer + sha1: + type: string + #format: custom + url: + type: string + format: url + release_entity: + type: object + properties: + <<: *ENTITYPROPS + work: + type: string + container: + type: string + license: + type: string + release_type: + type: string + date: + type: date + doi: + type: string + #format: custom + volume: + type: string + pages: + type: string + issue: + type: string + work_entity: + type: object + properties: + <<: *ENTITYPROPS + title: + type: string + work_type: + type: string + entity_edit: + type: object + properties: + <<: *ENTITYEDITPROPS + editor: + type: object + required: + - username + properties: + username: + type: string + editgroup: + type: object + required: + - id + - editor_id + properties: + id: + type: integer + editor_id: + type: integer + changelogentry: + type: object + required: + - index + properties: + index: + type: integer + editgroup_id: + type: integer + timestamp: + type: string + format: date-time + +x-entity-responses: &ENTITYRESPONSES + 400: + description: bad request + schema: + $ref: "#/definitions/error" + default: + description: generic error response + schema: + $ref: "#/definitions/error" + +paths: + /creator: + post: + parameters: + - name: body + in: body + schema: + $ref: "#/definitions/creator_entity" + responses: + 201: + description: created + schema: + $ref: "#/definitions/entity_edit" + <<: *ENTITYRESPONSES + /creator/{id}: + parameters: + - name: id + in: path + type: string + required: true + get: + responses: + 200: + description: fetch a single creator by id + schema: + $ref: "#/definitions/creator_entity" + <<: *ENTITYRESPONSES + /creator/lookup: + get: + parameters: + - name: orcid + in: query + type: string + required: true + responses: + 200: + description: find a single creator by external identifer + schema: + $ref: "#/definitions/creator_entity" + 404: + description: no such creator + schema: + $ref: "#/definitions/error" + <<: *ENTITYRESPONSES + /container: + post: + parameters: + - name: body + in: body + schema: + $ref: "#/definitions/container_entity" + responses: + 201: + description: created + schema: + $ref: "#/definitions/entity_edit" + <<: *ENTITYRESPONSES + /container/{id}: + parameters: + - name: id + in: path + type: string + required: true + get: + responses: + 200: + description: fetch a single container by id + schema: + $ref: "#/definitions/container_entity" + <<: *ENTITYRESPONSES + /container/lookup: + get: + parameters: + - name: issn + in: query + type: string + required: true + responses: + 200: + description: find a single container by external identifer + schema: + $ref: "#/definitions/container_entity" + 404: + description: no such container + schema: + $ref: "#/definitions/error" + <<: *ENTITYRESPONSES + /file: + post: + parameters: + - name: body + in: body + schema: + $ref: "#/definitions/file_entity" + responses: + 201: + description: created + schema: + $ref: "#/definitions/entity_edit" + <<: *ENTITYRESPONSES + /file/{id}: + parameters: + - name: id + in: path + type: string + required: true + get: + responses: + 200: + description: fetch a single file by id + schema: + $ref: "#/definitions/file_entity" + <<: *ENTITYRESPONSES + /file/lookup: + get: + parameters: + - name: sha1 + in: query + type: string + required: true + responses: + 200: + description: find a single file by external identifer + schema: + $ref: "#/definitions/file_entity" + 404: + description: no such file + schema: + $ref: "#/definitions/error" + <<: *ENTITYRESPONSES + /release: + post: + parameters: + - name: body + in: body + schema: + $ref: "#/definitions/release_entity" + responses: + 201: + description: created + schema: + $ref: "#/definitions/entity_edit" + <<: *ENTITYRESPONSES + /release/{id}: + parameters: + - name: id + in: path + type: string + required: true + get: + responses: + 200: + description: fetch a single release by id + schema: + $ref: "#/definitions/release_entity" + <<: *ENTITYRESPONSES + /release/lookup: + get: + parameters: + - name: doi + in: query + type: string + required: true + responses: + 200: + description: find a single release by external identifer + schema: + $ref: "#/definitions/release_entity" + 404: + description: no such release + schema: + $ref: "#/definitions/error" + <<: *ENTITYRESPONSES + /work: + post: + parameters: + - name: body + in: body + schema: + $ref: "#/definitions/work_entity" + responses: + 201: + description: created + schema: + $ref: "#/definitions/entity_edit" + <<: *ENTITYRESPONSES + /work/{id}: + parameters: + - name: id + in: path + type: string + required: true + get: + responses: + 200: + description: fetch a single work by id + schema: + $ref: "#/definitions/work_entity" + <<: *ENTITYRESPONSES + /editor/{username}: + parameters: + - name: username + in: path + type: string + required: true + get: + responses: + 200: + description: fetch generic information about an editor + schema: + $ref: "#/definitions/editor" + 404: + description: username not found + schema: + $ref: "#/definitions/error" + default: + description: generic error response + schema: + $ref: "#/definitions/error" + /editor/{username}/changelog: + parameters: + - name: username + in: path + type: string + required: true + get: + responses: + 200: + description: find changes (editgroups) by this editor which have been merged + schema: + $ref: "#/definitions/changelogentry" + 404: + description: username not found + schema: + $ref: "#/definitions/error" + default: + description: generic error response + schema: + $ref: "#/definitions/error" + /editgroup: + post: + responses: + 201: + description: successfully created + schema: + $ref: "#/definitions/editgroup" + 400: + description: invalid request parameters + schema: + $ref: "#/definitions/error" + default: + description: generic error response + schema: + $ref: "#/definitions/error" + /editgroup/{id}: + parameters: + - name: id + in: path + type: integer + required: true + get: + responses: + 200: + description: fetch editgroup by identifier + schema: + $ref: "#/definitions/editgroup" + 404: + description: no such editgroup + schema: + $ref: "#/definitions/error" + default: + description: generic error response + schema: + $ref: "#/definitions/error" + /editgroup/{id}/accept: + parameters: + - name: id + in: path + type: integer + required: true + post: + responses: + 200: + description: merged editgroup successfully ("live") + schema: + $ref: "#/definitions/success" + 400: + description: editgroup is in an unmergable state + schema: + $ref: "#/definitions/error" + 404: + description: no such editgroup + schema: + $ref: "#/definitions/error" + default: + description: generic error response + schema: + $ref: "#/definitions/error" diff --git a/rust/fatcat-api/api/swagger.yaml b/rust/fatcat-api/api/swagger.yaml new file mode 100644 index 00000000..66f77d5b --- /dev/null +++ b/rust/fatcat-api/api/swagger.yaml @@ -0,0 +1,1196 @@ +--- +swagger: "2.0" +info: + description: "A scalable, versioned, API-oriented catalog of bibliographic entities\ + \ and file metadata" + version: "0.1.0" + title: "fatcat" +host: "api.fatcat.wiki" +basePath: "/v0" +schemes: +- "http" +consumes: +- "application/json" +produces: +- "application/json" +paths: + /creator: + post: + parameters: + - in: "body" + name: "body" + required: false + schema: + $ref: "#/definitions/creator_entity" + uppercase_data_type: "CREATORENTITY" + refName: "creator_entity" + formatString: "{:?}" + example: "None" + model_key: "changelogentry" + uppercase_operation_id: "CREATOR_POST" + consumesJson: true + responses: + 201: + description: "created" + schema: + $ref: "#/definitions/entity_edit" + x-responseId: "Created" + x-uppercaseResponseId: "CREATED" + uppercase_operation_id: "CREATOR_POST" + uppercase_data_type: "ENTITYEDIT" + producesJson: true + 400: + description: "bad request" + schema: + $ref: "#/definitions/error" + x-responseId: "BadRequest" + x-uppercaseResponseId: "BAD_REQUEST" + uppercase_operation_id: "CREATOR_POST" + uppercase_data_type: "ERROR" + producesJson: true + default: + description: "generic error response" + schema: + $ref: "#/definitions/error" + x-responseId: "GenericErrorResponse" + x-uppercaseResponseId: "GENERIC_ERROR_RESPONSE" + uppercase_operation_id: "CREATOR_POST" + uppercase_data_type: "ERROR" + producesJson: true + operation_id: "creator_post" + uppercase_operation_id: "CREATOR_POST" + path: "/creator" + PATH_ID: "CREATOR" + hasPathParams: false + HttpMethod: "Post" + /creator/{id}: + get: + parameters: + - name: "id" + in: "path" + required: true + type: "string" + formatString: "\\\"{}\\\"" + example: "\"id_example\".to_string()" + responses: + 200: + description: "fetch a single creator by id" + schema: + $ref: "#/definitions/creator_entity" + x-responseId: "FetchASingleCreatorById" + x-uppercaseResponseId: "FETCH_A_SINGLE_CREATOR_BY_ID" + uppercase_operation_id: "CREATOR_ID_GET" + uppercase_data_type: "CREATORENTITY" + producesJson: true + 400: + description: "bad request" + schema: + $ref: "#/definitions/error" + x-responseId: "BadRequest" + x-uppercaseResponseId: "BAD_REQUEST" + uppercase_operation_id: "CREATOR_ID_GET" + uppercase_data_type: "ERROR" + producesJson: true + default: + description: "generic error response" + schema: + $ref: "#/definitions/error" + x-responseId: "GenericErrorResponse" + x-uppercaseResponseId: "GENERIC_ERROR_RESPONSE" + uppercase_operation_id: "CREATOR_ID_GET" + uppercase_data_type: "ERROR" + producesJson: true + operation_id: "creator_id_get" + uppercase_operation_id: "CREATOR_ID_GET" + path: "/creator/:id" + PATH_ID: "CREATOR_ID" + hasPathParams: true + HttpMethod: "Get" + /creator/lookup: + get: + parameters: + - name: "orcid" + in: "query" + required: true + type: "string" + formatString: "\\\"{}\\\"" + example: "\"orcid_example\".to_string()" + responses: + 200: + description: "find a single creator by external identifer" + schema: + $ref: "#/definitions/creator_entity" + x-responseId: "FindASingleCreatorByExternalIdentifer" + x-uppercaseResponseId: "FIND_A_SINGLE_CREATOR_BY_EXTERNAL_IDENTIFER" + uppercase_operation_id: "CREATOR_LOOKUP_GET" + uppercase_data_type: "CREATORENTITY" + producesJson: true + 400: + description: "bad request" + schema: + $ref: "#/definitions/error" + x-responseId: "BadRequest" + x-uppercaseResponseId: "BAD_REQUEST" + uppercase_operation_id: "CREATOR_LOOKUP_GET" + uppercase_data_type: "ERROR" + producesJson: true + 404: + description: "no such creator" + schema: + $ref: "#/definitions/error" + x-responseId: "NoSuchCreator" + x-uppercaseResponseId: "NO_SUCH_CREATOR" + uppercase_operation_id: "CREATOR_LOOKUP_GET" + uppercase_data_type: "ERROR" + producesJson: true + default: + description: "generic error response" + schema: + $ref: "#/definitions/error" + x-responseId: "GenericErrorResponse" + x-uppercaseResponseId: "GENERIC_ERROR_RESPONSE" + uppercase_operation_id: "CREATOR_LOOKUP_GET" + uppercase_data_type: "ERROR" + producesJson: true + operation_id: "creator_lookup_get" + uppercase_operation_id: "CREATOR_LOOKUP_GET" + path: "/creator/lookup" + PATH_ID: "CREATOR_LOOKUP" + hasPathParams: false + HttpMethod: "Get" + /container: + post: + parameters: + - in: "body" + name: "body" + required: false + schema: + $ref: "#/definitions/container_entity" + uppercase_data_type: "CONTAINERENTITY" + refName: "container_entity" + formatString: "{:?}" + example: "None" + model_key: "changelogentry" + uppercase_operation_id: "CONTAINER_POST" + consumesJson: true + responses: + 201: + description: "created" + schema: + $ref: "#/definitions/entity_edit" + x-responseId: "Created" + x-uppercaseResponseId: "CREATED" + uppercase_operation_id: "CONTAINER_POST" + uppercase_data_type: "ENTITYEDIT" + producesJson: true + 400: + description: "bad request" + schema: + $ref: "#/definitions/error" + x-responseId: "BadRequest" + x-uppercaseResponseId: "BAD_REQUEST" + uppercase_operation_id: "CONTAINER_POST" + uppercase_data_type: "ERROR" + producesJson: true + default: + description: "generic error response" + schema: + $ref: "#/definitions/error" + x-responseId: "GenericErrorResponse" + x-uppercaseResponseId: "GENERIC_ERROR_RESPONSE" + uppercase_operation_id: "CONTAINER_POST" + uppercase_data_type: "ERROR" + producesJson: true + operation_id: "container_post" + uppercase_operation_id: "CONTAINER_POST" + path: "/container" + PATH_ID: "CONTAINER" + hasPathParams: false + HttpMethod: "Post" + /container/{id}: + get: + parameters: + - name: "id" + in: "path" + required: true + type: "string" + formatString: "\\\"{}\\\"" + example: "\"id_example\".to_string()" + responses: + 200: + description: "fetch a single container by id" + schema: + $ref: "#/definitions/container_entity" + x-responseId: "FetchASingleContainerById" + x-uppercaseResponseId: "FETCH_A_SINGLE_CONTAINER_BY_ID" + uppercase_operation_id: "CONTAINER_ID_GET" + uppercase_data_type: "CONTAINERENTITY" + producesJson: true + 400: + description: "bad request" + schema: + $ref: "#/definitions/error" + x-responseId: "BadRequest" + x-uppercaseResponseId: "BAD_REQUEST" + uppercase_operation_id: "CONTAINER_ID_GET" + uppercase_data_type: "ERROR" + producesJson: true + default: + description: "generic error response" + schema: + $ref: "#/definitions/error" + x-responseId: "GenericErrorResponse" + x-uppercaseResponseId: "GENERIC_ERROR_RESPONSE" + uppercase_operation_id: "CONTAINER_ID_GET" + uppercase_data_type: "ERROR" + producesJson: true + operation_id: "container_id_get" + uppercase_operation_id: "CONTAINER_ID_GET" + path: "/container/:id" + PATH_ID: "CONTAINER_ID" + hasPathParams: true + HttpMethod: "Get" + /container/lookup: + get: + parameters: + - name: "issn" + in: "query" + required: true + type: "string" + formatString: "\\\"{}\\\"" + example: "\"issn_example\".to_string()" + responses: + 200: + description: "find a single container by external identifer" + schema: + $ref: "#/definitions/container_entity" + x-responseId: "FindASingleContainerByExternalIdentifer" + x-uppercaseResponseId: "FIND_A_SINGLE_CONTAINER_BY_EXTERNAL_IDENTIFER" + uppercase_operation_id: "CONTAINER_LOOKUP_GET" + uppercase_data_type: "CONTAINERENTITY" + producesJson: true + 400: + description: "bad request" + schema: + $ref: "#/definitions/error" + x-responseId: "BadRequest" + x-uppercaseResponseId: "BAD_REQUEST" + uppercase_operation_id: "CONTAINER_LOOKUP_GET" + uppercase_data_type: "ERROR" + producesJson: true + 404: + description: "no such container" + schema: + $ref: "#/definitions/error" + x-responseId: "NoSuchContainer" + x-uppercaseResponseId: "NO_SUCH_CONTAINER" + uppercase_operation_id: "CONTAINER_LOOKUP_GET" + uppercase_data_type: "ERROR" + producesJson: true + default: + description: "generic error response" + schema: + $ref: "#/definitions/error" + x-responseId: "GenericErrorResponse" + x-uppercaseResponseId: "GENERIC_ERROR_RESPONSE" + uppercase_operation_id: "CONTAINER_LOOKUP_GET" + uppercase_data_type: "ERROR" + producesJson: true + operation_id: "container_lookup_get" + uppercase_operation_id: "CONTAINER_LOOKUP_GET" + path: "/container/lookup" + PATH_ID: "CONTAINER_LOOKUP" + hasPathParams: false + HttpMethod: "Get" + /file: + post: + parameters: + - in: "body" + name: "body" + required: false + schema: + $ref: "#/definitions/file_entity" + uppercase_data_type: "FILEENTITY" + refName: "file_entity" + formatString: "{:?}" + example: "None" + model_key: "changelogentry" + uppercase_operation_id: "FILE_POST" + consumesJson: true + responses: + 201: + description: "created" + schema: + $ref: "#/definitions/entity_edit" + x-responseId: "Created" + x-uppercaseResponseId: "CREATED" + uppercase_operation_id: "FILE_POST" + uppercase_data_type: "ENTITYEDIT" + producesJson: true + 400: + description: "bad request" + schema: + $ref: "#/definitions/error" + x-responseId: "BadRequest" + x-uppercaseResponseId: "BAD_REQUEST" + uppercase_operation_id: "FILE_POST" + uppercase_data_type: "ERROR" + producesJson: true + default: + description: "generic error response" + schema: + $ref: "#/definitions/error" + x-responseId: "GenericErrorResponse" + x-uppercaseResponseId: "GENERIC_ERROR_RESPONSE" + uppercase_operation_id: "FILE_POST" + uppercase_data_type: "ERROR" + producesJson: true + operation_id: "file_post" + uppercase_operation_id: "FILE_POST" + path: "/file" + PATH_ID: "FILE" + hasPathParams: false + HttpMethod: "Post" + /file/{id}: + get: + parameters: + - name: "id" + in: "path" + required: true + type: "string" + formatString: "\\\"{}\\\"" + example: "\"id_example\".to_string()" + responses: + 200: + description: "fetch a single file by id" + schema: + $ref: "#/definitions/file_entity" + x-responseId: "FetchASingleFileById" + x-uppercaseResponseId: "FETCH_A_SINGLE_FILE_BY_ID" + uppercase_operation_id: "FILE_ID_GET" + uppercase_data_type: "FILEENTITY" + producesJson: true + 400: + description: "bad request" + schema: + $ref: "#/definitions/error" + x-responseId: "BadRequest" + x-uppercaseResponseId: "BAD_REQUEST" + uppercase_operation_id: "FILE_ID_GET" + uppercase_data_type: "ERROR" + producesJson: true + default: + description: "generic error response" + schema: + $ref: "#/definitions/error" + x-responseId: "GenericErrorResponse" + x-uppercaseResponseId: "GENERIC_ERROR_RESPONSE" + uppercase_operation_id: "FILE_ID_GET" + uppercase_data_type: "ERROR" + producesJson: true + operation_id: "file_id_get" + uppercase_operation_id: "FILE_ID_GET" + path: "/file/:id" + PATH_ID: "FILE_ID" + hasPathParams: true + HttpMethod: "Get" + /file/lookup: + get: + parameters: + - name: "sha1" + in: "query" + required: true + type: "string" + formatString: "\\\"{}\\\"" + example: "\"sha1_example\".to_string()" + responses: + 200: + description: "find a single file by external identifer" + schema: + $ref: "#/definitions/file_entity" + x-responseId: "FindASingleFileByExternalIdentifer" + x-uppercaseResponseId: "FIND_A_SINGLE_FILE_BY_EXTERNAL_IDENTIFER" + uppercase_operation_id: "FILE_LOOKUP_GET" + uppercase_data_type: "FILEENTITY" + producesJson: true + 400: + description: "bad request" + schema: + $ref: "#/definitions/error" + x-responseId: "BadRequest" + x-uppercaseResponseId: "BAD_REQUEST" + uppercase_operation_id: "FILE_LOOKUP_GET" + uppercase_data_type: "ERROR" + producesJson: true + 404: + description: "no such file" + schema: + $ref: "#/definitions/error" + x-responseId: "NoSuchFile" + x-uppercaseResponseId: "NO_SUCH_FILE" + uppercase_operation_id: "FILE_LOOKUP_GET" + uppercase_data_type: "ERROR" + producesJson: true + default: + description: "generic error response" + schema: + $ref: "#/definitions/error" + x-responseId: "GenericErrorResponse" + x-uppercaseResponseId: "GENERIC_ERROR_RESPONSE" + uppercase_operation_id: "FILE_LOOKUP_GET" + uppercase_data_type: "ERROR" + producesJson: true + operation_id: "file_lookup_get" + uppercase_operation_id: "FILE_LOOKUP_GET" + path: "/file/lookup" + PATH_ID: "FILE_LOOKUP" + hasPathParams: false + HttpMethod: "Get" + /release: + post: + parameters: + - in: "body" + name: "body" + required: false + schema: + $ref: "#/definitions/release_entity" + uppercase_data_type: "RELEASEENTITY" + refName: "release_entity" + formatString: "{:?}" + example: "None" + model_key: "changelogentry" + uppercase_operation_id: "RELEASE_POST" + consumesJson: true + responses: + 201: + description: "created" + schema: + $ref: "#/definitions/entity_edit" + x-responseId: "Created" + x-uppercaseResponseId: "CREATED" + uppercase_operation_id: "RELEASE_POST" + uppercase_data_type: "ENTITYEDIT" + producesJson: true + 400: + description: "bad request" + schema: + $ref: "#/definitions/error" + x-responseId: "BadRequest" + x-uppercaseResponseId: "BAD_REQUEST" + uppercase_operation_id: "RELEASE_POST" + uppercase_data_type: "ERROR" + producesJson: true + default: + description: "generic error response" + schema: + $ref: "#/definitions/error" + x-responseId: "GenericErrorResponse" + x-uppercaseResponseId: "GENERIC_ERROR_RESPONSE" + uppercase_operation_id: "RELEASE_POST" + uppercase_data_type: "ERROR" + producesJson: true + operation_id: "release_post" + uppercase_operation_id: "RELEASE_POST" + path: "/release" + PATH_ID: "RELEASE" + hasPathParams: false + HttpMethod: "Post" + /release/{id}: + get: + parameters: + - name: "id" + in: "path" + required: true + type: "string" + formatString: "\\\"{}\\\"" + example: "\"id_example\".to_string()" + responses: + 200: + description: "fetch a single release by id" + schema: + $ref: "#/definitions/release_entity" + x-responseId: "FetchASingleReleaseById" + x-uppercaseResponseId: "FETCH_A_SINGLE_RELEASE_BY_ID" + uppercase_operation_id: "RELEASE_ID_GET" + uppercase_data_type: "RELEASEENTITY" + producesJson: true + 400: + description: "bad request" + schema: + $ref: "#/definitions/error" + x-responseId: "BadRequest" + x-uppercaseResponseId: "BAD_REQUEST" + uppercase_operation_id: "RELEASE_ID_GET" + uppercase_data_type: "ERROR" + producesJson: true + default: + description: "generic error response" + schema: + $ref: "#/definitions/error" + x-responseId: "GenericErrorResponse" + x-uppercaseResponseId: "GENERIC_ERROR_RESPONSE" + uppercase_operation_id: "RELEASE_ID_GET" + uppercase_data_type: "ERROR" + producesJson: true + operation_id: "release_id_get" + uppercase_operation_id: "RELEASE_ID_GET" + path: "/release/:id" + PATH_ID: "RELEASE_ID" + hasPathParams: true + HttpMethod: "Get" + /release/lookup: + get: + parameters: + - name: "doi" + in: "query" + required: true + type: "string" + formatString: "\\\"{}\\\"" + example: "\"doi_example\".to_string()" + responses: + 200: + description: "find a single release by external identifer" + schema: + $ref: "#/definitions/release_entity" + x-responseId: "FindASingleReleaseByExternalIdentifer" + x-uppercaseResponseId: "FIND_A_SINGLE_RELEASE_BY_EXTERNAL_IDENTIFER" + uppercase_operation_id: "RELEASE_LOOKUP_GET" + uppercase_data_type: "RELEASEENTITY" + producesJson: true + 400: + description: "bad request" + schema: + $ref: "#/definitions/error" + x-responseId: "BadRequest" + x-uppercaseResponseId: "BAD_REQUEST" + uppercase_operation_id: "RELEASE_LOOKUP_GET" + uppercase_data_type: "ERROR" + producesJson: true + 404: + description: "no such release" + schema: + $ref: "#/definitions/error" + x-responseId: "NoSuchRelease" + x-uppercaseResponseId: "NO_SUCH_RELEASE" + uppercase_operation_id: "RELEASE_LOOKUP_GET" + uppercase_data_type: "ERROR" + producesJson: true + default: + description: "generic error response" + schema: + $ref: "#/definitions/error" + x-responseId: "GenericErrorResponse" + x-uppercaseResponseId: "GENERIC_ERROR_RESPONSE" + uppercase_operation_id: "RELEASE_LOOKUP_GET" + uppercase_data_type: "ERROR" + producesJson: true + operation_id: "release_lookup_get" + uppercase_operation_id: "RELEASE_LOOKUP_GET" + path: "/release/lookup" + PATH_ID: "RELEASE_LOOKUP" + hasPathParams: false + HttpMethod: "Get" + /work: + post: + parameters: + - in: "body" + name: "body" + required: false + schema: + $ref: "#/definitions/work_entity" + uppercase_data_type: "WORKENTITY" + refName: "work_entity" + formatString: "{:?}" + example: "None" + model_key: "changelogentry" + uppercase_operation_id: "WORK_POST" + consumesJson: true + responses: + 201: + description: "created" + schema: + $ref: "#/definitions/entity_edit" + x-responseId: "Created" + x-uppercaseResponseId: "CREATED" + uppercase_operation_id: "WORK_POST" + uppercase_data_type: "ENTITYEDIT" + producesJson: true + 400: + description: "bad request" + schema: + $ref: "#/definitions/error" + x-responseId: "BadRequest" + x-uppercaseResponseId: "BAD_REQUEST" + uppercase_operation_id: "WORK_POST" + uppercase_data_type: "ERROR" + producesJson: true + default: + description: "generic error response" + schema: + $ref: "#/definitions/error" + x-responseId: "GenericErrorResponse" + x-uppercaseResponseId: "GENERIC_ERROR_RESPONSE" + uppercase_operation_id: "WORK_POST" + uppercase_data_type: "ERROR" + producesJson: true + operation_id: "work_post" + uppercase_operation_id: "WORK_POST" + path: "/work" + PATH_ID: "WORK" + hasPathParams: false + HttpMethod: "Post" + /work/{id}: + get: + parameters: + - name: "id" + in: "path" + required: true + type: "string" + formatString: "\\\"{}\\\"" + example: "\"id_example\".to_string()" + responses: + 200: + description: "fetch a single work by id" + schema: + $ref: "#/definitions/work_entity" + x-responseId: "FetchASingleWorkById" + x-uppercaseResponseId: "FETCH_A_SINGLE_WORK_BY_ID" + uppercase_operation_id: "WORK_ID_GET" + uppercase_data_type: "WORKENTITY" + producesJson: true + 400: + description: "bad request" + schema: + $ref: "#/definitions/error" + x-responseId: "BadRequest" + x-uppercaseResponseId: "BAD_REQUEST" + uppercase_operation_id: "WORK_ID_GET" + uppercase_data_type: "ERROR" + producesJson: true + default: + description: "generic error response" + schema: + $ref: "#/definitions/error" + x-responseId: "GenericErrorResponse" + x-uppercaseResponseId: "GENERIC_ERROR_RESPONSE" + uppercase_operation_id: "WORK_ID_GET" + uppercase_data_type: "ERROR" + producesJson: true + operation_id: "work_id_get" + uppercase_operation_id: "WORK_ID_GET" + path: "/work/:id" + PATH_ID: "WORK_ID" + hasPathParams: true + HttpMethod: "Get" + /editor/{username}: + get: + parameters: + - name: "username" + in: "path" + required: true + type: "string" + formatString: "\\\"{}\\\"" + example: "\"username_example\".to_string()" + responses: + 200: + description: "fetch generic information about an editor" + schema: + $ref: "#/definitions/editor" + x-responseId: "FetchGenericInformationAboutAnEditor" + x-uppercaseResponseId: "FETCH_GENERIC_INFORMATION_ABOUT_AN_EDITOR" + uppercase_operation_id: "EDITOR_USERNAME_GET" + uppercase_data_type: "EDITOR" + producesJson: true + 404: + description: "username not found" + schema: + $ref: "#/definitions/error" + x-responseId: "UsernameNotFound" + x-uppercaseResponseId: "USERNAME_NOT_FOUND" + uppercase_operation_id: "EDITOR_USERNAME_GET" + uppercase_data_type: "ERROR" + producesJson: true + default: + description: "generic error response" + schema: + $ref: "#/definitions/error" + x-responseId: "GenericErrorResponse" + x-uppercaseResponseId: "GENERIC_ERROR_RESPONSE" + uppercase_operation_id: "EDITOR_USERNAME_GET" + uppercase_data_type: "ERROR" + producesJson: true + operation_id: "editor_username_get" + uppercase_operation_id: "EDITOR_USERNAME_GET" + path: "/editor/:username" + PATH_ID: "EDITOR_USERNAME" + hasPathParams: true + HttpMethod: "Get" + /editor/{username}/changelog: + get: + parameters: + - name: "username" + in: "path" + required: true + type: "string" + formatString: "\\\"{}\\\"" + example: "\"username_example\".to_string()" + responses: + 200: + description: "find changes (editgroups) by this editor which have been merged" + schema: + $ref: "#/definitions/changelogentry" + x-responseId: "FindChanges_" + x-uppercaseResponseId: "FIND_CHANGES_" + uppercase_operation_id: "EDITOR_USERNAME_CHANGELOG_GET" + uppercase_data_type: "CHANGELOGENTRY" + producesJson: true + 404: + description: "username not found" + schema: + $ref: "#/definitions/error" + x-responseId: "UsernameNotFound" + x-uppercaseResponseId: "USERNAME_NOT_FOUND" + uppercase_operation_id: "EDITOR_USERNAME_CHANGELOG_GET" + uppercase_data_type: "ERROR" + producesJson: true + default: + description: "generic error response" + schema: + $ref: "#/definitions/error" + x-responseId: "GenericErrorResponse" + x-uppercaseResponseId: "GENERIC_ERROR_RESPONSE" + uppercase_operation_id: "EDITOR_USERNAME_CHANGELOG_GET" + uppercase_data_type: "ERROR" + producesJson: true + operation_id: "editor_username_changelog_get" + uppercase_operation_id: "EDITOR_USERNAME_CHANGELOG_GET" + path: "/editor/:username/changelog" + PATH_ID: "EDITOR_USERNAME_CHANGELOG" + hasPathParams: true + HttpMethod: "Get" + /editgroup: + post: + parameters: [] + responses: + 201: + description: "successfully created" + schema: + $ref: "#/definitions/editgroup" + x-responseId: "SuccessfullyCreated" + x-uppercaseResponseId: "SUCCESSFULLY_CREATED" + uppercase_operation_id: "EDITGROUP_POST" + uppercase_data_type: "EDITGROUP" + producesJson: true + 400: + description: "invalid request parameters" + schema: + $ref: "#/definitions/error" + x-responseId: "InvalidRequestParameters" + x-uppercaseResponseId: "INVALID_REQUEST_PARAMETERS" + uppercase_operation_id: "EDITGROUP_POST" + uppercase_data_type: "ERROR" + producesJson: true + default: + description: "generic error response" + schema: + $ref: "#/definitions/error" + x-responseId: "GenericErrorResponse" + x-uppercaseResponseId: "GENERIC_ERROR_RESPONSE" + uppercase_operation_id: "EDITGROUP_POST" + uppercase_data_type: "ERROR" + producesJson: true + operation_id: "editgroup_post" + uppercase_operation_id: "EDITGROUP_POST" + path: "/editgroup" + PATH_ID: "EDITGROUP" + hasPathParams: false + HttpMethod: "Post" + /editgroup/{id}: + get: + parameters: + - name: "id" + in: "path" + required: true + type: "integer" + formatString: "{}" + example: "56" + responses: + 200: + description: "fetch editgroup by identifier" + schema: + $ref: "#/definitions/editgroup" + x-responseId: "FetchEditgroupByIdentifier" + x-uppercaseResponseId: "FETCH_EDITGROUP_BY_IDENTIFIER" + uppercase_operation_id: "EDITGROUP_ID_GET" + uppercase_data_type: "EDITGROUP" + producesJson: true + 404: + description: "no such editgroup" + schema: + $ref: "#/definitions/error" + x-responseId: "NoSuchEditgroup" + x-uppercaseResponseId: "NO_SUCH_EDITGROUP" + uppercase_operation_id: "EDITGROUP_ID_GET" + uppercase_data_type: "ERROR" + producesJson: true + default: + description: "generic error response" + schema: + $ref: "#/definitions/error" + x-responseId: "GenericErrorResponse" + x-uppercaseResponseId: "GENERIC_ERROR_RESPONSE" + uppercase_operation_id: "EDITGROUP_ID_GET" + uppercase_data_type: "ERROR" + producesJson: true + operation_id: "editgroup_id_get" + uppercase_operation_id: "EDITGROUP_ID_GET" + path: "/editgroup/:id" + PATH_ID: "EDITGROUP_ID" + hasPathParams: true + HttpMethod: "Get" + /editgroup/{id}/accept: + post: + parameters: + - name: "id" + in: "path" + required: true + type: "integer" + formatString: "{}" + example: "56" + responses: + 200: + description: "merged editgroup successfully (\"live\")" + schema: + $ref: "#/definitions/success" + x-responseId: "MergedEditgroupSuccessfully_" + x-uppercaseResponseId: "MERGED_EDITGROUP_SUCCESSFULLY_" + uppercase_operation_id: "EDITGROUP_ID_ACCEPT_POST" + uppercase_data_type: "SUCCESS" + producesJson: true + 400: + description: "editgroup is in an unmergable state" + schema: + $ref: "#/definitions/error" + x-responseId: "EditgroupIsInAnUnmergableState" + x-uppercaseResponseId: "EDITGROUP_IS_IN_AN_UNMERGABLE_STATE" + uppercase_operation_id: "EDITGROUP_ID_ACCEPT_POST" + uppercase_data_type: "ERROR" + producesJson: true + 404: + description: "no such editgroup" + schema: + $ref: "#/definitions/error" + x-responseId: "NoSuchEditgroup" + x-uppercaseResponseId: "NO_SUCH_EDITGROUP" + uppercase_operation_id: "EDITGROUP_ID_ACCEPT_POST" + uppercase_data_type: "ERROR" + producesJson: true + default: + description: "generic error response" + schema: + $ref: "#/definitions/error" + x-responseId: "GenericErrorResponse" + x-uppercaseResponseId: "GENERIC_ERROR_RESPONSE" + uppercase_operation_id: "EDITGROUP_ID_ACCEPT_POST" + uppercase_data_type: "ERROR" + producesJson: true + operation_id: "editgroup_id_accept_post" + uppercase_operation_id: "EDITGROUP_ID_ACCEPT_POST" + path: "/editgroup/:id/accept" + PATH_ID: "EDITGROUP_ID_ACCEPT" + hasPathParams: true + HttpMethod: "Post" +definitions: + error: + type: "object" + required: + - "message" + properties: + message: + type: "string" + upperCaseName: "ERROR" + success: + type: "object" + required: + - "message" + properties: + message: + type: "string" + example: + message: "message" + upperCaseName: "SUCCESS" + creator_entity: + type: "object" + properties: + orcid: + type: "string" + name: + type: "string" + editgroup: + type: "integer" + redirect: + type: "string" + revision: + type: "integer" + ident: + type: "string" + state: + type: "string" + enum: + - "wip" + - "active" + - "redirect" + - "deleted" + example: + redirect: "redirect" + editgroup: 0 + ident: "ident" + name: "name" + orcid: "orcid" + state: "wip" + revision: 6 + upperCaseName: "CREATOR_ENTITY" + container_entity: + type: "object" + properties: + issn: + type: "string" + publisher: + type: "string" + parent: + type: "string" + name: + type: "string" + state: + type: "string" + enum: + - "wip" + - "active" + - "redirect" + - "deleted" + ident: + type: "string" + revision: + type: "integer" + redirect: + type: "string" + editgroup: + type: "integer" + example: + redirect: "redirect" + parent: "parent" + editgroup: 6 + issn: "issn" + ident: "ident" + name: "name" + publisher: "publisher" + state: "wip" + revision: 0 + upperCaseName: "CONTAINER_ENTITY" + file_entity: + type: "object" + properties: + url: + type: "string" + format: "url" + sha1: + type: "string" + size: + type: "integer" + editgroup: + type: "integer" + redirect: + type: "string" + revision: + type: "integer" + ident: + type: "string" + state: + type: "string" + enum: + - "wip" + - "active" + - "redirect" + - "deleted" + example: + sha1: "sha1" + redirect: "redirect" + editgroup: 6 + size: 0 + ident: "ident" + state: "wip" + url: "http://example.com/aeiou" + revision: 1 + upperCaseName: "FILE_ENTITY" + release_entity: + type: "object" + properties: + issue: + type: "string" + pages: + type: "string" + volume: + type: "string" + doi: + type: "string" + release_type: + type: "string" + license: + type: "string" + container: + type: "string" + work: + type: "string" + state: + type: "string" + enum: + - "wip" + - "active" + - "redirect" + - "deleted" + ident: + type: "string" + revision: + type: "integer" + redirect: + type: "string" + editgroup: + type: "integer" + example: + container: "container" + redirect: "redirect" + editgroup: 6 + issue: "issue" + work: "work" + ident: "ident" + release_type: "release_type" + revision: 0 + volume: "volume" + license: "license" + pages: "pages" + state: "wip" + doi: "doi" + upperCaseName: "RELEASE_ENTITY" + work_entity: + type: "object" + properties: + work_type: + type: "string" + title: + type: "string" + editgroup: + type: "integer" + redirect: + type: "string" + revision: + type: "integer" + ident: + type: "string" + state: + type: "string" + enum: + - "wip" + - "active" + - "redirect" + - "deleted" + example: + redirect: "redirect" + editgroup: 0 + ident: "ident" + work_type: "work_type" + state: "wip" + title: "title" + revision: 6 + upperCaseName: "WORK_ENTITY" + entity_edit: + type: "object" + properties: + editgroup_id: + type: "integer" + revision: + type: "integer" + ident: + type: "string" + id: + type: "integer" + example: + ident: "ident" + editgroup_id: 0 + id: 1 + revision: 6 + upperCaseName: "ENTITY_EDIT" + editor: + type: "object" + required: + - "username" + properties: + username: + type: "string" + example: + username: "username" + upperCaseName: "EDITOR" + editgroup: + type: "object" + required: + - "editor_id" + - "id" + properties: + id: + type: "integer" + editor_id: + type: "integer" + example: + editor_id: 6 + id: 0 + upperCaseName: "EDITGROUP" + changelogentry: + type: "object" + required: + - "index" + properties: + index: + type: "integer" + editgroup_id: + type: "integer" + timestamp: + type: "string" + format: "date-time" + example: + index: 0 + editgroup_id: 6 + timestamp: "2000-01-23T04:56:07.000+00:00" + upperCaseName: "CHANGELOGENTRY" +x-entity-props: + state: + type: "string" + enum: + - "wip" + - "active" + - "redirect" + - "deleted" + ident: + type: "string" + revision: + type: "integer" + redirect: + type: "string" + editgroup: + type: "integer" +x-entity-edit-props: + id: + type: "integer" + ident: + type: "string" + revision: + type: "integer" + editgroup_id: + type: "integer" +x-entity-responses: + 400: + description: "bad request" + schema: + $ref: "#/definitions/error" + default: + description: "generic error response" + schema: + $ref: "#/definitions/error" diff --git a/rust/fatcat-api/examples/ca.pem b/rust/fatcat-api/examples/ca.pem new file mode 100644 index 00000000..d2317fb5 --- /dev/null +++ b/rust/fatcat-api/examples/ca.pem @@ -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----- diff --git a/rust/fatcat-api/examples/client.rs b/rust/fatcat-api/examples/client.rs new file mode 100644 index 00000000..6f7aa151 --- /dev/null +++ b/rust/fatcat-api/examples/client.rs @@ -0,0 +1,350 @@ +#![allow(missing_docs, unused_variables, trivial_casts)] + +extern crate clap; +extern crate fatcat; +#[allow(unused_extern_crates)] +extern crate futures; +#[allow(unused_extern_crates)] +extern crate swagger; +extern crate tokio_core; +#[allow(unused_extern_crates)] +extern crate uuid; + +use clap::{App, Arg}; +#[allow(unused_imports)] +use fatcat::{ApiError, ApiNoContext, ContainerIdGetResponse, ContainerLookupGetResponse, + ContainerPostResponse, ContextWrapperExt, CreatorIdGetResponse, + CreatorLookupGetResponse, CreatorPostResponse, EditgroupIdAcceptPostResponse, + EditgroupIdGetResponse, EditgroupPostResponse, EditorUsernameChangelogGetResponse, + EditorUsernameGetResponse, FileIdGetResponse, FileLookupGetResponse, + FilePostResponse, ReleaseIdGetResponse, ReleaseLookupGetResponse, + ReleasePostResponse, WorkIdGetResponse, WorkPostResponse}; +#[allow(unused_imports)] +use futures::{future, stream, Future, Stream}; +use tokio_core::reactor; + +fn main() { + let matches = App::new("client") + .arg( + Arg::with_name("operation") + .help("Sets the operation to run") + .possible_values(&[ + "ContainerIdGet", + "ContainerLookupGet", + "ContainerPost", + "CreatorIdGet", + "CreatorLookupGet", + "CreatorPost", + "EditgroupIdAcceptPost", + "EditgroupIdGet", + "EditgroupPost", + "EditorUsernameChangelogGet", + "EditorUsernameGet", + "FileIdGet", + "FileLookupGet", + "FilePost", + "ReleaseIdGet", + "ReleaseLookupGet", + "ReleasePost", + "WorkIdGet", + "WorkPost", + ]) + .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("api.fatcat.wiki") + .help("Hostname to contact"), + ) + .arg( + Arg::with_name("port") + .long("port") + .takes_value(true) + .default_value("8080") + .help("Port to contact"), + ) + .get_matches(); + + let mut core = reactor::Core::new().unwrap(); + 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 + fatcat::Client::try_new_https(core.handle(), &base_url, "examples/ca.pem") + .expect("Failed to create HTTPS client") + } else { + // Using HTTP + fatcat::Client::try_new_http(core.handle(), &base_url) + .expect("Failed to create HTTP client") + }; + + // Using a non-default `Context` is not required; this is just an example! + let client = client.with_context(fatcat::Context::new_with_span_id( + self::uuid::Uuid::new_v4().to_string(), + )); + + match matches.value_of("operation") { + Some("ContainerIdGet") => { + let result = core.run(client.container_id_get("id_example".to_string())); + println!( + "{:?} (X-Span-ID: {:?})", + result, + client + .context() + .x_span_id + .clone() + .unwrap_or(String::from("<none>")) + ); + } + + Some("ContainerLookupGet") => { + let result = core.run(client.container_lookup_get("issn_example".to_string())); + println!( + "{:?} (X-Span-ID: {:?})", + result, + client + .context() + .x_span_id + .clone() + .unwrap_or(String::from("<none>")) + ); + } + + Some("ContainerPost") => { + let result = core.run(client.container_post(None)); + println!( + "{:?} (X-Span-ID: {:?})", + result, + client + .context() + .x_span_id + .clone() + .unwrap_or(String::from("<none>")) + ); + } + + Some("CreatorIdGet") => { + let result = core.run(client.creator_id_get("id_example".to_string())); + println!( + "{:?} (X-Span-ID: {:?})", + result, + client + .context() + .x_span_id + .clone() + .unwrap_or(String::from("<none>")) + ); + } + + Some("CreatorLookupGet") => { + let result = core.run(client.creator_lookup_get("orcid_example".to_string())); + println!( + "{:?} (X-Span-ID: {:?})", + result, + client + .context() + .x_span_id + .clone() + .unwrap_or(String::from("<none>")) + ); + } + + Some("CreatorPost") => { + let result = core.run(client.creator_post(None)); + println!( + "{:?} (X-Span-ID: {:?})", + result, + client + .context() + .x_span_id + .clone() + .unwrap_or(String::from("<none>")) + ); + } + + Some("EditgroupIdAcceptPost") => { + let result = core.run(client.editgroup_id_accept_post(56)); + println!( + "{:?} (X-Span-ID: {:?})", + result, + client + .context() + .x_span_id + .clone() + .unwrap_or(String::from("<none>")) + ); + } + + Some("EditgroupIdGet") => { + let result = core.run(client.editgroup_id_get(56)); + println!( + "{:?} (X-Span-ID: {:?})", + result, + client + .context() + .x_span_id + .clone() + .unwrap_or(String::from("<none>")) + ); + } + + Some("EditgroupPost") => { + let result = core.run(client.editgroup_post()); + println!( + "{:?} (X-Span-ID: {:?})", + result, + client + .context() + .x_span_id + .clone() + .unwrap_or(String::from("<none>")) + ); + } + + Some("EditorUsernameChangelogGet") => { + let result = + core.run(client.editor_username_changelog_get("username_example".to_string())); + println!( + "{:?} (X-Span-ID: {:?})", + result, + client + .context() + .x_span_id + .clone() + .unwrap_or(String::from("<none>")) + ); + } + + Some("EditorUsernameGet") => { + let result = core.run(client.editor_username_get("username_example".to_string())); + println!( + "{:?} (X-Span-ID: {:?})", + result, + client + .context() + .x_span_id + .clone() + .unwrap_or(String::from("<none>")) + ); + } + + Some("FileIdGet") => { + let result = core.run(client.file_id_get("id_example".to_string())); + println!( + "{:?} (X-Span-ID: {:?})", + result, + client + .context() + .x_span_id + .clone() + .unwrap_or(String::from("<none>")) + ); + } + + Some("FileLookupGet") => { + let result = core.run(client.file_lookup_get("sha1_example".to_string())); + println!( + "{:?} (X-Span-ID: {:?})", + result, + client + .context() + .x_span_id + .clone() + .unwrap_or(String::from("<none>")) + ); + } + + Some("FilePost") => { + let result = core.run(client.file_post(None)); + println!( + "{:?} (X-Span-ID: {:?})", + result, + client + .context() + .x_span_id + .clone() + .unwrap_or(String::from("<none>")) + ); + } + + Some("ReleaseIdGet") => { + let result = core.run(client.release_id_get("id_example".to_string())); + println!( + "{:?} (X-Span-ID: {:?})", + result, + client + .context() + .x_span_id + .clone() + .unwrap_or(String::from("<none>")) + ); + } + + Some("ReleaseLookupGet") => { + let result = core.run(client.release_lookup_get("doi_example".to_string())); + println!( + "{:?} (X-Span-ID: {:?})", + result, + client + .context() + .x_span_id + .clone() + .unwrap_or(String::from("<none>")) + ); + } + + Some("ReleasePost") => { + let result = core.run(client.release_post(None)); + println!( + "{:?} (X-Span-ID: {:?})", + result, + client + .context() + .x_span_id + .clone() + .unwrap_or(String::from("<none>")) + ); + } + + Some("WorkIdGet") => { + let result = core.run(client.work_id_get("id_example".to_string())); + println!( + "{:?} (X-Span-ID: {:?})", + result, + client + .context() + .x_span_id + .clone() + .unwrap_or(String::from("<none>")) + ); + } + + Some("WorkPost") => { + let result = core.run(client.work_post(None)); + println!( + "{:?} (X-Span-ID: {:?})", + result, + client + .context() + .x_span_id + .clone() + .unwrap_or(String::from("<none>")) + ); + } + + _ => panic!("Invalid operation provided"), + } +} diff --git a/rust/fatcat-api/examples/server-chain.pem b/rust/fatcat-api/examples/server-chain.pem new file mode 100644 index 00000000..47d7e201 --- /dev/null +++ b/rust/fatcat-api/examples/server-chain.pem @@ -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----- diff --git a/rust/fatcat-api/examples/server-key.pem b/rust/fatcat-api/examples/server-key.pem new file mode 100644 index 00000000..29c00682 --- /dev/null +++ b/rust/fatcat-api/examples/server-key.pem @@ -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----- diff --git a/rust/fatcat-api/examples/server.rs b/rust/fatcat-api/examples/server.rs new file mode 100644 index 00000000..f9b8617c --- /dev/null +++ b/rust/fatcat-api/examples/server.rs @@ -0,0 +1,78 @@ +//! Main binary entry point for fatcat implementation. + +#![allow(missing_docs)] + +// Imports required by this file. +// extern crate <name of this crate>; +extern crate clap; +extern crate fatcat; +extern crate hyper; +extern crate native_tls; +extern crate openssl; +extern crate swagger; +extern crate tokio_proto; +extern crate tokio_tls; + +// Imports required by server library. +// extern crate fatcat; +// extern crate swagger; +extern crate chrono; +extern crate futures; +#[macro_use] +extern crate error_chain; + +use clap::{App, Arg}; +use hyper::server::Http; +use openssl::error::ErrorStack; +use openssl::ssl::{SslAcceptorBuilder, SslMethod}; +use openssl::x509::X509_FILETYPE_PEM; +use swagger::auth::AllowAllAuthenticator; +use tokio_proto::TcpServer; + +mod server_lib; + +// Builds an SSL implementation for Simple HTTPS from some hard-coded file names +fn ssl() -> Result<SslAcceptorBuilder, ErrorStack> { + let mut ssl = SslAcceptorBuilder::mozilla_intermediate_raw(SslMethod::tls())?; + + // Server authentication + ssl.set_private_key_file("examples/server-key.pem", X509_FILETYPE_PEM)?; + ssl.set_certificate_chain_file("examples/server-chain.pem")?; + ssl.check_private_key()?; + + Ok(ssl) +} + +/// Create custom server, wire it to the autogenerated router, +/// and pass it to the web server. +fn main() { + let matches = App::new("server") + .arg( + Arg::with_name("https") + .long("https") + .help("Whether to use HTTPS or not"), + ) + .get_matches(); + + let service_fn = fatcat::server::auth::NewService::new(AllowAllAuthenticator::new( + server_lib::NewService, + "cosmo", + )); + + let addr = "127.0.0.1:8080" + .parse() + .expect("Failed to parse bind address"); + if matches.is_present("https") { + let ssl = ssl().expect("Failed to load SSL keys"); + let builder: native_tls::TlsAcceptorBuilder = + native_tls::backend::openssl::TlsAcceptorBuilderExt::from_openssl(ssl); + let tls_acceptor = builder.build().expect("Failed to build TLS acceptor"); + TcpServer::new( + tokio_tls::proto::Server::new(Http::new(), tls_acceptor), + addr, + ).serve(service_fn); + } else { + // Using HTTP + TcpServer::new(Http::new(), addr).serve(service_fn); + } +} diff --git a/rust/fatcat-api/examples/server_lib/mod.rs b/rust/fatcat-api/examples/server_lib/mod.rs new file mode 100644 index 00000000..a45fbe50 --- /dev/null +++ b/rust/fatcat-api/examples/server_lib/mod.rs @@ -0,0 +1,26 @@ +//! Main library entry point for fatcat implementation. + +mod server; + +mod errors { + error_chain!{} +} + +pub use self::errors::*; +use fatcat; +use hyper; +use std::io; + +pub struct NewService; + +impl hyper::server::NewService for NewService { + type Request = (hyper::Request, fatcat::Context); + type Response = hyper::Response; + type Error = hyper::Error; + type Instance = fatcat::server::Service<server::Server>; + + /// Instantiate a new server. + fn new_service(&self) -> io::Result<Self::Instance> { + Ok(fatcat::server::Service::new(server::Server)) + } +} diff --git a/rust/fatcat-api/examples/server_lib/server.rs b/rust/fatcat-api/examples/server_lib/server.rs new file mode 100644 index 00000000..0dec7ed3 --- /dev/null +++ b/rust/fatcat-api/examples/server_lib/server.rs @@ -0,0 +1,287 @@ +//! Server implementation of fatcat. + +#![allow(unused_imports)] + +use chrono; +use futures::{self, Future}; + +use std::collections::HashMap; + +use swagger; + +use fatcat::models; +use fatcat::{Api, ApiError, ContainerIdGetResponse, ContainerLookupGetResponse, + ContainerPostResponse, Context, CreatorIdGetResponse, CreatorLookupGetResponse, + CreatorPostResponse, EditgroupIdAcceptPostResponse, EditgroupIdGetResponse, + EditgroupPostResponse, EditorUsernameChangelogGetResponse, EditorUsernameGetResponse, + FileIdGetResponse, FileLookupGetResponse, FilePostResponse, ReleaseIdGetResponse, + ReleaseLookupGetResponse, ReleasePostResponse, WorkIdGetResponse, WorkPostResponse}; + +#[derive(Copy, Clone)] +pub struct Server; + +impl Api for Server { + fn container_id_get( + &self, + id: String, + context: &Context, + ) -> Box<Future<Item = ContainerIdGetResponse, Error = ApiError>> { + let context = context.clone(); + println!( + "container_id_get(\"{}\") - X-Span-ID: {:?}", + id, + context.x_span_id.unwrap_or(String::from("<none>")).clone() + ); + Box::new(futures::failed("Generic failure".into())) + } + + fn container_lookup_get( + &self, + issn: String, + context: &Context, + ) -> Box<Future<Item = ContainerLookupGetResponse, Error = ApiError>> { + let context = context.clone(); + println!( + "container_lookup_get(\"{}\") - X-Span-ID: {:?}", + issn, + context.x_span_id.unwrap_or(String::from("<none>")).clone() + ); + Box::new(futures::failed("Generic failure".into())) + } + + fn container_post( + &self, + body: Option<models::ContainerEntity>, + context: &Context, + ) -> Box<Future<Item = ContainerPostResponse, Error = ApiError>> { + let context = context.clone(); + println!( + "container_post({:?}) - X-Span-ID: {:?}", + body, + context.x_span_id.unwrap_or(String::from("<none>")).clone() + ); + Box::new(futures::failed("Generic failure".into())) + } + + fn creator_id_get( + &self, + id: String, + context: &Context, + ) -> Box<Future<Item = CreatorIdGetResponse, Error = ApiError>> { + let context = context.clone(); + println!( + "creator_id_get(\"{}\") - X-Span-ID: {:?}", + id, + context.x_span_id.unwrap_or(String::from("<none>")).clone() + ); + Box::new(futures::failed("Generic failure".into())) + } + + fn creator_lookup_get( + &self, + orcid: String, + context: &Context, + ) -> Box<Future<Item = CreatorLookupGetResponse, Error = ApiError>> { + let context = context.clone(); + println!( + "creator_lookup_get(\"{}\") - X-Span-ID: {:?}", + orcid, + context.x_span_id.unwrap_or(String::from("<none>")).clone() + ); + Box::new(futures::failed("Generic failure".into())) + } + + fn creator_post( + &self, + body: Option<models::CreatorEntity>, + context: &Context, + ) -> Box<Future<Item = CreatorPostResponse, Error = ApiError>> { + let context = context.clone(); + println!( + "creator_post({:?}) - X-Span-ID: {:?}", + body, + context.x_span_id.unwrap_or(String::from("<none>")).clone() + ); + Box::new(futures::failed("Generic failure".into())) + } + + fn editgroup_id_accept_post( + &self, + id: i32, + context: &Context, + ) -> Box<Future<Item = EditgroupIdAcceptPostResponse, Error = ApiError>> { + let context = context.clone(); + println!( + "editgroup_id_accept_post({}) - X-Span-ID: {:?}", + id, + context.x_span_id.unwrap_or(String::from("<none>")).clone() + ); + Box::new(futures::failed("Generic failure".into())) + } + + fn editgroup_id_get( + &self, + id: i32, + context: &Context, + ) -> Box<Future<Item = EditgroupIdGetResponse, Error = ApiError>> { + let context = context.clone(); + println!( + "editgroup_id_get({}) - X-Span-ID: {:?}", + id, + context.x_span_id.unwrap_or(String::from("<none>")).clone() + ); + Box::new(futures::failed("Generic failure".into())) + } + + fn editgroup_post( + &self, + context: &Context, + ) -> Box<Future<Item = EditgroupPostResponse, Error = ApiError>> { + let context = context.clone(); + println!( + "editgroup_post() - X-Span-ID: {:?}", + context.x_span_id.unwrap_or(String::from("<none>")).clone() + ); + Box::new(futures::failed("Generic failure".into())) + } + + fn editor_username_changelog_get( + &self, + username: String, + context: &Context, + ) -> Box<Future<Item = EditorUsernameChangelogGetResponse, Error = ApiError>> { + let context = context.clone(); + println!( + "editor_username_changelog_get(\"{}\") - X-Span-ID: {:?}", + username, + context.x_span_id.unwrap_or(String::from("<none>")).clone() + ); + Box::new(futures::failed("Generic failure".into())) + } + + fn editor_username_get( + &self, + username: String, + context: &Context, + ) -> Box<Future<Item = EditorUsernameGetResponse, Error = ApiError>> { + let context = context.clone(); + println!( + "editor_username_get(\"{}\") - X-Span-ID: {:?}", + username, + context.x_span_id.unwrap_or(String::from("<none>")).clone() + ); + Box::new(futures::failed("Generic failure".into())) + } + + fn file_id_get( + &self, + id: String, + context: &Context, + ) -> Box<Future<Item = FileIdGetResponse, Error = ApiError>> { + let context = context.clone(); + println!( + "file_id_get(\"{}\") - X-Span-ID: {:?}", + id, + context.x_span_id.unwrap_or(String::from("<none>")).clone() + ); + Box::new(futures::failed("Generic failure".into())) + } + + fn file_lookup_get( + &self, + sha1: String, + context: &Context, + ) -> Box<Future<Item = FileLookupGetResponse, Error = ApiError>> { + let context = context.clone(); + println!( + "file_lookup_get(\"{}\") - X-Span-ID: {:?}", + sha1, + context.x_span_id.unwrap_or(String::from("<none>")).clone() + ); + Box::new(futures::failed("Generic failure".into())) + } + + fn file_post( + &self, + body: Option<models::FileEntity>, + context: &Context, + ) -> Box<Future<Item = FilePostResponse, Error = ApiError>> { + let context = context.clone(); + println!( + "file_post({:?}) - X-Span-ID: {:?}", + body, + context.x_span_id.unwrap_or(String::from("<none>")).clone() + ); + Box::new(futures::failed("Generic failure".into())) + } + + fn release_id_get( + &self, + id: String, + context: &Context, + ) -> Box<Future<Item = ReleaseIdGetResponse, Error = ApiError>> { + let context = context.clone(); + println!( + "release_id_get(\"{}\") - X-Span-ID: {:?}", + id, + context.x_span_id.unwrap_or(String::from("<none>")).clone() + ); + Box::new(futures::failed("Generic failure".into())) + } + + fn release_lookup_get( + &self, + doi: String, + context: &Context, + ) -> Box<Future<Item = ReleaseLookupGetResponse, Error = ApiError>> { + let context = context.clone(); + println!( + "release_lookup_get(\"{}\") - X-Span-ID: {:?}", + doi, + context.x_span_id.unwrap_or(String::from("<none>")).clone() + ); + Box::new(futures::failed("Generic failure".into())) + } + + fn release_post( + &self, + body: Option<models::ReleaseEntity>, + context: &Context, + ) -> Box<Future<Item = ReleasePostResponse, Error = ApiError>> { + let context = context.clone(); + println!( + "release_post({:?}) - X-Span-ID: {:?}", + body, + context.x_span_id.unwrap_or(String::from("<none>")).clone() + ); + Box::new(futures::failed("Generic failure".into())) + } + + fn work_id_get( + &self, + id: String, + context: &Context, + ) -> Box<Future<Item = WorkIdGetResponse, Error = ApiError>> { + let context = context.clone(); + println!( + "work_id_get(\"{}\") - X-Span-ID: {:?}", + id, + context.x_span_id.unwrap_or(String::from("<none>")).clone() + ); + Box::new(futures::failed("Generic failure".into())) + } + + fn work_post( + &self, + body: Option<models::WorkEntity>, + context: &Context, + ) -> Box<Future<Item = WorkPostResponse, Error = ApiError>> { + let context = context.clone(); + println!( + "work_post({:?}) - X-Span-ID: {:?}", + body, + context.x_span_id.unwrap_or(String::from("<none>")).clone() + ); + Box::new(futures::failed("Generic failure".into())) + } +} diff --git a/rust/fatcat-api/src/client/mod.rs b/rust/fatcat-api/src/client/mod.rs new file mode 100644 index 00000000..361fd390 --- /dev/null +++ b/rust/fatcat-api/src/client/mod.rs @@ -0,0 +1,2573 @@ +#![allow(unused_extern_crates)] +extern crate chrono; +extern crate hyper_tls; +extern crate mime; +extern crate native_tls; +extern crate openssl; +extern crate tokio_core; +extern crate url; + +use self::tokio_core::reactor::Handle; +use self::url::percent_encoding::{utf8_percent_encode, PATH_SEGMENT_ENCODE_SET, QUERY_ENCODE_SET}; +use futures; +use futures::{Future, Stream}; +use futures::{future, stream}; +use hyper; +use hyper::Uri; +use hyper::header::{ContentType, Headers}; +use std::borrow::Cow; +use std::error; +use std::fmt; +use std::io::{Error, ErrorKind, Read}; +use std::path::Path; +use std::str; +use std::str::FromStr; +use std::sync::Arc; + +use mimetypes; + +use serde_json; + +#[allow(unused_imports)] +use std::collections::{BTreeMap, HashMap}; +#[allow(unused_imports)] +use swagger; + +use swagger::{ApiError, Context, XSpanId}; + +use models; +use {Api, ContainerIdGetResponse, ContainerLookupGetResponse, ContainerPostResponse, + CreatorIdGetResponse, CreatorLookupGetResponse, CreatorPostResponse, + EditgroupIdAcceptPostResponse, EditgroupIdGetResponse, EditgroupPostResponse, + EditorUsernameChangelogGetResponse, EditorUsernameGetResponse, FileIdGetResponse, + FileLookupGetResponse, FilePostResponse, ReleaseIdGetResponse, ReleaseLookupGetResponse, + ReleasePostResponse, WorkIdGetResponse, WorkPostResponse}; + +/// 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().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().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. +#[derive(Clone)] +pub struct Client { + hyper_client: Arc< + Fn( + &Handle + ) -> Box< + hyper::client::Service< + Request = hyper::Request<hyper::Body>, + Response = hyper::Response, + Error = hyper::Error, + Future = hyper::client::FutureResponse, + >, + > + + Sync + + Send, + >, + handle: Arc<Handle>, + base_path: String, +} + +impl fmt::Debug for Client { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Client {{ base_path: {} }}", self.base_path) + } +} + +impl Client { + /// Create an HTTP client. + /// + /// # Arguments + /// * `handle` - tokio reactor handle to use for execution + /// * `base_path` - base path of the client API, i.e. "www.my-api-implementation.com" + pub fn try_new_http(handle: Handle, base_path: &str) -> Result<Client, ClientInitError> { + let http_connector = swagger::http_connector(); + Self::try_new_with_connector::<hyper::client::HttpConnector>( + handle, + base_path, + Some("http"), + http_connector, + ) + } + + /// Create a client with a TLS connection to the server. + /// + /// # Arguments + /// * `handle` - tokio reactor handle to use for execution + /// * `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 + pub fn try_new_https<CA>( + handle: Handle, + base_path: &str, + ca_certificate: CA, + ) -> Result<Client, ClientInitError> + where + CA: AsRef<Path>, + { + let https_connector = swagger::https_connector(ca_certificate); + Self::try_new_with_connector::<hyper_tls::HttpsConnector<hyper::client::HttpConnector>>( + handle, + base_path, + Some("https"), + https_connector, + ) + } + + /// Create a client with a mutually authenticated TLS connection to the server. + /// + /// # Arguments + /// * `handle` - tokio reactor handle to use for execution + /// * `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 + pub fn try_new_https_mutual<CA, K, C, T>( + handle: Handle, + base_path: &str, + ca_certificate: CA, + client_key: K, + client_certificate: C, + ) -> Result<Client, ClientInitError> + where + CA: AsRef<Path>, + K: AsRef<Path>, + C: AsRef<Path>, + { + let https_connector = + swagger::https_mutual_connector(ca_certificate, client_key, client_certificate); + Self::try_new_with_connector::<hyper_tls::HttpsConnector<hyper::client::HttpConnector>>( + handle, + base_path, + Some("https"), + https_connector, + ) + } + + /// 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::{http_connector, https_connector, https_mutual_connector}`. + /// + /// 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 + /// + /// * `handle` - tokio reactor handle to use for execution + /// * `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_fn` - Function which returns an implementation of `hyper::client::Connect` + pub fn try_new_with_connector<C>( + handle: Handle, + base_path: &str, + protocol: Option<&'static str>, + connector_fn: Box<Fn(&Handle) -> C + Send + Sync>, + ) -> Result<Client, ClientInitError> + where + C: hyper::client::Connect + hyper::client::Service, + { + let hyper_client = { + move |handle: &Handle| -> Box< + hyper::client::Service< + Request = hyper::Request<hyper::Body>, + Response = hyper::Response, + Error = hyper::Error, + Future = hyper::client::FutureResponse, + >, + > { + let connector = connector_fn(handle); + Box::new( + hyper::Client::configure() + .connector(connector) + .build(handle), + ) + } + }; + + Ok(Client { + hyper_client: Arc::new(hyper_client), + handle: Arc::new(handle), + base_path: into_base_path(base_path, protocol)?, + }) + } + + /// Constructor for creating a `Client` by passing in a pre-made `hyper` client. + /// + /// One should avoid relying on this function if possible, since it adds a dependency on the underlying transport + /// implementation, which it would be better to abstract away. Therefore, using this function may lead to a loss of + /// code generality, which may make it harder to move the application to a serverless environment, for example. + /// + /// The reason for this function's existence is to support legacy test code, which did mocking at the hyper layer. + /// This is not a recommended way to write new tests. If other reasons are found for using this function, they + /// should be mentioned here. + pub fn try_new_with_hyper_client( + hyper_client: Arc< + Fn( + &Handle + ) -> Box< + hyper::client::Service< + Request = hyper::Request<hyper::Body>, + Response = hyper::Response, + Error = hyper::Error, + Future = hyper::client::FutureResponse, + >, + > + + Sync + + Send, + >, + handle: Handle, + base_path: &str, + ) -> Result<Client, ClientInitError> { + Ok(Client { + hyper_client: hyper_client, + handle: Arc::new(handle), + base_path: into_base_path(base_path, None)?, + }) + } +} + +impl Api for Client { + fn container_id_get( + &self, + param_id: String, + context: &Context, + ) -> Box<Future<Item = ContainerIdGetResponse, Error = ApiError>> { + let uri = format!( + "{}/v0/container/{id}", + self.base_path, + id = utf8_percent_encode(¶m_id.to_string(), PATH_SEGMENT_ENCODE_SET) + ); + + let uri = match Uri::from_str(&uri) { + Ok(uri) => uri, + Err(err) => { + return Box::new(futures::done(Err(ApiError(format!( + "Unable to build URI: {}", + err + ))))) + } + }; + + let mut request = hyper::Request::new(hyper::Method::Get, uri); + + context + .x_span_id + .as_ref() + .map(|header| request.headers_mut().set(XSpanId(header.clone()))); + + let hyper_client = (self.hyper_client)(&*self.handle); + Box::new( + hyper_client + .call(request) + .map_err(|e| ApiError(format!("No response received: {}", e))) + .and_then(|mut response| match response.status().as_u16() { + 200 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::ContainerEntity>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| { + ContainerIdGetResponse::FetchASingleContainerById(body) + }), + ) as Box<Future<Item = _, Error = _>> + } + 400 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::Error>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| ContainerIdGetResponse::BadRequest(body)), + ) as Box<Future<Item = _, Error = _>> + } + 0 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::Error>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| { + ContainerIdGetResponse::GenericErrorResponse(body) + }), + ) as Box<Future<Item = _, Error = _>> + } + code => { + let headers = response.headers().clone(); + Box::new(response.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<Future<Item = _, Error = _>> + } + }), + ) + } + + fn container_lookup_get( + &self, + param_issn: String, + context: &Context, + ) -> Box<Future<Item = ContainerLookupGetResponse, Error = ApiError>> { + // Query parameters + let query_issn = format!("issn={issn}&", issn = param_issn.to_string()); + + let uri = format!( + "{}/v0/container/lookup?{issn}", + self.base_path, + issn = utf8_percent_encode(&query_issn, QUERY_ENCODE_SET) + ); + + let uri = match Uri::from_str(&uri) { + Ok(uri) => uri, + Err(err) => { + return Box::new(futures::done(Err(ApiError(format!( + "Unable to build URI: {}", + err + ))))) + } + }; + + let mut request = hyper::Request::new(hyper::Method::Get, uri); + + context + .x_span_id + .as_ref() + .map(|header| request.headers_mut().set(XSpanId(header.clone()))); + + let hyper_client = (self.hyper_client)(&*self.handle); + Box::new( + hyper_client + .call(request) + .map_err(|e| ApiError(format!("No response received: {}", e))) + .and_then(|mut response| match response.status().as_u16() { + 200 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::ContainerEntity>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| { + ContainerLookupGetResponse::FindASingleContainerByExternalIdentifer(body) + }), + ) as Box<Future<Item = _, Error = _>> + } + 400 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::Error>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| ContainerLookupGetResponse::BadRequest(body)), + ) as Box<Future<Item = _, Error = _>> + } + 404 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::Error>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| ContainerLookupGetResponse::NoSuchContainer(body)), + ) as Box<Future<Item = _, Error = _>> + } + 0 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::Error>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| { + ContainerLookupGetResponse::GenericErrorResponse(body) + }), + ) as Box<Future<Item = _, Error = _>> + } + code => { + let headers = response.headers().clone(); + Box::new(response.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<Future<Item = _, Error = _>> + } + }), + ) + } + + fn container_post( + &self, + param_body: Option<models::ContainerEntity>, + context: &Context, + ) -> Box<Future<Item = ContainerPostResponse, Error = ApiError>> { + let uri = format!("{}/v0/container", self.base_path); + + let uri = match Uri::from_str(&uri) { + Ok(uri) => uri, + Err(err) => { + return Box::new(futures::done(Err(ApiError(format!( + "Unable to build URI: {}", + err + ))))) + } + }; + + let mut request = hyper::Request::new(hyper::Method::Post, uri); + + let body = param_body + .map(|ref body| serde_json::to_string(body).expect("impossible to fail to serialize")); + + if let Some(body) = body { + request.set_body(body.into_bytes()); + } + + request + .headers_mut() + .set(ContentType(mimetypes::requests::CONTAINER_POST.clone())); + context + .x_span_id + .as_ref() + .map(|header| request.headers_mut().set(XSpanId(header.clone()))); + + let hyper_client = (self.hyper_client)(&*self.handle); + Box::new( + hyper_client + .call(request) + .map_err(|e| ApiError(format!("No response received: {}", e))) + .and_then(|mut response| match response.status().as_u16() { + 201 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::EntityEdit>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| ContainerPostResponse::Created(body)), + ) as Box<Future<Item = _, Error = _>> + } + 400 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::Error>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| ContainerPostResponse::BadRequest(body)), + ) as Box<Future<Item = _, Error = _>> + } + 0 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::Error>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| ContainerPostResponse::GenericErrorResponse(body)), + ) as Box<Future<Item = _, Error = _>> + } + code => { + let headers = response.headers().clone(); + Box::new(response.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<Future<Item = _, Error = _>> + } + }), + ) + } + + fn creator_id_get( + &self, + param_id: String, + context: &Context, + ) -> Box<Future<Item = CreatorIdGetResponse, Error = ApiError>> { + let uri = format!( + "{}/v0/creator/{id}", + self.base_path, + id = utf8_percent_encode(¶m_id.to_string(), PATH_SEGMENT_ENCODE_SET) + ); + + let uri = match Uri::from_str(&uri) { + Ok(uri) => uri, + Err(err) => { + return Box::new(futures::done(Err(ApiError(format!( + "Unable to build URI: {}", + err + ))))) + } + }; + + let mut request = hyper::Request::new(hyper::Method::Get, uri); + + context + .x_span_id + .as_ref() + .map(|header| request.headers_mut().set(XSpanId(header.clone()))); + + let hyper_client = (self.hyper_client)(&*self.handle); + Box::new( + hyper_client + .call(request) + .map_err(|e| ApiError(format!("No response received: {}", e))) + .and_then(|mut response| match response.status().as_u16() { + 200 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::CreatorEntity>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| { + CreatorIdGetResponse::FetchASingleCreatorById(body) + }), + ) as Box<Future<Item = _, Error = _>> + } + 400 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::Error>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| CreatorIdGetResponse::BadRequest(body)), + ) as Box<Future<Item = _, Error = _>> + } + 0 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::Error>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| CreatorIdGetResponse::GenericErrorResponse(body)), + ) as Box<Future<Item = _, Error = _>> + } + code => { + let headers = response.headers().clone(); + Box::new(response.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<Future<Item = _, Error = _>> + } + }), + ) + } + + fn creator_lookup_get( + &self, + param_orcid: String, + context: &Context, + ) -> Box<Future<Item = CreatorLookupGetResponse, Error = ApiError>> { + // Query parameters + let query_orcid = format!("orcid={orcid}&", orcid = param_orcid.to_string()); + + let uri = format!( + "{}/v0/creator/lookup?{orcid}", + self.base_path, + orcid = utf8_percent_encode(&query_orcid, QUERY_ENCODE_SET) + ); + + let uri = match Uri::from_str(&uri) { + Ok(uri) => uri, + Err(err) => { + return Box::new(futures::done(Err(ApiError(format!( + "Unable to build URI: {}", + err + ))))) + } + }; + + let mut request = hyper::Request::new(hyper::Method::Get, uri); + + context + .x_span_id + .as_ref() + .map(|header| request.headers_mut().set(XSpanId(header.clone()))); + + let hyper_client = (self.hyper_client)(&*self.handle); + Box::new( + hyper_client + .call(request) + .map_err(|e| ApiError(format!("No response received: {}", e))) + .and_then(|mut response| match response.status().as_u16() { + 200 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::CreatorEntity>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| { + CreatorLookupGetResponse::FindASingleCreatorByExternalIdentifer( + body, + ) + }), + ) as Box<Future<Item = _, Error = _>> + } + 400 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::Error>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| CreatorLookupGetResponse::BadRequest(body)), + ) as Box<Future<Item = _, Error = _>> + } + 404 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::Error>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| CreatorLookupGetResponse::NoSuchCreator(body)), + ) as Box<Future<Item = _, Error = _>> + } + 0 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::Error>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| { + CreatorLookupGetResponse::GenericErrorResponse(body) + }), + ) as Box<Future<Item = _, Error = _>> + } + code => { + let headers = response.headers().clone(); + Box::new(response.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<Future<Item = _, Error = _>> + } + }), + ) + } + + fn creator_post( + &self, + param_body: Option<models::CreatorEntity>, + context: &Context, + ) -> Box<Future<Item = CreatorPostResponse, Error = ApiError>> { + let uri = format!("{}/v0/creator", self.base_path); + + let uri = match Uri::from_str(&uri) { + Ok(uri) => uri, + Err(err) => { + return Box::new(futures::done(Err(ApiError(format!( + "Unable to build URI: {}", + err + ))))) + } + }; + + let mut request = hyper::Request::new(hyper::Method::Post, uri); + + let body = param_body + .map(|ref body| serde_json::to_string(body).expect("impossible to fail to serialize")); + + if let Some(body) = body { + request.set_body(body.into_bytes()); + } + + request + .headers_mut() + .set(ContentType(mimetypes::requests::CREATOR_POST.clone())); + context + .x_span_id + .as_ref() + .map(|header| request.headers_mut().set(XSpanId(header.clone()))); + + let hyper_client = (self.hyper_client)(&*self.handle); + Box::new( + hyper_client + .call(request) + .map_err(|e| ApiError(format!("No response received: {}", e))) + .and_then(|mut response| match response.status().as_u16() { + 201 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::EntityEdit>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| CreatorPostResponse::Created(body)), + ) as Box<Future<Item = _, Error = _>> + } + 400 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::Error>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| CreatorPostResponse::BadRequest(body)), + ) as Box<Future<Item = _, Error = _>> + } + 0 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::Error>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| CreatorPostResponse::GenericErrorResponse(body)), + ) as Box<Future<Item = _, Error = _>> + } + code => { + let headers = response.headers().clone(); + Box::new(response.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<Future<Item = _, Error = _>> + } + }), + ) + } + + fn editgroup_id_accept_post( + &self, + param_id: i32, + context: &Context, + ) -> Box<Future<Item = EditgroupIdAcceptPostResponse, Error = ApiError>> { + let uri = format!( + "{}/v0/editgroup/{id}/accept", + self.base_path, + id = utf8_percent_encode(¶m_id.to_string(), PATH_SEGMENT_ENCODE_SET) + ); + + let uri = match Uri::from_str(&uri) { + Ok(uri) => uri, + Err(err) => { + return Box::new(futures::done(Err(ApiError(format!( + "Unable to build URI: {}", + err + ))))) + } + }; + + let mut request = hyper::Request::new(hyper::Method::Post, uri); + + context + .x_span_id + .as_ref() + .map(|header| request.headers_mut().set(XSpanId(header.clone()))); + + let hyper_client = (self.hyper_client)(&*self.handle); + Box::new( + hyper_client + .call(request) + .map_err(|e| ApiError(format!("No response received: {}", e))) + .and_then(|mut response| match response.status().as_u16() { + 200 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::Success>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| { + EditgroupIdAcceptPostResponse::MergedEditgroupSuccessfully_( + body, + ) + }), + ) as Box<Future<Item = _, Error = _>> + } + 400 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::Error>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| { + EditgroupIdAcceptPostResponse::EditgroupIsInAnUnmergableState( + body, + ) + }), + ) as Box<Future<Item = _, Error = _>> + } + 404 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::Error>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| { + EditgroupIdAcceptPostResponse::NoSuchEditgroup(body) + }), + ) as Box<Future<Item = _, Error = _>> + } + 0 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::Error>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| { + EditgroupIdAcceptPostResponse::GenericErrorResponse(body) + }), + ) as Box<Future<Item = _, Error = _>> + } + code => { + let headers = response.headers().clone(); + Box::new(response.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<Future<Item = _, Error = _>> + } + }), + ) + } + + fn editgroup_id_get( + &self, + param_id: i32, + context: &Context, + ) -> Box<Future<Item = EditgroupIdGetResponse, Error = ApiError>> { + let uri = format!( + "{}/v0/editgroup/{id}", + self.base_path, + id = utf8_percent_encode(¶m_id.to_string(), PATH_SEGMENT_ENCODE_SET) + ); + + let uri = match Uri::from_str(&uri) { + Ok(uri) => uri, + Err(err) => { + return Box::new(futures::done(Err(ApiError(format!( + "Unable to build URI: {}", + err + ))))) + } + }; + + let mut request = hyper::Request::new(hyper::Method::Get, uri); + + context + .x_span_id + .as_ref() + .map(|header| request.headers_mut().set(XSpanId(header.clone()))); + + let hyper_client = (self.hyper_client)(&*self.handle); + Box::new( + hyper_client + .call(request) + .map_err(|e| ApiError(format!("No response received: {}", e))) + .and_then(|mut response| match response.status().as_u16() { + 200 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::Editgroup>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| { + EditgroupIdGetResponse::FetchEditgroupByIdentifier(body) + }), + ) as Box<Future<Item = _, Error = _>> + } + 404 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::Error>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| EditgroupIdGetResponse::NoSuchEditgroup(body)), + ) as Box<Future<Item = _, Error = _>> + } + 0 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::Error>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| { + EditgroupIdGetResponse::GenericErrorResponse(body) + }), + ) as Box<Future<Item = _, Error = _>> + } + code => { + let headers = response.headers().clone(); + Box::new(response.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<Future<Item = _, Error = _>> + } + }), + ) + } + + fn editgroup_post( + &self, + context: &Context, + ) -> Box<Future<Item = EditgroupPostResponse, Error = ApiError>> { + let uri = format!("{}/v0/editgroup", self.base_path); + + let uri = match Uri::from_str(&uri) { + Ok(uri) => uri, + Err(err) => { + return Box::new(futures::done(Err(ApiError(format!( + "Unable to build URI: {}", + err + ))))) + } + }; + + let mut request = hyper::Request::new(hyper::Method::Post, uri); + + context + .x_span_id + .as_ref() + .map(|header| request.headers_mut().set(XSpanId(header.clone()))); + + let hyper_client = (self.hyper_client)(&*self.handle); + Box::new( + hyper_client + .call(request) + .map_err(|e| ApiError(format!("No response received: {}", e))) + .and_then(|mut response| match response.status().as_u16() { + 201 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::Editgroup>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| EditgroupPostResponse::SuccessfullyCreated(body)), + ) as Box<Future<Item = _, Error = _>> + } + 400 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::Error>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| { + EditgroupPostResponse::InvalidRequestParameters(body) + }), + ) as Box<Future<Item = _, Error = _>> + } + 0 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::Error>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| EditgroupPostResponse::GenericErrorResponse(body)), + ) as Box<Future<Item = _, Error = _>> + } + code => { + let headers = response.headers().clone(); + Box::new(response.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<Future<Item = _, Error = _>> + } + }), + ) + } + + fn editor_username_changelog_get( + &self, + param_username: String, + context: &Context, + ) -> Box<Future<Item = EditorUsernameChangelogGetResponse, Error = ApiError>> { + let uri = format!( + "{}/v0/editor/{username}/changelog", + self.base_path, + username = utf8_percent_encode(¶m_username.to_string(), PATH_SEGMENT_ENCODE_SET) + ); + + let uri = match Uri::from_str(&uri) { + Ok(uri) => uri, + Err(err) => { + return Box::new(futures::done(Err(ApiError(format!( + "Unable to build URI: {}", + err + ))))) + } + }; + + let mut request = hyper::Request::new(hyper::Method::Get, uri); + + context + .x_span_id + .as_ref() + .map(|header| request.headers_mut().set(XSpanId(header.clone()))); + + let hyper_client = (self.hyper_client)(&*self.handle); + Box::new( + hyper_client + .call(request) + .map_err(|e| ApiError(format!("No response received: {}", e))) + .and_then(|mut response| match response.status().as_u16() { + 200 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::Changelogentry>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| { + EditorUsernameChangelogGetResponse::FindChanges_(body) + }), + ) as Box<Future<Item = _, Error = _>> + } + 404 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::Error>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| { + EditorUsernameChangelogGetResponse::UsernameNotFound(body) + }), + ) as Box<Future<Item = _, Error = _>> + } + 0 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::Error>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| { + EditorUsernameChangelogGetResponse::GenericErrorResponse(body) + }), + ) as Box<Future<Item = _, Error = _>> + } + code => { + let headers = response.headers().clone(); + Box::new(response.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<Future<Item = _, Error = _>> + } + }), + ) + } + + fn editor_username_get( + &self, + param_username: String, + context: &Context, + ) -> Box<Future<Item = EditorUsernameGetResponse, Error = ApiError>> { + let uri = format!( + "{}/v0/editor/{username}", + self.base_path, + username = utf8_percent_encode(¶m_username.to_string(), PATH_SEGMENT_ENCODE_SET) + ); + + let uri = match Uri::from_str(&uri) { + Ok(uri) => uri, + Err(err) => { + return Box::new(futures::done(Err(ApiError(format!( + "Unable to build URI: {}", + err + ))))) + } + }; + + let mut request = hyper::Request::new(hyper::Method::Get, uri); + + context + .x_span_id + .as_ref() + .map(|header| request.headers_mut().set(XSpanId(header.clone()))); + + let hyper_client = (self.hyper_client)(&*self.handle); + Box::new( + hyper_client + .call(request) + .map_err(|e| ApiError(format!("No response received: {}", e))) + .and_then(|mut response| match response.status().as_u16() { + 200 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::Editor>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| { + EditorUsernameGetResponse::FetchGenericInformationAboutAnEditor( + body, + ) + }), + ) as Box<Future<Item = _, Error = _>> + } + 404 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::Error>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| EditorUsernameGetResponse::UsernameNotFound(body)), + ) as Box<Future<Item = _, Error = _>> + } + 0 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::Error>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| { + EditorUsernameGetResponse::GenericErrorResponse(body) + }), + ) as Box<Future<Item = _, Error = _>> + } + code => { + let headers = response.headers().clone(); + Box::new(response.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<Future<Item = _, Error = _>> + } + }), + ) + } + + fn file_id_get( + &self, + param_id: String, + context: &Context, + ) -> Box<Future<Item = FileIdGetResponse, Error = ApiError>> { + let uri = format!( + "{}/v0/file/{id}", + self.base_path, + id = utf8_percent_encode(¶m_id.to_string(), PATH_SEGMENT_ENCODE_SET) + ); + + let uri = match Uri::from_str(&uri) { + Ok(uri) => uri, + Err(err) => { + return Box::new(futures::done(Err(ApiError(format!( + "Unable to build URI: {}", + err + ))))) + } + }; + + let mut request = hyper::Request::new(hyper::Method::Get, uri); + + context + .x_span_id + .as_ref() + .map(|header| request.headers_mut().set(XSpanId(header.clone()))); + + let hyper_client = (self.hyper_client)(&*self.handle); + Box::new( + hyper_client + .call(request) + .map_err(|e| ApiError(format!("No response received: {}", e))) + .and_then(|mut response| match response.status().as_u16() { + 200 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::FileEntity>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| FileIdGetResponse::FetchASingleFileById(body)), + ) as Box<Future<Item = _, Error = _>> + } + 400 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::Error>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| FileIdGetResponse::BadRequest(body)), + ) as Box<Future<Item = _, Error = _>> + } + 0 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::Error>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| FileIdGetResponse::GenericErrorResponse(body)), + ) as Box<Future<Item = _, Error = _>> + } + code => { + let headers = response.headers().clone(); + Box::new(response.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<Future<Item = _, Error = _>> + } + }), + ) + } + + fn file_lookup_get( + &self, + param_sha1: String, + context: &Context, + ) -> Box<Future<Item = FileLookupGetResponse, Error = ApiError>> { + // Query parameters + let query_sha1 = format!("sha1={sha1}&", sha1 = param_sha1.to_string()); + + let uri = format!( + "{}/v0/file/lookup?{sha1}", + self.base_path, + sha1 = utf8_percent_encode(&query_sha1, QUERY_ENCODE_SET) + ); + + let uri = match Uri::from_str(&uri) { + Ok(uri) => uri, + Err(err) => { + return Box::new(futures::done(Err(ApiError(format!( + "Unable to build URI: {}", + err + ))))) + } + }; + + let mut request = hyper::Request::new(hyper::Method::Get, uri); + + context + .x_span_id + .as_ref() + .map(|header| request.headers_mut().set(XSpanId(header.clone()))); + + let hyper_client = (self.hyper_client)(&*self.handle); + Box::new( + hyper_client + .call(request) + .map_err(|e| ApiError(format!("No response received: {}", e))) + .and_then(|mut response| match response.status().as_u16() { + 200 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::FileEntity>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| { + FileLookupGetResponse::FindASingleFileByExternalIdentifer(body) + }), + ) as Box<Future<Item = _, Error = _>> + } + 400 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::Error>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| FileLookupGetResponse::BadRequest(body)), + ) as Box<Future<Item = _, Error = _>> + } + 404 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::Error>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| FileLookupGetResponse::NoSuchFile(body)), + ) as Box<Future<Item = _, Error = _>> + } + 0 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::Error>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| FileLookupGetResponse::GenericErrorResponse(body)), + ) as Box<Future<Item = _, Error = _>> + } + code => { + let headers = response.headers().clone(); + Box::new(response.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<Future<Item = _, Error = _>> + } + }), + ) + } + + fn file_post( + &self, + param_body: Option<models::FileEntity>, + context: &Context, + ) -> Box<Future<Item = FilePostResponse, Error = ApiError>> { + let uri = format!("{}/v0/file", self.base_path); + + let uri = match Uri::from_str(&uri) { + Ok(uri) => uri, + Err(err) => { + return Box::new(futures::done(Err(ApiError(format!( + "Unable to build URI: {}", + err + ))))) + } + }; + + let mut request = hyper::Request::new(hyper::Method::Post, uri); + + let body = param_body + .map(|ref body| serde_json::to_string(body).expect("impossible to fail to serialize")); + + if let Some(body) = body { + request.set_body(body.into_bytes()); + } + + request + .headers_mut() + .set(ContentType(mimetypes::requests::FILE_POST.clone())); + context + .x_span_id + .as_ref() + .map(|header| request.headers_mut().set(XSpanId(header.clone()))); + + let hyper_client = (self.hyper_client)(&*self.handle); + Box::new( + hyper_client + .call(request) + .map_err(|e| ApiError(format!("No response received: {}", e))) + .and_then(|mut response| match response.status().as_u16() { + 201 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::EntityEdit>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| FilePostResponse::Created(body)), + ) as Box<Future<Item = _, Error = _>> + } + 400 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::Error>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| FilePostResponse::BadRequest(body)), + ) as Box<Future<Item = _, Error = _>> + } + 0 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::Error>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| FilePostResponse::GenericErrorResponse(body)), + ) as Box<Future<Item = _, Error = _>> + } + code => { + let headers = response.headers().clone(); + Box::new(response.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<Future<Item = _, Error = _>> + } + }), + ) + } + + fn release_id_get( + &self, + param_id: String, + context: &Context, + ) -> Box<Future<Item = ReleaseIdGetResponse, Error = ApiError>> { + let uri = format!( + "{}/v0/release/{id}", + self.base_path, + id = utf8_percent_encode(¶m_id.to_string(), PATH_SEGMENT_ENCODE_SET) + ); + + let uri = match Uri::from_str(&uri) { + Ok(uri) => uri, + Err(err) => { + return Box::new(futures::done(Err(ApiError(format!( + "Unable to build URI: {}", + err + ))))) + } + }; + + let mut request = hyper::Request::new(hyper::Method::Get, uri); + + context + .x_span_id + .as_ref() + .map(|header| request.headers_mut().set(XSpanId(header.clone()))); + + let hyper_client = (self.hyper_client)(&*self.handle); + Box::new( + hyper_client + .call(request) + .map_err(|e| ApiError(format!("No response received: {}", e))) + .and_then(|mut response| match response.status().as_u16() { + 200 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::ReleaseEntity>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| { + ReleaseIdGetResponse::FetchASingleReleaseById(body) + }), + ) as Box<Future<Item = _, Error = _>> + } + 400 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::Error>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| ReleaseIdGetResponse::BadRequest(body)), + ) as Box<Future<Item = _, Error = _>> + } + 0 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::Error>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| ReleaseIdGetResponse::GenericErrorResponse(body)), + ) as Box<Future<Item = _, Error = _>> + } + code => { + let headers = response.headers().clone(); + Box::new(response.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<Future<Item = _, Error = _>> + } + }), + ) + } + + fn release_lookup_get( + &self, + param_doi: String, + context: &Context, + ) -> Box<Future<Item = ReleaseLookupGetResponse, Error = ApiError>> { + // Query parameters + let query_doi = format!("doi={doi}&", doi = param_doi.to_string()); + + let uri = format!( + "{}/v0/release/lookup?{doi}", + self.base_path, + doi = utf8_percent_encode(&query_doi, QUERY_ENCODE_SET) + ); + + let uri = match Uri::from_str(&uri) { + Ok(uri) => uri, + Err(err) => { + return Box::new(futures::done(Err(ApiError(format!( + "Unable to build URI: {}", + err + ))))) + } + }; + + let mut request = hyper::Request::new(hyper::Method::Get, uri); + + context + .x_span_id + .as_ref() + .map(|header| request.headers_mut().set(XSpanId(header.clone()))); + + let hyper_client = (self.hyper_client)(&*self.handle); + Box::new( + hyper_client + .call(request) + .map_err(|e| ApiError(format!("No response received: {}", e))) + .and_then(|mut response| match response.status().as_u16() { + 200 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::ReleaseEntity>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| { + ReleaseLookupGetResponse::FindASingleReleaseByExternalIdentifer( + body, + ) + }), + ) as Box<Future<Item = _, Error = _>> + } + 400 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::Error>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| ReleaseLookupGetResponse::BadRequest(body)), + ) as Box<Future<Item = _, Error = _>> + } + 404 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::Error>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| ReleaseLookupGetResponse::NoSuchRelease(body)), + ) as Box<Future<Item = _, Error = _>> + } + 0 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::Error>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| { + ReleaseLookupGetResponse::GenericErrorResponse(body) + }), + ) as Box<Future<Item = _, Error = _>> + } + code => { + let headers = response.headers().clone(); + Box::new(response.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<Future<Item = _, Error = _>> + } + }), + ) + } + + fn release_post( + &self, + param_body: Option<models::ReleaseEntity>, + context: &Context, + ) -> Box<Future<Item = ReleasePostResponse, Error = ApiError>> { + let uri = format!("{}/v0/release", self.base_path); + + let uri = match Uri::from_str(&uri) { + Ok(uri) => uri, + Err(err) => { + return Box::new(futures::done(Err(ApiError(format!( + "Unable to build URI: {}", + err + ))))) + } + }; + + let mut request = hyper::Request::new(hyper::Method::Post, uri); + + let body = param_body + .map(|ref body| serde_json::to_string(body).expect("impossible to fail to serialize")); + + if let Some(body) = body { + request.set_body(body.into_bytes()); + } + + request + .headers_mut() + .set(ContentType(mimetypes::requests::RELEASE_POST.clone())); + context + .x_span_id + .as_ref() + .map(|header| request.headers_mut().set(XSpanId(header.clone()))); + + let hyper_client = (self.hyper_client)(&*self.handle); + Box::new( + hyper_client + .call(request) + .map_err(|e| ApiError(format!("No response received: {}", e))) + .and_then(|mut response| match response.status().as_u16() { + 201 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::EntityEdit>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| ReleasePostResponse::Created(body)), + ) as Box<Future<Item = _, Error = _>> + } + 400 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::Error>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| ReleasePostResponse::BadRequest(body)), + ) as Box<Future<Item = _, Error = _>> + } + 0 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::Error>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| ReleasePostResponse::GenericErrorResponse(body)), + ) as Box<Future<Item = _, Error = _>> + } + code => { + let headers = response.headers().clone(); + Box::new(response.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<Future<Item = _, Error = _>> + } + }), + ) + } + + fn work_id_get( + &self, + param_id: String, + context: &Context, + ) -> Box<Future<Item = WorkIdGetResponse, Error = ApiError>> { + let uri = format!( + "{}/v0/work/{id}", + self.base_path, + id = utf8_percent_encode(¶m_id.to_string(), PATH_SEGMENT_ENCODE_SET) + ); + + let uri = match Uri::from_str(&uri) { + Ok(uri) => uri, + Err(err) => { + return Box::new(futures::done(Err(ApiError(format!( + "Unable to build URI: {}", + err + ))))) + } + }; + + let mut request = hyper::Request::new(hyper::Method::Get, uri); + + context + .x_span_id + .as_ref() + .map(|header| request.headers_mut().set(XSpanId(header.clone()))); + + let hyper_client = (self.hyper_client)(&*self.handle); + Box::new( + hyper_client + .call(request) + .map_err(|e| ApiError(format!("No response received: {}", e))) + .and_then(|mut response| match response.status().as_u16() { + 200 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::WorkEntity>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| WorkIdGetResponse::FetchASingleWorkById(body)), + ) as Box<Future<Item = _, Error = _>> + } + 400 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::Error>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| WorkIdGetResponse::BadRequest(body)), + ) as Box<Future<Item = _, Error = _>> + } + 0 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::Error>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| WorkIdGetResponse::GenericErrorResponse(body)), + ) as Box<Future<Item = _, Error = _>> + } + code => { + let headers = response.headers().clone(); + Box::new(response.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<Future<Item = _, Error = _>> + } + }), + ) + } + + fn work_post( + &self, + param_body: Option<models::WorkEntity>, + context: &Context, + ) -> Box<Future<Item = WorkPostResponse, Error = ApiError>> { + let uri = format!("{}/v0/work", self.base_path); + + let uri = match Uri::from_str(&uri) { + Ok(uri) => uri, + Err(err) => { + return Box::new(futures::done(Err(ApiError(format!( + "Unable to build URI: {}", + err + ))))) + } + }; + + let mut request = hyper::Request::new(hyper::Method::Post, uri); + + let body = param_body + .map(|ref body| serde_json::to_string(body).expect("impossible to fail to serialize")); + + if let Some(body) = body { + request.set_body(body.into_bytes()); + } + + request + .headers_mut() + .set(ContentType(mimetypes::requests::WORK_POST.clone())); + context + .x_span_id + .as_ref() + .map(|header| request.headers_mut().set(XSpanId(header.clone()))); + + let hyper_client = (self.hyper_client)(&*self.handle); + Box::new( + hyper_client + .call(request) + .map_err(|e| ApiError(format!("No response received: {}", e))) + .and_then(|mut response| match response.status().as_u16() { + 201 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::EntityEdit>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| WorkPostResponse::Created(body)), + ) as Box<Future<Item = _, Error = _>> + } + 400 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::Error>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| WorkPostResponse::BadRequest(body)), + ) as Box<Future<Item = _, Error = _>> + } + 0 => { + let body = response.body(); + Box::new( + body.concat2() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))) + .and_then(|body| { + str::from_utf8(&body) + .map_err(|e| { + ApiError(format!("Response was not valid UTF8: {}", e)) + }) + .and_then(|body| { + serde_json::from_str::<models::Error>(body) + .map_err(|e| e.into()) + }) + }) + .map(move |body| WorkPostResponse::GenericErrorResponse(body)), + ) as Box<Future<Item = _, Error = _>> + } + code => { + let headers = response.headers().clone(); + Box::new(response.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<Future<Item = _, Error = _>> + } + }), + ) + } +} + +#[derive(Debug)] +pub enum ClientInitError { + InvalidScheme, + InvalidUri(hyper::error::UriError), + MissingHost, + SslError(openssl::error::ErrorStack), +} + +impl From<hyper::error::UriError> for ClientInitError { + fn from(err: hyper::error::UriError) -> ClientInitError { + ClientInitError::InvalidUri(err) + } +} + +impl From<openssl::error::ErrorStack> for ClientInitError { + fn from(err: openssl::error::ErrorStack) -> ClientInitError { + ClientInitError::SslError(err) + } +} + +impl fmt::Display for ClientInitError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + (self as &fmt::Debug).fmt(f) + } +} + +impl error::Error for ClientInitError { + fn description(&self) -> &str { + "Failed to produce a hyper client." + } +} diff --git a/rust/fatcat-api/src/lib.rs b/rust/fatcat-api/src/lib.rs new file mode 100644 index 00000000..36573537 --- /dev/null +++ b/rust/fatcat-api/src/lib.rs @@ -0,0 +1,599 @@ +#![allow(missing_docs, trivial_casts, unused_variables, unused_mut, unused_imports, + unused_extern_crates, non_camel_case_types)] +extern crate serde; +#[macro_use] +extern crate serde_derive; +extern crate serde_json; + +extern crate chrono; +extern crate futures; +#[macro_use] +extern crate lazy_static; +#[macro_use] +extern crate log; + +// Logically this should be in the client and server modules, but rust doesn't allow `macro_use` from a module. +#[cfg(any(feature = "client", feature = "server"))] +#[macro_use] +extern crate hyper; + +extern crate swagger; + +use futures::Stream; +use std::io::Error; + +#[allow(unused_imports)] +use std::collections::HashMap; + +pub use futures::Future; + +#[cfg(any(feature = "client", feature = "server"))] +mod mimetypes; + +pub use swagger::{ApiError, Context, ContextWrapper}; + +pub const BASE_PATH: &'static str = "/v0"; +pub const API_VERSION: &'static str = "0.1.0"; + +#[derive(Debug, PartialEq)] +pub enum ContainerIdGetResponse { + /// fetch a single container by id + FetchASingleContainerById(models::ContainerEntity), + /// bad request + BadRequest(models::Error), + /// generic error response + GenericErrorResponse(models::Error), +} + +#[derive(Debug, PartialEq)] +pub enum ContainerLookupGetResponse { + /// find a single container by external identifer + FindASingleContainerByExternalIdentifer(models::ContainerEntity), + /// bad request + BadRequest(models::Error), + /// no such container + NoSuchContainer(models::Error), + /// generic error response + GenericErrorResponse(models::Error), +} + +#[derive(Debug, PartialEq)] +pub enum ContainerPostResponse { + /// created + Created(models::EntityEdit), + /// bad request + BadRequest(models::Error), + /// generic error response + GenericErrorResponse(models::Error), +} + +#[derive(Debug, PartialEq)] +pub enum CreatorIdGetResponse { + /// fetch a single creator by id + FetchASingleCreatorById(models::CreatorEntity), + /// bad request + BadRequest(models::Error), + /// generic error response + GenericErrorResponse(models::Error), +} + +#[derive(Debug, PartialEq)] +pub enum CreatorLookupGetResponse { + /// find a single creator by external identifer + FindASingleCreatorByExternalIdentifer(models::CreatorEntity), + /// bad request + BadRequest(models::Error), + /// no such creator + NoSuchCreator(models::Error), + /// generic error response + GenericErrorResponse(models::Error), +} + +#[derive(Debug, PartialEq)] +pub enum CreatorPostResponse { + /// created + Created(models::EntityEdit), + /// bad request + BadRequest(models::Error), + /// generic error response + GenericErrorResponse(models::Error), +} + +#[derive(Debug, PartialEq)] +pub enum EditgroupIdAcceptPostResponse { + /// merged editgroup successfully (\"live\") + MergedEditgroupSuccessfully_(models::Success), + /// editgroup is in an unmergable state + EditgroupIsInAnUnmergableState(models::Error), + /// no such editgroup + NoSuchEditgroup(models::Error), + /// generic error response + GenericErrorResponse(models::Error), +} + +#[derive(Debug, PartialEq)] +pub enum EditgroupIdGetResponse { + /// fetch editgroup by identifier + FetchEditgroupByIdentifier(models::Editgroup), + /// no such editgroup + NoSuchEditgroup(models::Error), + /// generic error response + GenericErrorResponse(models::Error), +} + +#[derive(Debug, PartialEq)] +pub enum EditgroupPostResponse { + /// successfully created + SuccessfullyCreated(models::Editgroup), + /// invalid request parameters + InvalidRequestParameters(models::Error), + /// generic error response + GenericErrorResponse(models::Error), +} + +#[derive(Debug, PartialEq)] +pub enum EditorUsernameChangelogGetResponse { + /// find changes (editgroups) by this editor which have been merged + FindChanges_(models::Changelogentry), + /// username not found + UsernameNotFound(models::Error), + /// generic error response + GenericErrorResponse(models::Error), +} + +#[derive(Debug, PartialEq)] +pub enum EditorUsernameGetResponse { + /// fetch generic information about an editor + FetchGenericInformationAboutAnEditor(models::Editor), + /// username not found + UsernameNotFound(models::Error), + /// generic error response + GenericErrorResponse(models::Error), +} + +#[derive(Debug, PartialEq)] +pub enum FileIdGetResponse { + /// fetch a single file by id + FetchASingleFileById(models::FileEntity), + /// bad request + BadRequest(models::Error), + /// generic error response + GenericErrorResponse(models::Error), +} + +#[derive(Debug, PartialEq)] +pub enum FileLookupGetResponse { + /// find a single file by external identifer + FindASingleFileByExternalIdentifer(models::FileEntity), + /// bad request + BadRequest(models::Error), + /// no such file + NoSuchFile(models::Error), + /// generic error response + GenericErrorResponse(models::Error), +} + +#[derive(Debug, PartialEq)] +pub enum FilePostResponse { + /// created + Created(models::EntityEdit), + /// bad request + BadRequest(models::Error), + /// generic error response + GenericErrorResponse(models::Error), +} + +#[derive(Debug, PartialEq)] +pub enum ReleaseIdGetResponse { + /// fetch a single release by id + FetchASingleReleaseById(models::ReleaseEntity), + /// bad request + BadRequest(models::Error), + /// generic error response + GenericErrorResponse(models::Error), +} + +#[derive(Debug, PartialEq)] +pub enum ReleaseLookupGetResponse { + /// find a single release by external identifer + FindASingleReleaseByExternalIdentifer(models::ReleaseEntity), + /// bad request + BadRequest(models::Error), + /// no such release + NoSuchRelease(models::Error), + /// generic error response + GenericErrorResponse(models::Error), +} + +#[derive(Debug, PartialEq)] +pub enum ReleasePostResponse { + /// created + Created(models::EntityEdit), + /// bad request + BadRequest(models::Error), + /// generic error response + GenericErrorResponse(models::Error), +} + +#[derive(Debug, PartialEq)] +pub enum WorkIdGetResponse { + /// fetch a single work by id + FetchASingleWorkById(models::WorkEntity), + /// bad request + BadRequest(models::Error), + /// generic error response + GenericErrorResponse(models::Error), +} + +#[derive(Debug, PartialEq)] +pub enum WorkPostResponse { + /// created + Created(models::EntityEdit), + /// bad request + BadRequest(models::Error), + /// generic error response + GenericErrorResponse(models::Error), +} + +/// API +pub trait Api { + fn container_id_get( + &self, + id: String, + context: &Context, + ) -> Box<Future<Item = ContainerIdGetResponse, Error = ApiError>>; + + fn container_lookup_get( + &self, + issn: String, + context: &Context, + ) -> Box<Future<Item = ContainerLookupGetResponse, Error = ApiError>>; + + fn container_post( + &self, + body: Option<models::ContainerEntity>, + context: &Context, + ) -> Box<Future<Item = ContainerPostResponse, Error = ApiError>>; + + fn creator_id_get( + &self, + id: String, + context: &Context, + ) -> Box<Future<Item = CreatorIdGetResponse, Error = ApiError>>; + + fn creator_lookup_get( + &self, + orcid: String, + context: &Context, + ) -> Box<Future<Item = CreatorLookupGetResponse, Error = ApiError>>; + + fn creator_post( + &self, + body: Option<models::CreatorEntity>, + context: &Context, + ) -> Box<Future<Item = CreatorPostResponse, Error = ApiError>>; + + fn editgroup_id_accept_post( + &self, + id: i32, + context: &Context, + ) -> Box<Future<Item = EditgroupIdAcceptPostResponse, Error = ApiError>>; + + fn editgroup_id_get( + &self, + id: i32, + context: &Context, + ) -> Box<Future<Item = EditgroupIdGetResponse, Error = ApiError>>; + + fn editgroup_post( + &self, + context: &Context, + ) -> Box<Future<Item = EditgroupPostResponse, Error = ApiError>>; + + fn editor_username_changelog_get( + &self, + username: String, + context: &Context, + ) -> Box<Future<Item = EditorUsernameChangelogGetResponse, Error = ApiError>>; + + fn editor_username_get( + &self, + username: String, + context: &Context, + ) -> Box<Future<Item = EditorUsernameGetResponse, Error = ApiError>>; + + fn file_id_get( + &self, + id: String, + context: &Context, + ) -> Box<Future<Item = FileIdGetResponse, Error = ApiError>>; + + fn file_lookup_get( + &self, + sha1: String, + context: &Context, + ) -> Box<Future<Item = FileLookupGetResponse, Error = ApiError>>; + + fn file_post( + &self, + body: Option<models::FileEntity>, + context: &Context, + ) -> Box<Future<Item = FilePostResponse, Error = ApiError>>; + + fn release_id_get( + &self, + id: String, + context: &Context, + ) -> Box<Future<Item = ReleaseIdGetResponse, Error = ApiError>>; + + fn release_lookup_get( + &self, + doi: String, + context: &Context, + ) -> Box<Future<Item = ReleaseLookupGetResponse, Error = ApiError>>; + + fn release_post( + &self, + body: Option<models::ReleaseEntity>, + context: &Context, + ) -> Box<Future<Item = ReleasePostResponse, Error = ApiError>>; + + fn work_id_get( + &self, + id: String, + context: &Context, + ) -> Box<Future<Item = WorkIdGetResponse, Error = ApiError>>; + + fn work_post( + &self, + body: Option<models::WorkEntity>, + context: &Context, + ) -> Box<Future<Item = WorkPostResponse, Error = ApiError>>; +} + +/// API without a `Context` +pub trait ApiNoContext { + fn container_id_get( + &self, + id: String, + ) -> Box<Future<Item = ContainerIdGetResponse, Error = ApiError>>; + + fn container_lookup_get( + &self, + issn: String, + ) -> Box<Future<Item = ContainerLookupGetResponse, Error = ApiError>>; + + fn container_post( + &self, + body: Option<models::ContainerEntity>, + ) -> Box<Future<Item = ContainerPostResponse, Error = ApiError>>; + + fn creator_id_get( + &self, + id: String, + ) -> Box<Future<Item = CreatorIdGetResponse, Error = ApiError>>; + + fn creator_lookup_get( + &self, + orcid: String, + ) -> Box<Future<Item = CreatorLookupGetResponse, Error = ApiError>>; + + fn creator_post( + &self, + body: Option<models::CreatorEntity>, + ) -> Box<Future<Item = CreatorPostResponse, Error = ApiError>>; + + fn editgroup_id_accept_post( + &self, + id: i32, + ) -> Box<Future<Item = EditgroupIdAcceptPostResponse, Error = ApiError>>; + + fn editgroup_id_get( + &self, + id: i32, + ) -> Box<Future<Item = EditgroupIdGetResponse, Error = ApiError>>; + + fn editgroup_post(&self) -> Box<Future<Item = EditgroupPostResponse, Error = ApiError>>; + + fn editor_username_changelog_get( + &self, + username: String, + ) -> Box<Future<Item = EditorUsernameChangelogGetResponse, Error = ApiError>>; + + fn editor_username_get( + &self, + username: String, + ) -> Box<Future<Item = EditorUsernameGetResponse, Error = ApiError>>; + + fn file_id_get(&self, id: String) -> Box<Future<Item = FileIdGetResponse, Error = ApiError>>; + + fn file_lookup_get( + &self, + sha1: String, + ) -> Box<Future<Item = FileLookupGetResponse, Error = ApiError>>; + + fn file_post( + &self, + body: Option<models::FileEntity>, + ) -> Box<Future<Item = FilePostResponse, Error = ApiError>>; + + fn release_id_get( + &self, + id: String, + ) -> Box<Future<Item = ReleaseIdGetResponse, Error = ApiError>>; + + fn release_lookup_get( + &self, + doi: String, + ) -> Box<Future<Item = ReleaseLookupGetResponse, Error = ApiError>>; + + fn release_post( + &self, + body: Option<models::ReleaseEntity>, + ) -> Box<Future<Item = ReleasePostResponse, Error = ApiError>>; + + fn work_id_get(&self, id: String) -> Box<Future<Item = WorkIdGetResponse, Error = ApiError>>; + + fn work_post( + &self, + body: Option<models::WorkEntity>, + ) -> Box<Future<Item = WorkPostResponse, Error = ApiError>>; +} + +/// Trait to extend an API to make it easy to bind it to a context. +pub trait ContextWrapperExt<'a> +where + Self: Sized, +{ + /// Binds this API to a context. + fn with_context(self: &'a Self, context: Context) -> ContextWrapper<'a, Self>; +} + +impl<'a, T: Api + Sized> ContextWrapperExt<'a> for T { + fn with_context(self: &'a T, context: Context) -> ContextWrapper<'a, T> { + ContextWrapper::<T>::new(self, context) + } +} + +impl<'a, T: Api> ApiNoContext for ContextWrapper<'a, T> { + fn container_id_get( + &self, + id: String, + ) -> Box<Future<Item = ContainerIdGetResponse, Error = ApiError>> { + self.api().container_id_get(id, &self.context()) + } + + fn container_lookup_get( + &self, + issn: String, + ) -> Box<Future<Item = ContainerLookupGetResponse, Error = ApiError>> { + self.api().container_lookup_get(issn, &self.context()) + } + + fn container_post( + &self, + body: Option<models::ContainerEntity>, + ) -> Box<Future<Item = ContainerPostResponse, Error = ApiError>> { + self.api().container_post(body, &self.context()) + } + + fn creator_id_get( + &self, + id: String, + ) -> Box<Future<Item = CreatorIdGetResponse, Error = ApiError>> { + self.api().creator_id_get(id, &self.context()) + } + + fn creator_lookup_get( + &self, + orcid: String, + ) -> Box<Future<Item = CreatorLookupGetResponse, Error = ApiError>> { + self.api().creator_lookup_get(orcid, &self.context()) + } + + fn creator_post( + &self, + body: Option<models::CreatorEntity>, + ) -> Box<Future<Item = CreatorPostResponse, Error = ApiError>> { + self.api().creator_post(body, &self.context()) + } + + fn editgroup_id_accept_post( + &self, + id: i32, + ) -> Box<Future<Item = EditgroupIdAcceptPostResponse, Error = ApiError>> { + self.api().editgroup_id_accept_post(id, &self.context()) + } + + fn editgroup_id_get( + &self, + id: i32, + ) -> Box<Future<Item = EditgroupIdGetResponse, Error = ApiError>> { + self.api().editgroup_id_get(id, &self.context()) + } + + fn editgroup_post(&self) -> Box<Future<Item = EditgroupPostResponse, Error = ApiError>> { + self.api().editgroup_post(&self.context()) + } + + fn editor_username_changelog_get( + &self, + username: String, + ) -> Box<Future<Item = EditorUsernameChangelogGetResponse, Error = ApiError>> { + self.api() + .editor_username_changelog_get(username, &self.context()) + } + + fn editor_username_get( + &self, + username: String, + ) -> Box<Future<Item = EditorUsernameGetResponse, Error = ApiError>> { + self.api().editor_username_get(username, &self.context()) + } + + fn file_id_get(&self, id: String) -> Box<Future<Item = FileIdGetResponse, Error = ApiError>> { + self.api().file_id_get(id, &self.context()) + } + + fn file_lookup_get( + &self, + sha1: String, + ) -> Box<Future<Item = FileLookupGetResponse, Error = ApiError>> { + self.api().file_lookup_get(sha1, &self.context()) + } + + fn file_post( + &self, + body: Option<models::FileEntity>, + ) -> Box<Future<Item = FilePostResponse, Error = ApiError>> { + self.api().file_post(body, &self.context()) + } + + fn release_id_get( + &self, + id: String, + ) -> Box<Future<Item = ReleaseIdGetResponse, Error = ApiError>> { + self.api().release_id_get(id, &self.context()) + } + + fn release_lookup_get( + &self, + doi: String, + ) -> Box<Future<Item = ReleaseLookupGetResponse, Error = ApiError>> { + self.api().release_lookup_get(doi, &self.context()) + } + + fn release_post( + &self, + body: Option<models::ReleaseEntity>, + ) -> Box<Future<Item = ReleasePostResponse, Error = ApiError>> { + self.api().release_post(body, &self.context()) + } + + fn work_id_get(&self, id: String) -> Box<Future<Item = WorkIdGetResponse, Error = ApiError>> { + self.api().work_id_get(id, &self.context()) + } + + fn work_post( + &self, + body: Option<models::WorkEntity>, + ) -> Box<Future<Item = WorkPostResponse, Error = ApiError>> { + self.api().work_post(body, &self.context()) + } +} + +#[cfg(feature = "client")] +pub mod client; + +// Re-export Client as a top-level name +#[cfg(feature = "client")] +pub use self::client::Client; + +#[cfg(feature = "server")] +pub mod server; + +// Re-export router() as a top-level name +#[cfg(feature = "server")] +pub use self::server::Service; + +pub mod models; diff --git a/rust/fatcat-api/src/mimetypes.rs b/rust/fatcat-api/src/mimetypes.rs new file mode 100644 index 00000000..dfbff890 --- /dev/null +++ b/rust/fatcat-api/src/mimetypes.rs @@ -0,0 +1,313 @@ +/// mime types for requests and responses + +pub mod responses { + use hyper::mime::*; + + // The macro is called per-operation to beat the recursion limit + /// Create Mime objects for the response content types for ContainerIdGet + lazy_static! { + pub static ref CONTAINER_ID_GET_FETCH_A_SINGLE_CONTAINER_BY_ID: Mime = + "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for ContainerIdGet + lazy_static! { + pub static ref CONTAINER_ID_GET_BAD_REQUEST: Mime = "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for ContainerIdGet + lazy_static! { + pub static ref CONTAINER_ID_GET_GENERIC_ERROR_RESPONSE: Mime = + "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for ContainerLookupGet + lazy_static! { + pub static ref CONTAINER_LOOKUP_GET_FIND_A_SINGLE_CONTAINER_BY_EXTERNAL_IDENTIFER: Mime = + "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for ContainerLookupGet + lazy_static! { + pub static ref CONTAINER_LOOKUP_GET_BAD_REQUEST: Mime = "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for ContainerLookupGet + lazy_static! { + pub static ref CONTAINER_LOOKUP_GET_NO_SUCH_CONTAINER: Mime = + "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for ContainerLookupGet + lazy_static! { + pub static ref CONTAINER_LOOKUP_GET_GENERIC_ERROR_RESPONSE: Mime = + "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for ContainerPost + lazy_static! { + pub static ref CONTAINER_POST_CREATED: Mime = "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for ContainerPost + lazy_static! { + pub static ref CONTAINER_POST_BAD_REQUEST: Mime = "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for ContainerPost + lazy_static! { + pub static ref CONTAINER_POST_GENERIC_ERROR_RESPONSE: Mime = + "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for CreatorIdGet + lazy_static! { + pub static ref CREATOR_ID_GET_FETCH_A_SINGLE_CREATOR_BY_ID: Mime = + "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for CreatorIdGet + lazy_static! { + pub static ref CREATOR_ID_GET_BAD_REQUEST: Mime = "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for CreatorIdGet + lazy_static! { + pub static ref CREATOR_ID_GET_GENERIC_ERROR_RESPONSE: Mime = + "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for CreatorLookupGet + lazy_static! { + pub static ref CREATOR_LOOKUP_GET_FIND_A_SINGLE_CREATOR_BY_EXTERNAL_IDENTIFER: Mime = + "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for CreatorLookupGet + lazy_static! { + pub static ref CREATOR_LOOKUP_GET_BAD_REQUEST: Mime = "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for CreatorLookupGet + lazy_static! { + pub static ref CREATOR_LOOKUP_GET_NO_SUCH_CREATOR: Mime = "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for CreatorLookupGet + lazy_static! { + pub static ref CREATOR_LOOKUP_GET_GENERIC_ERROR_RESPONSE: Mime = + "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for CreatorPost + lazy_static! { + pub static ref CREATOR_POST_CREATED: Mime = "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for CreatorPost + lazy_static! { + pub static ref CREATOR_POST_BAD_REQUEST: Mime = "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for CreatorPost + lazy_static! { + pub static ref CREATOR_POST_GENERIC_ERROR_RESPONSE: Mime = "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for EditgroupIdAcceptPost + lazy_static! { + pub static ref EDITGROUP_ID_ACCEPT_POST_MERGED_EDITGROUP_SUCCESSFULLY_: Mime = + "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for EditgroupIdAcceptPost + lazy_static! { + pub static ref EDITGROUP_ID_ACCEPT_POST_EDITGROUP_IS_IN_AN_UNMERGABLE_STATE: Mime = + "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for EditgroupIdAcceptPost + lazy_static! { + pub static ref EDITGROUP_ID_ACCEPT_POST_NO_SUCH_EDITGROUP: Mime = + "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for EditgroupIdAcceptPost + lazy_static! { + pub static ref EDITGROUP_ID_ACCEPT_POST_GENERIC_ERROR_RESPONSE: Mime = + "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for EditgroupIdGet + lazy_static! { + pub static ref EDITGROUP_ID_GET_FETCH_EDITGROUP_BY_IDENTIFIER: Mime = + "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for EditgroupIdGet + lazy_static! { + pub static ref EDITGROUP_ID_GET_NO_SUCH_EDITGROUP: Mime = "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for EditgroupIdGet + lazy_static! { + pub static ref EDITGROUP_ID_GET_GENERIC_ERROR_RESPONSE: Mime = + "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for EditgroupPost + lazy_static! { + pub static ref EDITGROUP_POST_SUCCESSFULLY_CREATED: Mime = "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for EditgroupPost + lazy_static! { + pub static ref EDITGROUP_POST_INVALID_REQUEST_PARAMETERS: Mime = + "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for EditgroupPost + lazy_static! { + pub static ref EDITGROUP_POST_GENERIC_ERROR_RESPONSE: Mime = + "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for EditorUsernameChangelogGet + lazy_static! { + pub static ref EDITOR_USERNAME_CHANGELOG_GET_FIND_CHANGES_: Mime = + "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for EditorUsernameChangelogGet + lazy_static! { + pub static ref EDITOR_USERNAME_CHANGELOG_GET_USERNAME_NOT_FOUND: Mime = + "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for EditorUsernameChangelogGet + lazy_static! { + pub static ref EDITOR_USERNAME_CHANGELOG_GET_GENERIC_ERROR_RESPONSE: Mime = + "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for EditorUsernameGet + lazy_static! { + pub static ref EDITOR_USERNAME_GET_FETCH_GENERIC_INFORMATION_ABOUT_AN_EDITOR: Mime = + "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for EditorUsernameGet + lazy_static! { + pub static ref EDITOR_USERNAME_GET_USERNAME_NOT_FOUND: Mime = + "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for EditorUsernameGet + lazy_static! { + pub static ref EDITOR_USERNAME_GET_GENERIC_ERROR_RESPONSE: Mime = + "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for FileIdGet + lazy_static! { + pub static ref FILE_ID_GET_FETCH_A_SINGLE_FILE_BY_ID: Mime = + "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for FileIdGet + lazy_static! { + pub static ref FILE_ID_GET_BAD_REQUEST: Mime = "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for FileIdGet + lazy_static! { + pub static ref FILE_ID_GET_GENERIC_ERROR_RESPONSE: Mime = "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for FileLookupGet + lazy_static! { + pub static ref FILE_LOOKUP_GET_FIND_A_SINGLE_FILE_BY_EXTERNAL_IDENTIFER: Mime = + "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for FileLookupGet + lazy_static! { + pub static ref FILE_LOOKUP_GET_BAD_REQUEST: Mime = "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for FileLookupGet + lazy_static! { + pub static ref FILE_LOOKUP_GET_NO_SUCH_FILE: Mime = "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for FileLookupGet + lazy_static! { + pub static ref FILE_LOOKUP_GET_GENERIC_ERROR_RESPONSE: Mime = + "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for FilePost + lazy_static! { + pub static ref FILE_POST_CREATED: Mime = "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for FilePost + lazy_static! { + pub static ref FILE_POST_BAD_REQUEST: Mime = "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for FilePost + lazy_static! { + pub static ref FILE_POST_GENERIC_ERROR_RESPONSE: Mime = "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for ReleaseIdGet + lazy_static! { + pub static ref RELEASE_ID_GET_FETCH_A_SINGLE_RELEASE_BY_ID: Mime = + "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for ReleaseIdGet + lazy_static! { + pub static ref RELEASE_ID_GET_BAD_REQUEST: Mime = "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for ReleaseIdGet + lazy_static! { + pub static ref RELEASE_ID_GET_GENERIC_ERROR_RESPONSE: Mime = + "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for ReleaseLookupGet + lazy_static! { + pub static ref RELEASE_LOOKUP_GET_FIND_A_SINGLE_RELEASE_BY_EXTERNAL_IDENTIFER: Mime = + "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for ReleaseLookupGet + lazy_static! { + pub static ref RELEASE_LOOKUP_GET_BAD_REQUEST: Mime = "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for ReleaseLookupGet + lazy_static! { + pub static ref RELEASE_LOOKUP_GET_NO_SUCH_RELEASE: Mime = "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for ReleaseLookupGet + lazy_static! { + pub static ref RELEASE_LOOKUP_GET_GENERIC_ERROR_RESPONSE: Mime = + "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for ReleasePost + lazy_static! { + pub static ref RELEASE_POST_CREATED: Mime = "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for ReleasePost + lazy_static! { + pub static ref RELEASE_POST_BAD_REQUEST: Mime = "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for ReleasePost + lazy_static! { + pub static ref RELEASE_POST_GENERIC_ERROR_RESPONSE: Mime = "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for WorkIdGet + lazy_static! { + pub static ref WORK_ID_GET_FETCH_A_SINGLE_WORK_BY_ID: Mime = + "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for WorkIdGet + lazy_static! { + pub static ref WORK_ID_GET_BAD_REQUEST: Mime = "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for WorkIdGet + lazy_static! { + pub static ref WORK_ID_GET_GENERIC_ERROR_RESPONSE: Mime = "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for WorkPost + lazy_static! { + pub static ref WORK_POST_CREATED: Mime = "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for WorkPost + lazy_static! { + pub static ref WORK_POST_BAD_REQUEST: Mime = "application/json".parse().unwrap(); + } + /// Create Mime objects for the response content types for WorkPost + lazy_static! { + pub static ref WORK_POST_GENERIC_ERROR_RESPONSE: Mime = "application/json".parse().unwrap(); + } + +} + +pub mod requests { + use hyper::mime::*; + /// Create Mime objects for the request content types for ContainerPost + lazy_static! { + pub static ref CONTAINER_POST: Mime = "application/json".parse().unwrap(); + } + /// Create Mime objects for the request content types for CreatorPost + lazy_static! { + pub static ref CREATOR_POST: Mime = "application/json".parse().unwrap(); + } + /// Create Mime objects for the request content types for FilePost + lazy_static! { + pub static ref FILE_POST: Mime = "application/json".parse().unwrap(); + } + /// Create Mime objects for the request content types for ReleasePost + lazy_static! { + pub static ref RELEASE_POST: Mime = "application/json".parse().unwrap(); + } + /// Create Mime objects for the request content types for WorkPost + lazy_static! { + pub static ref WORK_POST: Mime = "application/json".parse().unwrap(); + } + +} diff --git a/rust/fatcat-api/src/models.rs b/rust/fatcat-api/src/models.rs new file mode 100644 index 00000000..3d104b78 --- /dev/null +++ b/rust/fatcat-api/src/models.rs @@ -0,0 +1,392 @@ +#![allow(unused_imports, unused_qualifications, unused_extern_crates)] +extern crate chrono; +extern crate uuid; + +use serde::ser::Serializer; + +use models; +use std::collections::HashMap; +use swagger; + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct Changelogentry { + #[serde(rename = "index")] + pub index: isize, + + #[serde(rename = "editgroup_id")] + #[serde(skip_serializing_if = "Option::is_none")] + pub editgroup_id: Option<isize>, + + #[serde(rename = "timestamp")] + #[serde(skip_serializing_if = "Option::is_none")] + pub timestamp: Option<chrono::DateTime<chrono::Utc>>, +} + +impl Changelogentry { + pub fn new(index: isize) -> Changelogentry { + Changelogentry { + index: index, + editgroup_id: None, + timestamp: None, + } + } +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct ContainerEntity { + #[serde(rename = "issn")] + #[serde(skip_serializing_if = "Option::is_none")] + pub issn: Option<String>, + + #[serde(rename = "publisher")] + #[serde(skip_serializing_if = "Option::is_none")] + pub publisher: Option<String>, + + #[serde(rename = "parent")] + #[serde(skip_serializing_if = "Option::is_none")] + pub parent: Option<String>, + + #[serde(rename = "name")] + #[serde(skip_serializing_if = "Option::is_none")] + pub name: Option<String>, + + // Note: inline enums are not fully supported by swagger-codegen + #[serde(rename = "state")] + #[serde(skip_serializing_if = "Option::is_none")] + pub state: Option<String>, + + #[serde(rename = "ident")] + #[serde(skip_serializing_if = "Option::is_none")] + pub ident: Option<String>, + + #[serde(rename = "revision")] + #[serde(skip_serializing_if = "Option::is_none")] + pub revision: Option<isize>, + + #[serde(rename = "redirect")] + #[serde(skip_serializing_if = "Option::is_none")] + pub redirect: Option<String>, + + #[serde(rename = "editgroup")] + #[serde(skip_serializing_if = "Option::is_none")] + pub editgroup: Option<isize>, +} + +impl ContainerEntity { + pub fn new() -> ContainerEntity { + ContainerEntity { + issn: None, + publisher: None, + parent: None, + name: None, + state: None, + ident: None, + revision: None, + redirect: None, + editgroup: None, + } + } +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct CreatorEntity { + #[serde(rename = "orcid")] + #[serde(skip_serializing_if = "Option::is_none")] + pub orcid: Option<String>, + + #[serde(rename = "name")] + #[serde(skip_serializing_if = "Option::is_none")] + pub name: Option<String>, + + #[serde(rename = "editgroup")] + #[serde(skip_serializing_if = "Option::is_none")] + pub editgroup: Option<isize>, + + #[serde(rename = "redirect")] + #[serde(skip_serializing_if = "Option::is_none")] + pub redirect: Option<String>, + + #[serde(rename = "revision")] + #[serde(skip_serializing_if = "Option::is_none")] + pub revision: Option<isize>, + + #[serde(rename = "ident")] + #[serde(skip_serializing_if = "Option::is_none")] + pub ident: Option<String>, + + // Note: inline enums are not fully supported by swagger-codegen + #[serde(rename = "state")] + #[serde(skip_serializing_if = "Option::is_none")] + pub state: Option<String>, +} + +impl CreatorEntity { + pub fn new() -> CreatorEntity { + CreatorEntity { + orcid: None, + name: None, + editgroup: None, + redirect: None, + revision: None, + ident: None, + state: None, + } + } +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct Editgroup { + #[serde(rename = "id")] + pub id: isize, + + #[serde(rename = "editor_id")] + pub editor_id: isize, +} + +impl Editgroup { + pub fn new(id: isize, editor_id: isize) -> Editgroup { + Editgroup { + id: id, + editor_id: editor_id, + } + } +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct Editor { + #[serde(rename = "username")] + pub username: String, +} + +impl Editor { + pub fn new(username: String) -> Editor { + Editor { username: username } + } +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct EntityEdit { + #[serde(rename = "editgroup_id")] + #[serde(skip_serializing_if = "Option::is_none")] + pub editgroup_id: Option<isize>, + + #[serde(rename = "revision")] + #[serde(skip_serializing_if = "Option::is_none")] + pub revision: Option<isize>, + + #[serde(rename = "ident")] + #[serde(skip_serializing_if = "Option::is_none")] + pub ident: Option<String>, + + #[serde(rename = "id")] + #[serde(skip_serializing_if = "Option::is_none")] + pub id: Option<isize>, +} + +impl EntityEdit { + pub fn new() -> EntityEdit { + EntityEdit { + editgroup_id: None, + revision: None, + ident: None, + id: None, + } + } +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct Error { + #[serde(rename = "message")] + pub message: String, +} + +impl Error { + pub fn new(message: String) -> Error { + Error { message: message } + } +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct FileEntity { + #[serde(rename = "url")] + #[serde(skip_serializing_if = "Option::is_none")] + pub url: Option<String>, + + #[serde(rename = "sha1")] + #[serde(skip_serializing_if = "Option::is_none")] + pub sha1: Option<String>, + + #[serde(rename = "size")] + #[serde(skip_serializing_if = "Option::is_none")] + pub size: Option<isize>, + + #[serde(rename = "editgroup")] + #[serde(skip_serializing_if = "Option::is_none")] + pub editgroup: Option<isize>, + + #[serde(rename = "redirect")] + #[serde(skip_serializing_if = "Option::is_none")] + pub redirect: Option<String>, + + #[serde(rename = "revision")] + #[serde(skip_serializing_if = "Option::is_none")] + pub revision: Option<isize>, + + #[serde(rename = "ident")] + #[serde(skip_serializing_if = "Option::is_none")] + pub ident: Option<String>, + + // Note: inline enums are not fully supported by swagger-codegen + #[serde(rename = "state")] + #[serde(skip_serializing_if = "Option::is_none")] + pub state: Option<String>, +} + +impl FileEntity { + pub fn new() -> FileEntity { + FileEntity { + url: None, + sha1: None, + size: None, + editgroup: None, + redirect: None, + revision: None, + ident: None, + state: None, + } + } +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct ReleaseEntity { + #[serde(rename = "issue")] + #[serde(skip_serializing_if = "Option::is_none")] + pub issue: Option<String>, + + #[serde(rename = "pages")] + #[serde(skip_serializing_if = "Option::is_none")] + pub pages: Option<String>, + + #[serde(rename = "volume")] + #[serde(skip_serializing_if = "Option::is_none")] + pub volume: Option<String>, + + #[serde(rename = "doi")] + #[serde(skip_serializing_if = "Option::is_none")] + pub doi: Option<String>, + + #[serde(rename = "release_type")] + #[serde(skip_serializing_if = "Option::is_none")] + pub release_type: Option<String>, + + #[serde(rename = "license")] + #[serde(skip_serializing_if = "Option::is_none")] + pub license: Option<String>, + + #[serde(rename = "container")] + #[serde(skip_serializing_if = "Option::is_none")] + pub container: Option<String>, + + #[serde(rename = "work")] + #[serde(skip_serializing_if = "Option::is_none")] + pub work: Option<String>, + + // Note: inline enums are not fully supported by swagger-codegen + #[serde(rename = "state")] + #[serde(skip_serializing_if = "Option::is_none")] + pub state: Option<String>, + + #[serde(rename = "ident")] + #[serde(skip_serializing_if = "Option::is_none")] + pub ident: Option<String>, + + #[serde(rename = "revision")] + #[serde(skip_serializing_if = "Option::is_none")] + pub revision: Option<isize>, + + #[serde(rename = "redirect")] + #[serde(skip_serializing_if = "Option::is_none")] + pub redirect: Option<String>, + + #[serde(rename = "editgroup")] + #[serde(skip_serializing_if = "Option::is_none")] + pub editgroup: Option<isize>, +} + +impl ReleaseEntity { + pub fn new() -> ReleaseEntity { + ReleaseEntity { + issue: None, + pages: None, + volume: None, + doi: None, + release_type: None, + license: None, + container: None, + work: None, + state: None, + ident: None, + revision: None, + redirect: None, + editgroup: None, + } + } +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct Success { + #[serde(rename = "message")] + pub message: String, +} + +impl Success { + pub fn new(message: String) -> Success { + Success { message: message } + } +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct WorkEntity { + #[serde(rename = "work_type")] + #[serde(skip_serializing_if = "Option::is_none")] + pub work_type: Option<String>, + + #[serde(rename = "title")] + #[serde(skip_serializing_if = "Option::is_none")] + pub title: Option<String>, + + #[serde(rename = "editgroup")] + #[serde(skip_serializing_if = "Option::is_none")] + pub editgroup: Option<isize>, + + #[serde(rename = "redirect")] + #[serde(skip_serializing_if = "Option::is_none")] + pub redirect: Option<String>, + + #[serde(rename = "revision")] + #[serde(skip_serializing_if = "Option::is_none")] + pub revision: Option<isize>, + + #[serde(rename = "ident")] + #[serde(skip_serializing_if = "Option::is_none")] + pub ident: Option<String>, + + // Note: inline enums are not fully supported by swagger-codegen + #[serde(rename = "state")] + #[serde(skip_serializing_if = "Option::is_none")] + pub state: Option<String>, +} + +impl WorkEntity { + pub fn new() -> WorkEntity { + WorkEntity { + work_type: None, + title: None, + editgroup: None, + redirect: None, + revision: None, + ident: None, + state: None, + } + } +} diff --git a/rust/fatcat-api/src/server/auth.rs b/rust/fatcat-api/src/server/auth.rs new file mode 100644 index 00000000..48dfd7d6 --- /dev/null +++ b/rust/fatcat-api/src/server/auth.rs @@ -0,0 +1,93 @@ +use Api; +use hyper; +use hyper::{Error, Request, Response, StatusCode}; +use server::url::form_urlencoded; +use std::io; +use swagger::auth::{AuthData, Authorization, Scopes}; + +pub struct NewService<T> +where + T: hyper::server::NewService< + Request = (Request, Option<AuthData>), + Response = Response, + Error = Error, + >, +{ + inner: T, +} + +impl<T> NewService<T> +where + T: hyper::server::NewService< + Request = (Request, Option<AuthData>), + Response = Response, + Error = Error, + > + + 'static, +{ + pub fn new(inner: T) -> NewService<T> { + NewService { inner } + } +} + +impl<T> hyper::server::NewService for NewService<T> +where + T: hyper::server::NewService< + Request = (Request, Option<AuthData>), + Response = Response, + Error = Error, + > + + 'static, +{ + type Request = Request; + type Response = Response; + type Error = Error; + type Instance = Service<T::Instance>; + + fn new_service(&self) -> Result<Self::Instance, io::Error> { + self.inner.new_service().map(|s| Service::new(s)) + } +} + +/// Middleware to extract authentication data from request +pub struct Service<T> +where + T: hyper::server::Service< + Request = (Request, Option<AuthData>), + Response = Response, + Error = Error, + >, +{ + inner: T, +} + +impl<T> Service<T> +where + T: hyper::server::Service< + Request = (Request, Option<AuthData>), + Response = Response, + Error = Error, + >, +{ + pub fn new(inner: T) -> Service<T> { + Service { inner } + } +} + +impl<T> hyper::server::Service for Service<T> +where + T: hyper::server::Service< + Request = (Request, Option<AuthData>), + Response = Response, + Error = Error, + >, +{ + type Request = Request; + type Response = Response; + type Error = Error; + type Future = T::Future; + + fn call(&self, req: Self::Request) -> Self::Future { + return self.inner.call((req, None)); + } +} diff --git a/rust/fatcat-api/src/server/mod.rs b/rust/fatcat-api/src/server/mod.rs new file mode 100644 index 00000000..186f8272 --- /dev/null +++ b/rust/fatcat-api/src/server/mod.rs @@ -0,0 +1,2485 @@ +#![allow(unused_extern_crates)] +extern crate chrono; +extern crate hyper_tls; +extern crate mime; +extern crate native_tls; +extern crate openssl; +extern crate serde_ignored; +extern crate tokio_core; +extern crate uuid; + +extern crate percent_encoding; +extern crate url; + +use self::url::form_urlencoded; +use futures::{future, stream, Future, Stream}; +use hyper; +use hyper::header::{ContentType, Headers}; +use hyper::{Error, Request, Response, StatusCode}; +use mimetypes; +use std::sync::Arc; + +use serde_json; + +#[allow(unused_imports)] +use std::collections::{BTreeMap, HashMap}; +use std::io; +#[allow(unused_imports)] +use swagger; + +#[allow(unused_imports)] +use std::collections::BTreeSet; + +pub use swagger::auth::Authorization; +use swagger::auth::Scopes; +use swagger::{ApiError, Context, XSpanId}; + +#[allow(unused_imports)] +use models; +use {Api, ContainerIdGetResponse, ContainerLookupGetResponse, ContainerPostResponse, + CreatorIdGetResponse, CreatorLookupGetResponse, CreatorPostResponse, + EditgroupIdAcceptPostResponse, EditgroupIdGetResponse, EditgroupPostResponse, + EditorUsernameChangelogGetResponse, EditorUsernameGetResponse, FileIdGetResponse, + FileLookupGetResponse, FilePostResponse, ReleaseIdGetResponse, ReleaseLookupGetResponse, + ReleasePostResponse, WorkIdGetResponse, WorkPostResponse}; + +pub mod auth; + +header! { (Warning, "Warning") => [String] } + +mod paths { + extern crate regex; + + lazy_static! { + pub static ref GLOBAL_REGEX_SET: regex::RegexSet = regex::RegexSet::new(&[ + r"^/v0/container$", + r"^/v0/container/lookup$", + r"^/v0/container/(?P<id>[^/?#]*)$", + r"^/v0/creator$", + r"^/v0/creator/lookup$", + r"^/v0/creator/(?P<id>[^/?#]*)$", + r"^/v0/editgroup$", + r"^/v0/editgroup/(?P<id>[^/?#]*)$", + r"^/v0/editgroup/(?P<id>[^/?#]*)/accept$", + r"^/v0/editor/(?P<username>[^/?#]*)$", + r"^/v0/editor/(?P<username>[^/?#]*)/changelog$", + r"^/v0/file$", + r"^/v0/file/lookup$", + r"^/v0/file/(?P<id>[^/?#]*)$", + r"^/v0/release$", + r"^/v0/release/lookup$", + r"^/v0/release/(?P<id>[^/?#]*)$", + r"^/v0/work$", + r"^/v0/work/(?P<id>[^/?#]*)$" + ]).unwrap(); + } + pub static ID_CONTAINER: usize = 0; + pub static ID_CONTAINER_LOOKUP: usize = 1; + pub static ID_CONTAINER_ID: usize = 2; + lazy_static! { + pub static ref REGEX_CONTAINER_ID: regex::Regex = + regex::Regex::new(r"^/v0/container/(?P<id>[^/?#]*)$").unwrap(); + } + pub static ID_CREATOR: usize = 3; + pub static ID_CREATOR_LOOKUP: usize = 4; + pub static ID_CREATOR_ID: usize = 5; + lazy_static! { + pub static ref REGEX_CREATOR_ID: regex::Regex = + regex::Regex::new(r"^/v0/creator/(?P<id>[^/?#]*)$").unwrap(); + } + pub static ID_EDITGROUP: usize = 6; + pub static ID_EDITGROUP_ID: usize = 7; + lazy_static! { + pub static ref REGEX_EDITGROUP_ID: regex::Regex = + regex::Regex::new(r"^/v0/editgroup/(?P<id>[^/?#]*)$").unwrap(); + } + pub static ID_EDITGROUP_ID_ACCEPT: usize = 8; + lazy_static! { + pub static ref REGEX_EDITGROUP_ID_ACCEPT: regex::Regex = + regex::Regex::new(r"^/v0/editgroup/(?P<id>[^/?#]*)/accept$").unwrap(); + } + pub static ID_EDITOR_USERNAME: usize = 9; + lazy_static! { + pub static ref REGEX_EDITOR_USERNAME: regex::Regex = + regex::Regex::new(r"^/v0/editor/(?P<username>[^/?#]*)$").unwrap(); + } + pub static ID_EDITOR_USERNAME_CHANGELOG: usize = 10; + lazy_static! { + pub static ref REGEX_EDITOR_USERNAME_CHANGELOG: regex::Regex = + regex::Regex::new(r"^/v0/editor/(?P<username>[^/?#]*)/changelog$").unwrap(); + } + pub static ID_FILE: usize = 11; + pub static ID_FILE_LOOKUP: usize = 12; + pub static ID_FILE_ID: usize = 13; + lazy_static! { + pub static ref REGEX_FILE_ID: regex::Regex = + regex::Regex::new(r"^/v0/file/(?P<id>[^/?#]*)$").unwrap(); + } + pub static ID_RELEASE: usize = 14; + pub static ID_RELEASE_LOOKUP: usize = 15; + pub static ID_RELEASE_ID: usize = 16; + lazy_static! { + pub static ref REGEX_RELEASE_ID: regex::Regex = + regex::Regex::new(r"^/v0/release/(?P<id>[^/?#]*)$").unwrap(); + } + pub static ID_WORK: usize = 17; + pub static ID_WORK_ID: usize = 18; + lazy_static! { + pub static ref REGEX_WORK_ID: regex::Regex = + regex::Regex::new(r"^/v0/work/(?P<id>[^/?#]*)$").unwrap(); + } +} + +pub struct NewService<T> { + api_impl: Arc<T>, +} + +impl<T> NewService<T> +where + T: Api + Clone + 'static, +{ + pub fn new<U: Into<Arc<T>>>(api_impl: U) -> NewService<T> { + NewService { + api_impl: api_impl.into(), + } + } +} + +impl<T> hyper::server::NewService for NewService<T> +where + T: Api + Clone + 'static, +{ + type Request = (Request, Context); + type Response = Response; + type Error = Error; + type Instance = Service<T>; + + fn new_service(&self) -> Result<Self::Instance, io::Error> { + Ok(Service::new(self.api_impl.clone())) + } +} + +pub struct Service<T> { + api_impl: Arc<T>, +} + +impl<T> Service<T> +where + T: Api + Clone + 'static, +{ + pub fn new<U: Into<Arc<T>>>(api_impl: U) -> Service<T> { + Service { + api_impl: api_impl.into(), + } + } +} + +impl<T> hyper::server::Service for Service<T> +where + T: Api + Clone + 'static, +{ + type Request = (Request, Context); + type Response = Response; + type Error = Error; + type Future = Box<Future<Item = Response, Error = Error>>; + + fn call(&self, (req, mut context): Self::Request) -> Self::Future { + let api_impl = self.api_impl.clone(); + let (method, uri, _, headers, body) = req.deconstruct(); + let path = paths::GLOBAL_REGEX_SET.matches(uri.path()); + match &method { + // ContainerIdGet - GET /container/{id} + &hyper::Method::Get if path.matched(paths::ID_CONTAINER_ID) => { + if context.x_span_id.is_none() { + context.x_span_id = Some( + headers + .get::<XSpanId>() + .map(XSpanId::to_string) + .unwrap_or_else(|| self::uuid::Uuid::new_v4().to_string()), + ); + } + + // Path parameters + let path = uri.path().to_string(); + let path_params = paths::REGEX_CONTAINER_ID + .captures(&path) + .unwrap_or_else(|| { + panic!("Path {} matched RE CONTAINER_ID in set but failed match against \"{}\"", path, paths::REGEX_CONTAINER_ID.as_str()) + }); + + let param_id = match percent_encoding::percent_decode(path_params["id"].as_bytes()) + .decode_utf8() + { + Ok(param_id) => match param_id.parse::<String>() { + Ok(param_id) => param_id, + Err(e) => { + return Box::new(future::ok( + Response::new() + .with_status(StatusCode::BadRequest) + .with_body(format!("Couldn't parse path parameter id: {}", e)), + )) + } + }, + Err(_) => { + return Box::new(future::ok( + Response::new() + .with_status(StatusCode::BadRequest) + .with_body(format!( + "Couldn't percent-decode path parameter as UTF-8: {}", + &path_params["id"] + )), + )) + } + }; + + Box::new( + ({ + { + { + Box::new(api_impl.container_id_get(param_id, &context).then( + move |result| { + let mut response = Response::new(); + context.x_span_id.as_ref().map(|header| { + response.headers_mut().set(XSpanId(header.clone())) + }); + + match result { + Ok(rsp) => match rsp { + ContainerIdGetResponse::FetchASingleContainerById + + (body) + + + => { + response.set_status(StatusCode::try_from(200).unwrap()); + + response.headers_mut().set(ContentType(mimetypes::responses::CONTAINER_ID_GET_FETCH_A_SINGLE_CONTAINER_BY_ID.clone())); + + + let body = serde_json::to_string(&body).expect("impossible to fail to serialize"); + + response.set_body(body); + }, + ContainerIdGetResponse::BadRequest + + (body) + + + => { + response.set_status(StatusCode::try_from(400).unwrap()); + + response.headers_mut().set(ContentType(mimetypes::responses::CONTAINER_ID_GET_BAD_REQUEST.clone())); + + + let body = serde_json::to_string(&body).expect("impossible to fail to serialize"); + + response.set_body(body); + }, + ContainerIdGetResponse::GenericErrorResponse + + (body) + + + => { + response.set_status(StatusCode::try_from(0).unwrap()); + + response.headers_mut().set(ContentType(mimetypes::responses::CONTAINER_ID_GET_GENERIC_ERROR_RESPONSE.clone())); + + + let body = serde_json::to_string(&body).expect("impossible to fail to serialize"); + + response.set_body(body); + }, + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.set_status(StatusCode::InternalServerError); + response.set_body("An internal error occurred"); + }, + } + + future::ok(response) + }, + )) + } + } + }), + ) as Box<Future<Item = Response, Error = Error>> + } + + // ContainerLookupGet - GET /container/lookup + &hyper::Method::Get if path.matched(paths::ID_CONTAINER_LOOKUP) => { + if context.x_span_id.is_none() { + context.x_span_id = Some( + headers + .get::<XSpanId>() + .map(XSpanId::to_string) + .unwrap_or_else(|| self::uuid::Uuid::new_v4().to_string()), + ); + } + + // Query parameters (note that non-required or collection query parameters will ignore garbage values, rather than causing a 400 response) + let query_params = form_urlencoded::parse( + uri.query().unwrap_or_default().as_bytes(), + ).collect::<Vec<_>>(); + let param_issn = query_params + .iter() + .filter(|e| e.0 == "issn") + .map(|e| e.1.to_owned()) + .nth(0); + let param_issn = match param_issn { + Some(param_issn) => match param_issn.parse::<String>() { + Ok(param_issn) => param_issn, + Err(e) => { + return Box::new(future::ok( + Response::new() + .with_status(StatusCode::BadRequest) + .with_body(format!( + "Couldn't parse query parameter issn - doesn't match schema: {}", + e + )), + )) + } + }, + None => { + return Box::new(future::ok( + Response::new() + .with_status(StatusCode::BadRequest) + .with_body("Missing required query parameter issn"), + )) + } + }; + + Box::new( + ({ + { + { + Box::new(api_impl.container_lookup_get(param_issn, &context).then( + move |result| { + let mut response = Response::new(); + context.x_span_id.as_ref().map(|header| { + response.headers_mut().set(XSpanId(header.clone())) + }); + + match result { + Ok(rsp) => match rsp { + ContainerLookupGetResponse::FindASingleContainerByExternalIdentifer + + (body) + + + => { + response.set_status(StatusCode::try_from(200).unwrap()); + + response.headers_mut().set(ContentType(mimetypes::responses::CONTAINER_LOOKUP_GET_FIND_A_SINGLE_CONTAINER_BY_EXTERNAL_IDENTIFER.clone())); + + + let body = serde_json::to_string(&body).expect("impossible to fail to serialize"); + + response.set_body(body); + }, + ContainerLookupGetResponse::BadRequest + + (body) + + + => { + response.set_status(StatusCode::try_from(400).unwrap()); + + response.headers_mut().set(ContentType(mimetypes::responses::CONTAINER_LOOKUP_GET_BAD_REQUEST.clone())); + + + let body = serde_json::to_string(&body).expect("impossible to fail to serialize"); + + response.set_body(body); + }, + ContainerLookupGetResponse::NoSuchContainer + + (body) + + + => { + response.set_status(StatusCode::try_from(404).unwrap()); + + response.headers_mut().set(ContentType(mimetypes::responses::CONTAINER_LOOKUP_GET_NO_SUCH_CONTAINER.clone())); + + + let body = serde_json::to_string(&body).expect("impossible to fail to serialize"); + + response.set_body(body); + }, + ContainerLookupGetResponse::GenericErrorResponse + + (body) + + + => { + response.set_status(StatusCode::try_from(0).unwrap()); + + response.headers_mut().set(ContentType(mimetypes::responses::CONTAINER_LOOKUP_GET_GENERIC_ERROR_RESPONSE.clone())); + + + let body = serde_json::to_string(&body).expect("impossible to fail to serialize"); + + response.set_body(body); + }, + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.set_status(StatusCode::InternalServerError); + response.set_body("An internal error occurred"); + }, + } + + future::ok(response) + }, + )) + } + } + }), + ) as Box<Future<Item = Response, Error = Error>> + } + + // ContainerPost - POST /container + &hyper::Method::Post if path.matched(paths::ID_CONTAINER) => { + if context.x_span_id.is_none() { + context.x_span_id = Some( + headers + .get::<XSpanId>() + .map(XSpanId::to_string) + .unwrap_or_else(|| self::uuid::Uuid::new_v4().to_string()), + ); + } + + // 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| -> Box<Future<Item = Response, Error = Error>> { + match result { + Ok(body) => { + let mut unused_elements = Vec::new(); + let param_body: Option< + models::ContainerEntity, + > = 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_body) => param_body, + + Err(_) => None, + } + } else { + None + }; + + Box::new(api_impl.container_post(param_body, &context).then( + move |result| { + let mut response = Response::new(); + context.x_span_id.as_ref().map(|header| { + response.headers_mut().set(XSpanId(header.clone())) + }); + + if !unused_elements.is_empty() { + response.headers_mut().set(Warning(format!( + "Ignoring unknown fields in body: {:?}", + unused_elements + ))); + } + + match result { + Ok(rsp) => match rsp { + ContainerPostResponse::Created(body) => { + response.set_status( + StatusCode::try_from(201).unwrap(), + ); + + response.headers_mut().set(ContentType(mimetypes::responses::CONTAINER_POST_CREATED.clone())); + + let body = serde_json::to_string(&body) + .expect("impossible to fail to serialize"); + + response.set_body(body); + } + ContainerPostResponse::BadRequest(body) => { + response.set_status( + StatusCode::try_from(400).unwrap(), + ); + + response.headers_mut().set(ContentType(mimetypes::responses::CONTAINER_POST_BAD_REQUEST.clone())); + + let body = serde_json::to_string(&body) + .expect("impossible to fail to serialize"); + + response.set_body(body); + } + ContainerPostResponse::GenericErrorResponse( + body, + ) => { + response.set_status( + StatusCode::try_from(0).unwrap(), + ); + + response.headers_mut().set(ContentType(mimetypes::responses::CONTAINER_POST_GENERIC_ERROR_RESPONSE.clone())); + + let body = serde_json::to_string(&body) + .expect("impossible to fail to serialize"); + + response.set_body(body); + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response + .set_status(StatusCode::InternalServerError); + response.set_body("An internal error occurred"); + } + } + + future::ok(response) + }, + )) + } + Err(e) => Box::new(future::ok( + Response::new() + .with_status(StatusCode::BadRequest) + .with_body(format!("Couldn't read body parameter body: {}", e)), + )), + } + }, + )) as Box<Future<Item = Response, Error = Error>> + } + + // CreatorIdGet - GET /creator/{id} + &hyper::Method::Get if path.matched(paths::ID_CREATOR_ID) => { + if context.x_span_id.is_none() { + context.x_span_id = Some( + headers + .get::<XSpanId>() + .map(XSpanId::to_string) + .unwrap_or_else(|| self::uuid::Uuid::new_v4().to_string()), + ); + } + + // Path parameters + let path = uri.path().to_string(); + let path_params = paths::REGEX_CREATOR_ID.captures(&path).unwrap_or_else(|| { + panic!( + "Path {} matched RE CREATOR_ID in set but failed match against \"{}\"", + path, + paths::REGEX_CREATOR_ID.as_str() + ) + }); + + let param_id = match percent_encoding::percent_decode(path_params["id"].as_bytes()) + .decode_utf8() + { + Ok(param_id) => match param_id.parse::<String>() { + Ok(param_id) => param_id, + Err(e) => { + return Box::new(future::ok( + Response::new() + .with_status(StatusCode::BadRequest) + .with_body(format!("Couldn't parse path parameter id: {}", e)), + )) + } + }, + Err(_) => { + return Box::new(future::ok( + Response::new() + .with_status(StatusCode::BadRequest) + .with_body(format!( + "Couldn't percent-decode path parameter as UTF-8: {}", + &path_params["id"] + )), + )) + } + }; + + Box::new( + ({ + { + { + Box::new(api_impl.creator_id_get(param_id, &context).then( + move |result| { + let mut response = Response::new(); + context.x_span_id.as_ref().map(|header| { + response.headers_mut().set(XSpanId(header.clone())) + }); + + match result { + Ok(rsp) => match rsp { + CreatorIdGetResponse::FetchASingleCreatorById( + body, + ) => { + response.set_status( + StatusCode::try_from(200).unwrap(), + ); + + response.headers_mut().set(ContentType(mimetypes::responses::CREATOR_ID_GET_FETCH_A_SINGLE_CREATOR_BY_ID.clone())); + + let body = serde_json::to_string(&body) + .expect("impossible to fail to serialize"); + + response.set_body(body); + } + CreatorIdGetResponse::BadRequest(body) => { + response.set_status( + StatusCode::try_from(400).unwrap(), + ); + + response.headers_mut().set(ContentType(mimetypes::responses::CREATOR_ID_GET_BAD_REQUEST.clone())); + + let body = serde_json::to_string(&body) + .expect("impossible to fail to serialize"); + + response.set_body(body); + } + CreatorIdGetResponse::GenericErrorResponse( + body, + ) => { + response.set_status( + StatusCode::try_from(0).unwrap(), + ); + + response.headers_mut().set(ContentType(mimetypes::responses::CREATOR_ID_GET_GENERIC_ERROR_RESPONSE.clone())); + + let body = serde_json::to_string(&body) + .expect("impossible to fail to serialize"); + + response.set_body(body); + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response + .set_status(StatusCode::InternalServerError); + response.set_body("An internal error occurred"); + } + } + + future::ok(response) + }, + )) + } + } + }), + ) as Box<Future<Item = Response, Error = Error>> + } + + // CreatorLookupGet - GET /creator/lookup + &hyper::Method::Get if path.matched(paths::ID_CREATOR_LOOKUP) => { + if context.x_span_id.is_none() { + context.x_span_id = Some( + headers + .get::<XSpanId>() + .map(XSpanId::to_string) + .unwrap_or_else(|| self::uuid::Uuid::new_v4().to_string()), + ); + } + + // Query parameters (note that non-required or collection query parameters will ignore garbage values, rather than causing a 400 response) + let query_params = form_urlencoded::parse( + uri.query().unwrap_or_default().as_bytes(), + ).collect::<Vec<_>>(); + let param_orcid = query_params + .iter() + .filter(|e| e.0 == "orcid") + .map(|e| e.1.to_owned()) + .nth(0); + let param_orcid = match param_orcid { + Some(param_orcid) => match param_orcid.parse::<String>() { + Ok(param_orcid) => param_orcid, + Err(e) => { + return Box::new(future::ok( + Response::new() + .with_status(StatusCode::BadRequest) + .with_body(format!( + "Couldn't parse query parameter orcid - doesn't match schema: {}", + e + )), + )) + } + }, + None => { + return Box::new(future::ok( + Response::new() + .with_status(StatusCode::BadRequest) + .with_body("Missing required query parameter orcid"), + )) + } + }; + + Box::new( + ({ + { + { + Box::new(api_impl.creator_lookup_get(param_orcid, &context).then( + move |result| { + let mut response = Response::new(); + context.x_span_id.as_ref().map(|header| { + response.headers_mut().set(XSpanId(header.clone())) + }); + + match result { + Ok(rsp) => match rsp { + CreatorLookupGetResponse::FindASingleCreatorByExternalIdentifer + + (body) + + + => { + response.set_status(StatusCode::try_from(200).unwrap()); + + response.headers_mut().set(ContentType(mimetypes::responses::CREATOR_LOOKUP_GET_FIND_A_SINGLE_CREATOR_BY_EXTERNAL_IDENTIFER.clone())); + + + let body = serde_json::to_string(&body).expect("impossible to fail to serialize"); + + response.set_body(body); + }, + CreatorLookupGetResponse::BadRequest + + (body) + + + => { + response.set_status(StatusCode::try_from(400).unwrap()); + + response.headers_mut().set(ContentType(mimetypes::responses::CREATOR_LOOKUP_GET_BAD_REQUEST.clone())); + + + let body = serde_json::to_string(&body).expect("impossible to fail to serialize"); + + response.set_body(body); + }, + CreatorLookupGetResponse::NoSuchCreator + + (body) + + + => { + response.set_status(StatusCode::try_from(404).unwrap()); + + response.headers_mut().set(ContentType(mimetypes::responses::CREATOR_LOOKUP_GET_NO_SUCH_CREATOR.clone())); + + + let body = serde_json::to_string(&body).expect("impossible to fail to serialize"); + + response.set_body(body); + }, + CreatorLookupGetResponse::GenericErrorResponse + + (body) + + + => { + response.set_status(StatusCode::try_from(0).unwrap()); + + response.headers_mut().set(ContentType(mimetypes::responses::CREATOR_LOOKUP_GET_GENERIC_ERROR_RESPONSE.clone())); + + + let body = serde_json::to_string(&body).expect("impossible to fail to serialize"); + + response.set_body(body); + }, + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.set_status(StatusCode::InternalServerError); + response.set_body("An internal error occurred"); + }, + } + + future::ok(response) + }, + )) + } + } + }), + ) as Box<Future<Item = Response, Error = Error>> + } + + // CreatorPost - POST /creator + &hyper::Method::Post if path.matched(paths::ID_CREATOR) => { + if context.x_span_id.is_none() { + context.x_span_id = Some( + headers + .get::<XSpanId>() + .map(XSpanId::to_string) + .unwrap_or_else(|| self::uuid::Uuid::new_v4().to_string()), + ); + } + + // 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| -> Box<Future<Item = Response, Error = Error>> { + match result { + Ok(body) => { + let mut unused_elements = Vec::new(); + let param_body: Option< + models::CreatorEntity, + > = 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_body) => param_body, + + Err(_) => None, + } + } else { + None + }; + + Box::new(api_impl.creator_post(param_body, &context).then( + move |result| { + let mut response = Response::new(); + context.x_span_id.as_ref().map(|header| { + response.headers_mut().set(XSpanId(header.clone())) + }); + + if !unused_elements.is_empty() { + response.headers_mut().set(Warning(format!( + "Ignoring unknown fields in body: {:?}", + unused_elements + ))); + } + + match result { + Ok(rsp) => match rsp { + CreatorPostResponse::Created(body) => { + response.set_status( + StatusCode::try_from(201).unwrap(), + ); + + response.headers_mut().set(ContentType( + mimetypes::responses::CREATOR_POST_CREATED + .clone(), + )); + + let body = serde_json::to_string(&body) + .expect("impossible to fail to serialize"); + + response.set_body(body); + } + CreatorPostResponse::BadRequest(body) => { + response.set_status( + StatusCode::try_from(400).unwrap(), + ); + + response.headers_mut().set(ContentType(mimetypes::responses::CREATOR_POST_BAD_REQUEST.clone())); + + let body = serde_json::to_string(&body) + .expect("impossible to fail to serialize"); + + response.set_body(body); + } + CreatorPostResponse::GenericErrorResponse(body) => { + response.set_status( + StatusCode::try_from(0).unwrap(), + ); + + response.headers_mut().set(ContentType(mimetypes::responses::CREATOR_POST_GENERIC_ERROR_RESPONSE.clone())); + + let body = serde_json::to_string(&body) + .expect("impossible to fail to serialize"); + + response.set_body(body); + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response + .set_status(StatusCode::InternalServerError); + response.set_body("An internal error occurred"); + } + } + + future::ok(response) + }, + )) + } + Err(e) => Box::new(future::ok( + Response::new() + .with_status(StatusCode::BadRequest) + .with_body(format!("Couldn't read body parameter body: {}", e)), + )), + } + }, + )) as Box<Future<Item = Response, Error = Error>> + } + + // EditgroupIdAcceptPost - POST /editgroup/{id}/accept + &hyper::Method::Post if path.matched(paths::ID_EDITGROUP_ID_ACCEPT) => { + if context.x_span_id.is_none() { + context.x_span_id = Some( + headers + .get::<XSpanId>() + .map(XSpanId::to_string) + .unwrap_or_else(|| self::uuid::Uuid::new_v4().to_string()), + ); + } + + // Path parameters + let path = uri.path().to_string(); + let path_params = paths::REGEX_EDITGROUP_ID_ACCEPT + .captures(&path) + .unwrap_or_else(|| { + panic!("Path {} matched RE EDITGROUP_ID_ACCEPT in set but failed match against \"{}\"", path, paths::REGEX_EDITGROUP_ID_ACCEPT.as_str()) + }); + + let param_id = match percent_encoding::percent_decode(path_params["id"].as_bytes()) + .decode_utf8() + { + Ok(param_id) => match param_id.parse::<i32>() { + Ok(param_id) => param_id, + Err(e) => { + return Box::new(future::ok( + Response::new() + .with_status(StatusCode::BadRequest) + .with_body(format!("Couldn't parse path parameter id: {}", e)), + )) + } + }, + Err(_) => { + return Box::new(future::ok( + Response::new() + .with_status(StatusCode::BadRequest) + .with_body(format!( + "Couldn't percent-decode path parameter as UTF-8: {}", + &path_params["id"] + )), + )) + } + }; + + Box::new( + ({ + { + { + Box::new( + api_impl.editgroup_id_accept_post(param_id, &context).then( + move |result| { + let mut response = Response::new(); + context.x_span_id.as_ref().map(|header| { + response.headers_mut().set(XSpanId(header.clone())) + }); + + match result { + Ok(rsp) => match rsp { + EditgroupIdAcceptPostResponse::MergedEditgroupSuccessfully_ + + (body) + + + => { + response.set_status(StatusCode::try_from(200).unwrap()); + + response.headers_mut().set(ContentType(mimetypes::responses::EDITGROUP_ID_ACCEPT_POST_MERGED_EDITGROUP_SUCCESSFULLY_.clone())); + + + let body = serde_json::to_string(&body).expect("impossible to fail to serialize"); + + response.set_body(body); + }, + EditgroupIdAcceptPostResponse::EditgroupIsInAnUnmergableState + + (body) + + + => { + response.set_status(StatusCode::try_from(400).unwrap()); + + response.headers_mut().set(ContentType(mimetypes::responses::EDITGROUP_ID_ACCEPT_POST_EDITGROUP_IS_IN_AN_UNMERGABLE_STATE.clone())); + + + let body = serde_json::to_string(&body).expect("impossible to fail to serialize"); + + response.set_body(body); + }, + EditgroupIdAcceptPostResponse::NoSuchEditgroup + + (body) + + + => { + response.set_status(StatusCode::try_from(404).unwrap()); + + response.headers_mut().set(ContentType(mimetypes::responses::EDITGROUP_ID_ACCEPT_POST_NO_SUCH_EDITGROUP.clone())); + + + let body = serde_json::to_string(&body).expect("impossible to fail to serialize"); + + response.set_body(body); + }, + EditgroupIdAcceptPostResponse::GenericErrorResponse + + (body) + + + => { + response.set_status(StatusCode::try_from(0).unwrap()); + + response.headers_mut().set(ContentType(mimetypes::responses::EDITGROUP_ID_ACCEPT_POST_GENERIC_ERROR_RESPONSE.clone())); + + + let body = serde_json::to_string(&body).expect("impossible to fail to serialize"); + + response.set_body(body); + }, + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.set_status(StatusCode::InternalServerError); + response.set_body("An internal error occurred"); + }, + } + + future::ok(response) + }, + ), + ) + } + } + }), + ) as Box<Future<Item = Response, Error = Error>> + } + + // EditgroupIdGet - GET /editgroup/{id} + &hyper::Method::Get if path.matched(paths::ID_EDITGROUP_ID) => { + if context.x_span_id.is_none() { + context.x_span_id = Some( + headers + .get::<XSpanId>() + .map(XSpanId::to_string) + .unwrap_or_else(|| self::uuid::Uuid::new_v4().to_string()), + ); + } + + // Path parameters + let path = uri.path().to_string(); + let path_params = paths::REGEX_EDITGROUP_ID + .captures(&path) + .unwrap_or_else(|| { + panic!("Path {} matched RE EDITGROUP_ID in set but failed match against \"{}\"", path, paths::REGEX_EDITGROUP_ID.as_str()) + }); + + let param_id = match percent_encoding::percent_decode(path_params["id"].as_bytes()) + .decode_utf8() + { + Ok(param_id) => match param_id.parse::<i32>() { + Ok(param_id) => param_id, + Err(e) => { + return Box::new(future::ok( + Response::new() + .with_status(StatusCode::BadRequest) + .with_body(format!("Couldn't parse path parameter id: {}", e)), + )) + } + }, + Err(_) => { + return Box::new(future::ok( + Response::new() + .with_status(StatusCode::BadRequest) + .with_body(format!( + "Couldn't percent-decode path parameter as UTF-8: {}", + &path_params["id"] + )), + )) + } + }; + + Box::new( + ({ + { + { + Box::new(api_impl.editgroup_id_get(param_id, &context).then( + move |result| { + let mut response = Response::new(); + context.x_span_id.as_ref().map(|header| { + response.headers_mut().set(XSpanId(header.clone())) + }); + + match result { + Ok(rsp) => match rsp { + EditgroupIdGetResponse::FetchEditgroupByIdentifier + + (body) + + + => { + response.set_status(StatusCode::try_from(200).unwrap()); + + response.headers_mut().set(ContentType(mimetypes::responses::EDITGROUP_ID_GET_FETCH_EDITGROUP_BY_IDENTIFIER.clone())); + + + let body = serde_json::to_string(&body).expect("impossible to fail to serialize"); + + response.set_body(body); + }, + EditgroupIdGetResponse::NoSuchEditgroup + + (body) + + + => { + response.set_status(StatusCode::try_from(404).unwrap()); + + response.headers_mut().set(ContentType(mimetypes::responses::EDITGROUP_ID_GET_NO_SUCH_EDITGROUP.clone())); + + + let body = serde_json::to_string(&body).expect("impossible to fail to serialize"); + + response.set_body(body); + }, + EditgroupIdGetResponse::GenericErrorResponse + + (body) + + + => { + response.set_status(StatusCode::try_from(0).unwrap()); + + response.headers_mut().set(ContentType(mimetypes::responses::EDITGROUP_ID_GET_GENERIC_ERROR_RESPONSE.clone())); + + + let body = serde_json::to_string(&body).expect("impossible to fail to serialize"); + + response.set_body(body); + }, + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.set_status(StatusCode::InternalServerError); + response.set_body("An internal error occurred"); + }, + } + + future::ok(response) + }, + )) + } + } + }), + ) as Box<Future<Item = Response, Error = Error>> + } + + // EditgroupPost - POST /editgroup + &hyper::Method::Post if path.matched(paths::ID_EDITGROUP) => { + if context.x_span_id.is_none() { + context.x_span_id = Some( + headers + .get::<XSpanId>() + .map(XSpanId::to_string) + .unwrap_or_else(|| self::uuid::Uuid::new_v4().to_string()), + ); + } + + Box::new( + ({ + { + { + Box::new(api_impl.editgroup_post(&context).then(move |result| { + let mut response = Response::new(); + context.x_span_id.as_ref().map(|header| { + response.headers_mut().set(XSpanId(header.clone())) + }); + + match result { + Ok(rsp) => match rsp { + EditgroupPostResponse::SuccessfullyCreated(body) => { + response + .set_status(StatusCode::try_from(201).unwrap()); + + response.headers_mut().set(ContentType(mimetypes::responses::EDITGROUP_POST_SUCCESSFULLY_CREATED.clone())); + + let body = serde_json::to_string(&body) + .expect("impossible to fail to serialize"); + + response.set_body(body); + } + EditgroupPostResponse::InvalidRequestParameters( + body, + ) => { + response + .set_status(StatusCode::try_from(400).unwrap()); + + response.headers_mut().set(ContentType(mimetypes::responses::EDITGROUP_POST_INVALID_REQUEST_PARAMETERS.clone())); + + let body = serde_json::to_string(&body) + .expect("impossible to fail to serialize"); + + response.set_body(body); + } + EditgroupPostResponse::GenericErrorResponse(body) => { + response + .set_status(StatusCode::try_from(0).unwrap()); + + response.headers_mut().set(ContentType(mimetypes::responses::EDITGROUP_POST_GENERIC_ERROR_RESPONSE.clone())); + + let body = serde_json::to_string(&body) + .expect("impossible to fail to serialize"); + + response.set_body(body); + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.set_status(StatusCode::InternalServerError); + response.set_body("An internal error occurred"); + } + } + + future::ok(response) + })) + } + } + }), + ) as Box<Future<Item = Response, Error = Error>> + } + + // EditorUsernameChangelogGet - GET /editor/{username}/changelog + &hyper::Method::Get if path.matched(paths::ID_EDITOR_USERNAME_CHANGELOG) => { + if context.x_span_id.is_none() { + context.x_span_id = Some( + headers + .get::<XSpanId>() + .map(XSpanId::to_string) + .unwrap_or_else(|| self::uuid::Uuid::new_v4().to_string()), + ); + } + + // Path parameters + let path = uri.path().to_string(); + let path_params = paths::REGEX_EDITOR_USERNAME_CHANGELOG + .captures(&path) + .unwrap_or_else(|| { + panic!("Path {} matched RE EDITOR_USERNAME_CHANGELOG in set but failed match against \"{}\"", path, paths::REGEX_EDITOR_USERNAME_CHANGELOG.as_str()) + }); + + let param_username = match percent_encoding::percent_decode( + path_params["username"].as_bytes(), + ).decode_utf8() + { + Ok(param_username) => match param_username.parse::<String>() { + Ok(param_username) => param_username, + Err(e) => { + return Box::new(future::ok( + Response::new() + .with_status(StatusCode::BadRequest) + .with_body(format!( + "Couldn't parse path parameter username: {}", + e + )), + )) + } + }, + Err(_) => { + return Box::new(future::ok( + Response::new() + .with_status(StatusCode::BadRequest) + .with_body(format!( + "Couldn't percent-decode path parameter as UTF-8: {}", + &path_params["username"] + )), + )) + } + }; + + Box::new( + ({ + { + { + Box::new( + api_impl + .editor_username_changelog_get(param_username, &context) + .then(move |result| { + let mut response = Response::new(); + context.x_span_id.as_ref().map(|header| { + response.headers_mut().set(XSpanId(header.clone())) + }); + + match result { + Ok(rsp) => match rsp { + EditorUsernameChangelogGetResponse::FindChanges_ + + (body) + + + => { + response.set_status(StatusCode::try_from(200).unwrap()); + + response.headers_mut().set(ContentType(mimetypes::responses::EDITOR_USERNAME_CHANGELOG_GET_FIND_CHANGES_.clone())); + + + let body = serde_json::to_string(&body).expect("impossible to fail to serialize"); + + response.set_body(body); + }, + EditorUsernameChangelogGetResponse::UsernameNotFound + + (body) + + + => { + response.set_status(StatusCode::try_from(404).unwrap()); + + response.headers_mut().set(ContentType(mimetypes::responses::EDITOR_USERNAME_CHANGELOG_GET_USERNAME_NOT_FOUND.clone())); + + + let body = serde_json::to_string(&body).expect("impossible to fail to serialize"); + + response.set_body(body); + }, + EditorUsernameChangelogGetResponse::GenericErrorResponse + + (body) + + + => { + response.set_status(StatusCode::try_from(0).unwrap()); + + response.headers_mut().set(ContentType(mimetypes::responses::EDITOR_USERNAME_CHANGELOG_GET_GENERIC_ERROR_RESPONSE.clone())); + + + let body = serde_json::to_string(&body).expect("impossible to fail to serialize"); + + response.set_body(body); + }, + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.set_status(StatusCode::InternalServerError); + response.set_body("An internal error occurred"); + }, + } + + future::ok(response) + }), + ) + } + } + }), + ) as Box<Future<Item = Response, Error = Error>> + } + + // EditorUsernameGet - GET /editor/{username} + &hyper::Method::Get if path.matched(paths::ID_EDITOR_USERNAME) => { + if context.x_span_id.is_none() { + context.x_span_id = Some( + headers + .get::<XSpanId>() + .map(XSpanId::to_string) + .unwrap_or_else(|| self::uuid::Uuid::new_v4().to_string()), + ); + } + + // Path parameters + let path = uri.path().to_string(); + let path_params = paths::REGEX_EDITOR_USERNAME.captures(&path).unwrap_or_else( + || { + panic!("Path {} matched RE EDITOR_USERNAME in set but failed match against \"{}\"", path, paths::REGEX_EDITOR_USERNAME.as_str()) + }, + ); + + let param_username = match percent_encoding::percent_decode( + path_params["username"].as_bytes(), + ).decode_utf8() + { + Ok(param_username) => match param_username.parse::<String>() { + Ok(param_username) => param_username, + Err(e) => { + return Box::new(future::ok( + Response::new() + .with_status(StatusCode::BadRequest) + .with_body(format!( + "Couldn't parse path parameter username: {}", + e + )), + )) + } + }, + Err(_) => { + return Box::new(future::ok( + Response::new() + .with_status(StatusCode::BadRequest) + .with_body(format!( + "Couldn't percent-decode path parameter as UTF-8: {}", + &path_params["username"] + )), + )) + } + }; + + Box::new( + ({ + { + { + Box::new( + api_impl.editor_username_get(param_username, &context).then( + move |result| { + let mut response = Response::new(); + context.x_span_id.as_ref().map(|header| { + response.headers_mut().set(XSpanId(header.clone())) + }); + + match result { + Ok(rsp) => match rsp { + EditorUsernameGetResponse::FetchGenericInformationAboutAnEditor + + (body) + + + => { + response.set_status(StatusCode::try_from(200).unwrap()); + + response.headers_mut().set(ContentType(mimetypes::responses::EDITOR_USERNAME_GET_FETCH_GENERIC_INFORMATION_ABOUT_AN_EDITOR.clone())); + + + let body = serde_json::to_string(&body).expect("impossible to fail to serialize"); + + response.set_body(body); + }, + EditorUsernameGetResponse::UsernameNotFound + + (body) + + + => { + response.set_status(StatusCode::try_from(404).unwrap()); + + response.headers_mut().set(ContentType(mimetypes::responses::EDITOR_USERNAME_GET_USERNAME_NOT_FOUND.clone())); + + + let body = serde_json::to_string(&body).expect("impossible to fail to serialize"); + + response.set_body(body); + }, + EditorUsernameGetResponse::GenericErrorResponse + + (body) + + + => { + response.set_status(StatusCode::try_from(0).unwrap()); + + response.headers_mut().set(ContentType(mimetypes::responses::EDITOR_USERNAME_GET_GENERIC_ERROR_RESPONSE.clone())); + + + let body = serde_json::to_string(&body).expect("impossible to fail to serialize"); + + response.set_body(body); + }, + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.set_status(StatusCode::InternalServerError); + response.set_body("An internal error occurred"); + }, + } + + future::ok(response) + }, + ), + ) + } + } + }), + ) as Box<Future<Item = Response, Error = Error>> + } + + // FileIdGet - GET /file/{id} + &hyper::Method::Get if path.matched(paths::ID_FILE_ID) => { + if context.x_span_id.is_none() { + context.x_span_id = Some( + headers + .get::<XSpanId>() + .map(XSpanId::to_string) + .unwrap_or_else(|| self::uuid::Uuid::new_v4().to_string()), + ); + } + + // Path parameters + let path = uri.path().to_string(); + let path_params = paths::REGEX_FILE_ID.captures(&path).unwrap_or_else(|| { + panic!( + "Path {} matched RE FILE_ID in set but failed match against \"{}\"", + path, + paths::REGEX_FILE_ID.as_str() + ) + }); + + let param_id = match percent_encoding::percent_decode(path_params["id"].as_bytes()) + .decode_utf8() + { + Ok(param_id) => match param_id.parse::<String>() { + Ok(param_id) => param_id, + Err(e) => { + return Box::new(future::ok( + Response::new() + .with_status(StatusCode::BadRequest) + .with_body(format!("Couldn't parse path parameter id: {}", e)), + )) + } + }, + Err(_) => { + return Box::new(future::ok( + Response::new() + .with_status(StatusCode::BadRequest) + .with_body(format!( + "Couldn't percent-decode path parameter as UTF-8: {}", + &path_params["id"] + )), + )) + } + }; + + Box::new( + ({ + { + { + Box::new(api_impl.file_id_get(param_id, &context).then( + move |result| { + let mut response = Response::new(); + context.x_span_id.as_ref().map(|header| { + response.headers_mut().set(XSpanId(header.clone())) + }); + + match result { + Ok(rsp) => match rsp { + FileIdGetResponse::FetchASingleFileById(body) => { + response.set_status( + StatusCode::try_from(200).unwrap(), + ); + + response.headers_mut().set(ContentType(mimetypes::responses::FILE_ID_GET_FETCH_A_SINGLE_FILE_BY_ID.clone())); + + let body = serde_json::to_string(&body) + .expect("impossible to fail to serialize"); + + response.set_body(body); + } + FileIdGetResponse::BadRequest(body) => { + response.set_status( + StatusCode::try_from(400).unwrap(), + ); + + response.headers_mut().set(ContentType(mimetypes::responses::FILE_ID_GET_BAD_REQUEST.clone())); + + let body = serde_json::to_string(&body) + .expect("impossible to fail to serialize"); + + response.set_body(body); + } + FileIdGetResponse::GenericErrorResponse(body) => { + response.set_status( + StatusCode::try_from(0).unwrap(), + ); + + response.headers_mut().set(ContentType(mimetypes::responses::FILE_ID_GET_GENERIC_ERROR_RESPONSE.clone())); + + let body = serde_json::to_string(&body) + .expect("impossible to fail to serialize"); + + response.set_body(body); + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response + .set_status(StatusCode::InternalServerError); + response.set_body("An internal error occurred"); + } + } + + future::ok(response) + }, + )) + } + } + }), + ) as Box<Future<Item = Response, Error = Error>> + } + + // FileLookupGet - GET /file/lookup + &hyper::Method::Get if path.matched(paths::ID_FILE_LOOKUP) => { + if context.x_span_id.is_none() { + context.x_span_id = Some( + headers + .get::<XSpanId>() + .map(XSpanId::to_string) + .unwrap_or_else(|| self::uuid::Uuid::new_v4().to_string()), + ); + } + + // Query parameters (note that non-required or collection query parameters will ignore garbage values, rather than causing a 400 response) + let query_params = form_urlencoded::parse( + uri.query().unwrap_or_default().as_bytes(), + ).collect::<Vec<_>>(); + let param_sha1 = query_params + .iter() + .filter(|e| e.0 == "sha1") + .map(|e| e.1.to_owned()) + .nth(0); + let param_sha1 = match param_sha1 { + Some(param_sha1) => match param_sha1.parse::<String>() { + Ok(param_sha1) => param_sha1, + Err(e) => { + return Box::new(future::ok( + Response::new() + .with_status(StatusCode::BadRequest) + .with_body(format!( + "Couldn't parse query parameter sha1 - doesn't match schema: {}", + e + )), + )) + } + }, + None => { + return Box::new(future::ok( + Response::new() + .with_status(StatusCode::BadRequest) + .with_body("Missing required query parameter sha1"), + )) + } + }; + + Box::new( + ({ + { + { + Box::new(api_impl.file_lookup_get(param_sha1, &context).then( + move |result| { + let mut response = Response::new(); + context.x_span_id.as_ref().map(|header| { + response.headers_mut().set(XSpanId(header.clone())) + }); + + match result { + Ok(rsp) => match rsp { + FileLookupGetResponse::FindASingleFileByExternalIdentifer + + (body) + + + => { + response.set_status(StatusCode::try_from(200).unwrap()); + + response.headers_mut().set(ContentType(mimetypes::responses::FILE_LOOKUP_GET_FIND_A_SINGLE_FILE_BY_EXTERNAL_IDENTIFER.clone())); + + + let body = serde_json::to_string(&body).expect("impossible to fail to serialize"); + + response.set_body(body); + }, + FileLookupGetResponse::BadRequest + + (body) + + + => { + response.set_status(StatusCode::try_from(400).unwrap()); + + response.headers_mut().set(ContentType(mimetypes::responses::FILE_LOOKUP_GET_BAD_REQUEST.clone())); + + + let body = serde_json::to_string(&body).expect("impossible to fail to serialize"); + + response.set_body(body); + }, + FileLookupGetResponse::NoSuchFile + + (body) + + + => { + response.set_status(StatusCode::try_from(404).unwrap()); + + response.headers_mut().set(ContentType(mimetypes::responses::FILE_LOOKUP_GET_NO_SUCH_FILE.clone())); + + + let body = serde_json::to_string(&body).expect("impossible to fail to serialize"); + + response.set_body(body); + }, + FileLookupGetResponse::GenericErrorResponse + + (body) + + + => { + response.set_status(StatusCode::try_from(0).unwrap()); + + response.headers_mut().set(ContentType(mimetypes::responses::FILE_LOOKUP_GET_GENERIC_ERROR_RESPONSE.clone())); + + + let body = serde_json::to_string(&body).expect("impossible to fail to serialize"); + + response.set_body(body); + }, + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.set_status(StatusCode::InternalServerError); + response.set_body("An internal error occurred"); + }, + } + + future::ok(response) + }, + )) + } + } + }), + ) as Box<Future<Item = Response, Error = Error>> + } + + // FilePost - POST /file + &hyper::Method::Post if path.matched(paths::ID_FILE) => { + if context.x_span_id.is_none() { + context.x_span_id = Some( + headers + .get::<XSpanId>() + .map(XSpanId::to_string) + .unwrap_or_else(|| self::uuid::Uuid::new_v4().to_string()), + ); + } + + // 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| -> Box<Future<Item = Response, Error = Error>> { + match result { + Ok(body) => { + let mut unused_elements = Vec::new(); + let param_body: Option< + models::FileEntity, + > = 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_body) => param_body, + + Err(_) => None, + } + } else { + None + }; + + Box::new(api_impl.file_post(param_body, &context).then( + move |result| { + let mut response = Response::new(); + context.x_span_id.as_ref().map(|header| { + response.headers_mut().set(XSpanId(header.clone())) + }); + + if !unused_elements.is_empty() { + response.headers_mut().set(Warning(format!( + "Ignoring unknown fields in body: {:?}", + unused_elements + ))); + } + + match result { + Ok(rsp) => match rsp { + FilePostResponse::Created(body) => { + response.set_status( + StatusCode::try_from(201).unwrap(), + ); + + response.headers_mut().set(ContentType( + mimetypes::responses::FILE_POST_CREATED + .clone(), + )); + + let body = serde_json::to_string(&body) + .expect("impossible to fail to serialize"); + + response.set_body(body); + } + FilePostResponse::BadRequest(body) => { + response.set_status( + StatusCode::try_from(400).unwrap(), + ); + + response.headers_mut().set(ContentType( + mimetypes::responses::FILE_POST_BAD_REQUEST + .clone(), + )); + + let body = serde_json::to_string(&body) + .expect("impossible to fail to serialize"); + + response.set_body(body); + } + FilePostResponse::GenericErrorResponse(body) => { + response.set_status( + StatusCode::try_from(0).unwrap(), + ); + + response.headers_mut().set(ContentType(mimetypes::responses::FILE_POST_GENERIC_ERROR_RESPONSE.clone())); + + let body = serde_json::to_string(&body) + .expect("impossible to fail to serialize"); + + response.set_body(body); + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response + .set_status(StatusCode::InternalServerError); + response.set_body("An internal error occurred"); + } + } + + future::ok(response) + }, + )) + } + Err(e) => Box::new(future::ok( + Response::new() + .with_status(StatusCode::BadRequest) + .with_body(format!("Couldn't read body parameter body: {}", e)), + )), + } + }, + )) as Box<Future<Item = Response, Error = Error>> + } + + // ReleaseIdGet - GET /release/{id} + &hyper::Method::Get if path.matched(paths::ID_RELEASE_ID) => { + if context.x_span_id.is_none() { + context.x_span_id = Some( + headers + .get::<XSpanId>() + .map(XSpanId::to_string) + .unwrap_or_else(|| self::uuid::Uuid::new_v4().to_string()), + ); + } + + // Path parameters + let path = uri.path().to_string(); + let path_params = paths::REGEX_RELEASE_ID.captures(&path).unwrap_or_else(|| { + panic!( + "Path {} matched RE RELEASE_ID in set but failed match against \"{}\"", + path, + paths::REGEX_RELEASE_ID.as_str() + ) + }); + + let param_id = match percent_encoding::percent_decode(path_params["id"].as_bytes()) + .decode_utf8() + { + Ok(param_id) => match param_id.parse::<String>() { + Ok(param_id) => param_id, + Err(e) => { + return Box::new(future::ok( + Response::new() + .with_status(StatusCode::BadRequest) + .with_body(format!("Couldn't parse path parameter id: {}", e)), + )) + } + }, + Err(_) => { + return Box::new(future::ok( + Response::new() + .with_status(StatusCode::BadRequest) + .with_body(format!( + "Couldn't percent-decode path parameter as UTF-8: {}", + &path_params["id"] + )), + )) + } + }; + + Box::new( + ({ + { + { + Box::new(api_impl.release_id_get(param_id, &context).then( + move |result| { + let mut response = Response::new(); + context.x_span_id.as_ref().map(|header| { + response.headers_mut().set(XSpanId(header.clone())) + }); + + match result { + Ok(rsp) => match rsp { + ReleaseIdGetResponse::FetchASingleReleaseById( + body, + ) => { + response.set_status( + StatusCode::try_from(200).unwrap(), + ); + + response.headers_mut().set(ContentType(mimetypes::responses::RELEASE_ID_GET_FETCH_A_SINGLE_RELEASE_BY_ID.clone())); + + let body = serde_json::to_string(&body) + .expect("impossible to fail to serialize"); + + response.set_body(body); + } + ReleaseIdGetResponse::BadRequest(body) => { + response.set_status( + StatusCode::try_from(400).unwrap(), + ); + + response.headers_mut().set(ContentType(mimetypes::responses::RELEASE_ID_GET_BAD_REQUEST.clone())); + + let body = serde_json::to_string(&body) + .expect("impossible to fail to serialize"); + + response.set_body(body); + } + ReleaseIdGetResponse::GenericErrorResponse( + body, + ) => { + response.set_status( + StatusCode::try_from(0).unwrap(), + ); + + response.headers_mut().set(ContentType(mimetypes::responses::RELEASE_ID_GET_GENERIC_ERROR_RESPONSE.clone())); + + let body = serde_json::to_string(&body) + .expect("impossible to fail to serialize"); + + response.set_body(body); + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response + .set_status(StatusCode::InternalServerError); + response.set_body("An internal error occurred"); + } + } + + future::ok(response) + }, + )) + } + } + }), + ) as Box<Future<Item = Response, Error = Error>> + } + + // ReleaseLookupGet - GET /release/lookup + &hyper::Method::Get if path.matched(paths::ID_RELEASE_LOOKUP) => { + if context.x_span_id.is_none() { + context.x_span_id = Some( + headers + .get::<XSpanId>() + .map(XSpanId::to_string) + .unwrap_or_else(|| self::uuid::Uuid::new_v4().to_string()), + ); + } + + // Query parameters (note that non-required or collection query parameters will ignore garbage values, rather than causing a 400 response) + let query_params = form_urlencoded::parse( + uri.query().unwrap_or_default().as_bytes(), + ).collect::<Vec<_>>(); + let param_doi = query_params + .iter() + .filter(|e| e.0 == "doi") + .map(|e| e.1.to_owned()) + .nth(0); + let param_doi = match param_doi { + Some(param_doi) => match param_doi.parse::<String>() { + Ok(param_doi) => param_doi, + Err(e) => { + return Box::new(future::ok( + Response::new() + .with_status(StatusCode::BadRequest) + .with_body(format!( + "Couldn't parse query parameter doi - doesn't match schema: {}", + e + )), + )) + } + }, + None => { + return Box::new(future::ok( + Response::new() + .with_status(StatusCode::BadRequest) + .with_body("Missing required query parameter doi"), + )) + } + }; + + Box::new( + ({ + { + { + Box::new(api_impl.release_lookup_get(param_doi, &context).then( + move |result| { + let mut response = Response::new(); + context.x_span_id.as_ref().map(|header| { + response.headers_mut().set(XSpanId(header.clone())) + }); + + match result { + Ok(rsp) => match rsp { + ReleaseLookupGetResponse::FindASingleReleaseByExternalIdentifer + + (body) + + + => { + response.set_status(StatusCode::try_from(200).unwrap()); + + response.headers_mut().set(ContentType(mimetypes::responses::RELEASE_LOOKUP_GET_FIND_A_SINGLE_RELEASE_BY_EXTERNAL_IDENTIFER.clone())); + + + let body = serde_json::to_string(&body).expect("impossible to fail to serialize"); + + response.set_body(body); + }, + ReleaseLookupGetResponse::BadRequest + + (body) + + + => { + response.set_status(StatusCode::try_from(400).unwrap()); + + response.headers_mut().set(ContentType(mimetypes::responses::RELEASE_LOOKUP_GET_BAD_REQUEST.clone())); + + + let body = serde_json::to_string(&body).expect("impossible to fail to serialize"); + + response.set_body(body); + }, + ReleaseLookupGetResponse::NoSuchRelease + + (body) + + + => { + response.set_status(StatusCode::try_from(404).unwrap()); + + response.headers_mut().set(ContentType(mimetypes::responses::RELEASE_LOOKUP_GET_NO_SUCH_RELEASE.clone())); + + + let body = serde_json::to_string(&body).expect("impossible to fail to serialize"); + + response.set_body(body); + }, + ReleaseLookupGetResponse::GenericErrorResponse + + (body) + + + => { + response.set_status(StatusCode::try_from(0).unwrap()); + + response.headers_mut().set(ContentType(mimetypes::responses::RELEASE_LOOKUP_GET_GENERIC_ERROR_RESPONSE.clone())); + + + let body = serde_json::to_string(&body).expect("impossible to fail to serialize"); + + response.set_body(body); + }, + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.set_status(StatusCode::InternalServerError); + response.set_body("An internal error occurred"); + }, + } + + future::ok(response) + }, + )) + } + } + }), + ) as Box<Future<Item = Response, Error = Error>> + } + + // ReleasePost - POST /release + &hyper::Method::Post if path.matched(paths::ID_RELEASE) => { + if context.x_span_id.is_none() { + context.x_span_id = Some( + headers + .get::<XSpanId>() + .map(XSpanId::to_string) + .unwrap_or_else(|| self::uuid::Uuid::new_v4().to_string()), + ); + } + + // 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| -> Box<Future<Item = Response, Error = Error>> { + match result { + Ok(body) => { + let mut unused_elements = Vec::new(); + let param_body: Option< + models::ReleaseEntity, + > = 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_body) => param_body, + + Err(_) => None, + } + } else { + None + }; + + Box::new(api_impl.release_post(param_body, &context).then( + move |result| { + let mut response = Response::new(); + context.x_span_id.as_ref().map(|header| { + response.headers_mut().set(XSpanId(header.clone())) + }); + + if !unused_elements.is_empty() { + response.headers_mut().set(Warning(format!( + "Ignoring unknown fields in body: {:?}", + unused_elements + ))); + } + + match result { + Ok(rsp) => match rsp { + ReleasePostResponse::Created(body) => { + response.set_status( + StatusCode::try_from(201).unwrap(), + ); + + response.headers_mut().set(ContentType( + mimetypes::responses::RELEASE_POST_CREATED + .clone(), + )); + + let body = serde_json::to_string(&body) + .expect("impossible to fail to serialize"); + + response.set_body(body); + } + ReleasePostResponse::BadRequest(body) => { + response.set_status( + StatusCode::try_from(400).unwrap(), + ); + + response.headers_mut().set(ContentType(mimetypes::responses::RELEASE_POST_BAD_REQUEST.clone())); + + let body = serde_json::to_string(&body) + .expect("impossible to fail to serialize"); + + response.set_body(body); + } + ReleasePostResponse::GenericErrorResponse(body) => { + response.set_status( + StatusCode::try_from(0).unwrap(), + ); + + response.headers_mut().set(ContentType(mimetypes::responses::RELEASE_POST_GENERIC_ERROR_RESPONSE.clone())); + + let body = serde_json::to_string(&body) + .expect("impossible to fail to serialize"); + + response.set_body(body); + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response + .set_status(StatusCode::InternalServerError); + response.set_body("An internal error occurred"); + } + } + + future::ok(response) + }, + )) + } + Err(e) => Box::new(future::ok( + Response::new() + .with_status(StatusCode::BadRequest) + .with_body(format!("Couldn't read body parameter body: {}", e)), + )), + } + }, + )) as Box<Future<Item = Response, Error = Error>> + } + + // WorkIdGet - GET /work/{id} + &hyper::Method::Get if path.matched(paths::ID_WORK_ID) => { + if context.x_span_id.is_none() { + context.x_span_id = Some( + headers + .get::<XSpanId>() + .map(XSpanId::to_string) + .unwrap_or_else(|| self::uuid::Uuid::new_v4().to_string()), + ); + } + + // Path parameters + let path = uri.path().to_string(); + let path_params = paths::REGEX_WORK_ID.captures(&path).unwrap_or_else(|| { + panic!( + "Path {} matched RE WORK_ID in set but failed match against \"{}\"", + path, + paths::REGEX_WORK_ID.as_str() + ) + }); + + let param_id = match percent_encoding::percent_decode(path_params["id"].as_bytes()) + .decode_utf8() + { + Ok(param_id) => match param_id.parse::<String>() { + Ok(param_id) => param_id, + Err(e) => { + return Box::new(future::ok( + Response::new() + .with_status(StatusCode::BadRequest) + .with_body(format!("Couldn't parse path parameter id: {}", e)), + )) + } + }, + Err(_) => { + return Box::new(future::ok( + Response::new() + .with_status(StatusCode::BadRequest) + .with_body(format!( + "Couldn't percent-decode path parameter as UTF-8: {}", + &path_params["id"] + )), + )) + } + }; + + Box::new( + ({ + { + { + Box::new(api_impl.work_id_get(param_id, &context).then( + move |result| { + let mut response = Response::new(); + context.x_span_id.as_ref().map(|header| { + response.headers_mut().set(XSpanId(header.clone())) + }); + + match result { + Ok(rsp) => match rsp { + WorkIdGetResponse::FetchASingleWorkById(body) => { + response.set_status( + StatusCode::try_from(200).unwrap(), + ); + + response.headers_mut().set(ContentType(mimetypes::responses::WORK_ID_GET_FETCH_A_SINGLE_WORK_BY_ID.clone())); + + let body = serde_json::to_string(&body) + .expect("impossible to fail to serialize"); + + response.set_body(body); + } + WorkIdGetResponse::BadRequest(body) => { + response.set_status( + StatusCode::try_from(400).unwrap(), + ); + + response.headers_mut().set(ContentType(mimetypes::responses::WORK_ID_GET_BAD_REQUEST.clone())); + + let body = serde_json::to_string(&body) + .expect("impossible to fail to serialize"); + + response.set_body(body); + } + WorkIdGetResponse::GenericErrorResponse(body) => { + response.set_status( + StatusCode::try_from(0).unwrap(), + ); + + response.headers_mut().set(ContentType(mimetypes::responses::WORK_ID_GET_GENERIC_ERROR_RESPONSE.clone())); + + let body = serde_json::to_string(&body) + .expect("impossible to fail to serialize"); + + response.set_body(body); + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response + .set_status(StatusCode::InternalServerError); + response.set_body("An internal error occurred"); + } + } + + future::ok(response) + }, + )) + } + } + }), + ) as Box<Future<Item = Response, Error = Error>> + } + + // WorkPost - POST /work + &hyper::Method::Post if path.matched(paths::ID_WORK) => { + if context.x_span_id.is_none() { + context.x_span_id = Some( + headers + .get::<XSpanId>() + .map(XSpanId::to_string) + .unwrap_or_else(|| self::uuid::Uuid::new_v4().to_string()), + ); + } + + // 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| -> Box<Future<Item = Response, Error = Error>> { + match result { + Ok(body) => { + let mut unused_elements = Vec::new(); + let param_body: Option< + models::WorkEntity, + > = 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_body) => param_body, + + Err(_) => None, + } + } else { + None + }; + + Box::new(api_impl.work_post(param_body, &context).then( + move |result| { + let mut response = Response::new(); + context.x_span_id.as_ref().map(|header| { + response.headers_mut().set(XSpanId(header.clone())) + }); + + if !unused_elements.is_empty() { + response.headers_mut().set(Warning(format!( + "Ignoring unknown fields in body: {:?}", + unused_elements + ))); + } + + match result { + Ok(rsp) => match rsp { + WorkPostResponse::Created(body) => { + response.set_status( + StatusCode::try_from(201).unwrap(), + ); + + response.headers_mut().set(ContentType( + mimetypes::responses::WORK_POST_CREATED + .clone(), + )); + + let body = serde_json::to_string(&body) + .expect("impossible to fail to serialize"); + + response.set_body(body); + } + WorkPostResponse::BadRequest(body) => { + response.set_status( + StatusCode::try_from(400).unwrap(), + ); + + response.headers_mut().set(ContentType( + mimetypes::responses::WORK_POST_BAD_REQUEST + .clone(), + )); + + let body = serde_json::to_string(&body) + .expect("impossible to fail to serialize"); + + response.set_body(body); + } + WorkPostResponse::GenericErrorResponse(body) => { + response.set_status( + StatusCode::try_from(0).unwrap(), + ); + + response.headers_mut().set(ContentType(mimetypes::responses::WORK_POST_GENERIC_ERROR_RESPONSE.clone())); + + let body = serde_json::to_string(&body) + .expect("impossible to fail to serialize"); + + response.set_body(body); + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response + .set_status(StatusCode::InternalServerError); + response.set_body("An internal error occurred"); + } + } + + future::ok(response) + }, + )) + } + Err(e) => Box::new(future::ok( + Response::new() + .with_status(StatusCode::BadRequest) + .with_body(format!("Couldn't read body parameter body: {}", e)), + )), + } + }, + )) as Box<Future<Item = Response, Error = Error>> + } + + _ => Box::new(future::ok( + Response::new().with_status(StatusCode::NotFound), + )) as Box<Future<Item = Response, Error = Error>>, + } + } +} diff --git a/rust/src/api_lib.rs b/rust/src/api_lib.rs new file mode 100644 index 00000000..cc8c2313 --- /dev/null +++ b/rust/src/api_lib.rs @@ -0,0 +1,34 @@ +//! Main library entry point for fatcat implementation. + +// Imports required by server library. +// extern crate fatcat; +// extern crate swagger; +extern crate futures; +extern crate chrono; +#[macro_use] +extern crate error_chain; + +mod server; + +mod errors { + error_chain!{} +} + +pub use self::errors::*; +use std::io; +use hyper; +use fatcat; + +pub struct NewService; + +impl hyper::server::NewService for NewService { + type Request = (hyper::Request, fatcat::Context); + type Response = hyper::Response; + type Error = hyper::Error; + type Instance = fatcat::server::Service<server::Server>; + + /// Instantiate a new server. + fn new_service(&self) -> io::Result<Self::Instance> { + Ok(fatcat::server::Service::new(server::Server)) + } +} diff --git a/rust/src/api_server.rs b/rust/src/api_server.rs new file mode 100644 index 00000000..16701b27 --- /dev/null +++ b/rust/src/api_server.rs @@ -0,0 +1,288 @@ +//! Server implementation of fatcat. + +#![allow(unused_imports)] + +use chrono; +use futures::{self, Future}; + +use std::collections::HashMap; + +use swagger; + +use fatcat_api::models; +use fatcat_api::{Api, ApiError, ContainerIdGetResponse, ContainerLookupGetResponse, + ContainerPostResponse, Context, CreatorIdGetResponse, CreatorLookupGetResponse, + CreatorPostResponse, EditgroupIdAcceptPostResponse, EditgroupIdGetResponse, + EditgroupPostResponse, EditorUsernameChangelogGetResponse, + EditorUsernameGetResponse, FileIdGetResponse, FileLookupGetResponse, + FilePostResponse, ReleaseIdGetResponse, ReleaseLookupGetResponse, + ReleasePostResponse, WorkIdGetResponse, WorkPostResponse}; + +#[derive(Copy, Clone)] +pub struct Server; + +impl Api for Server { + fn container_id_get( + &self, + id: String, + context: &Context, + ) -> Box<Future<Item = ContainerIdGetResponse, Error = ApiError>> { + let context = context.clone(); + println!( + "container_id_get(\"{}\") - X-Span-ID: {:?}", + id, + context.x_span_id.unwrap_or(String::from("<none>")).clone() + ); + Box::new(futures::failed("Generic failure".into())) + } + + fn container_lookup_get( + &self, + issn: String, + context: &Context, + ) -> Box<Future<Item = ContainerLookupGetResponse, Error = ApiError>> { + let context = context.clone(); + println!( + "container_lookup_get(\"{}\") - X-Span-ID: {:?}", + issn, + context.x_span_id.unwrap_or(String::from("<none>")).clone() + ); + Box::new(futures::failed("Generic failure".into())) + } + + fn container_post( + &self, + body: Option<models::ContainerEntity>, + context: &Context, + ) -> Box<Future<Item = ContainerPostResponse, Error = ApiError>> { + let context = context.clone(); + println!( + "container_post({:?}) - X-Span-ID: {:?}", + body, + context.x_span_id.unwrap_or(String::from("<none>")).clone() + ); + Box::new(futures::failed("Generic failure".into())) + } + + fn creator_id_get( + &self, + id: String, + context: &Context, + ) -> Box<Future<Item = CreatorIdGetResponse, Error = ApiError>> { + let context = context.clone(); + println!( + "creator_id_get(\"{}\") - X-Span-ID: {:?}", + id, + context.x_span_id.unwrap_or(String::from("<none>")).clone() + ); + Box::new(futures::failed("Generic failure".into())) + } + + fn creator_lookup_get( + &self, + orcid: String, + context: &Context, + ) -> Box<Future<Item = CreatorLookupGetResponse, Error = ApiError>> { + let context = context.clone(); + println!( + "creator_lookup_get(\"{}\") - X-Span-ID: {:?}", + orcid, + context.x_span_id.unwrap_or(String::from("<none>")).clone() + ); + Box::new(futures::failed("Generic failure".into())) + } + + fn creator_post( + &self, + body: Option<models::CreatorEntity>, + context: &Context, + ) -> Box<Future<Item = CreatorPostResponse, Error = ApiError>> { + let context = context.clone(); + println!( + "creator_post({:?}) - X-Span-ID: {:?}", + body, + context.x_span_id.unwrap_or(String::from("<none>")).clone() + ); + Box::new(futures::failed("Generic failure".into())) + } + + fn editgroup_id_accept_post( + &self, + id: i32, + context: &Context, + ) -> Box<Future<Item = EditgroupIdAcceptPostResponse, Error = ApiError>> { + let context = context.clone(); + println!( + "editgroup_id_accept_post({}) - X-Span-ID: {:?}", + id, + context.x_span_id.unwrap_or(String::from("<none>")).clone() + ); + Box::new(futures::failed("Generic failure".into())) + } + + fn editgroup_id_get( + &self, + id: i32, + context: &Context, + ) -> Box<Future<Item = EditgroupIdGetResponse, Error = ApiError>> { + let context = context.clone(); + println!( + "editgroup_id_get({}) - X-Span-ID: {:?}", + id, + context.x_span_id.unwrap_or(String::from("<none>")).clone() + ); + Box::new(futures::failed("Generic failure".into())) + } + + fn editgroup_post( + &self, + context: &Context, + ) -> Box<Future<Item = EditgroupPostResponse, Error = ApiError>> { + let context = context.clone(); + println!( + "editgroup_post() - X-Span-ID: {:?}", + context.x_span_id.unwrap_or(String::from("<none>")).clone() + ); + Box::new(futures::failed("Generic failure".into())) + } + + fn editor_username_changelog_get( + &self, + username: String, + context: &Context, + ) -> Box<Future<Item = EditorUsernameChangelogGetResponse, Error = ApiError>> { + let context = context.clone(); + println!( + "editor_username_changelog_get(\"{}\") - X-Span-ID: {:?}", + username, + context.x_span_id.unwrap_or(String::from("<none>")).clone() + ); + Box::new(futures::failed("Generic failure".into())) + } + + fn editor_username_get( + &self, + username: String, + context: &Context, + ) -> Box<Future<Item = EditorUsernameGetResponse, Error = ApiError>> { + let context = context.clone(); + println!( + "editor_username_get(\"{}\") - X-Span-ID: {:?}", + username, + context.x_span_id.unwrap_or(String::from("<none>")).clone() + ); + Box::new(futures::failed("Generic failure".into())) + } + + fn file_id_get( + &self, + id: String, + context: &Context, + ) -> Box<Future<Item = FileIdGetResponse, Error = ApiError>> { + let context = context.clone(); + println!( + "file_id_get(\"{}\") - X-Span-ID: {:?}", + id, + context.x_span_id.unwrap_or(String::from("<none>")).clone() + ); + Box::new(futures::failed("Generic failure".into())) + } + + fn file_lookup_get( + &self, + sha1: String, + context: &Context, + ) -> Box<Future<Item = FileLookupGetResponse, Error = ApiError>> { + let context = context.clone(); + println!( + "file_lookup_get(\"{}\") - X-Span-ID: {:?}", + sha1, + context.x_span_id.unwrap_or(String::from("<none>")).clone() + ); + Box::new(futures::failed("Generic failure".into())) + } + + fn file_post( + &self, + body: Option<models::FileEntity>, + context: &Context, + ) -> Box<Future<Item = FilePostResponse, Error = ApiError>> { + let context = context.clone(); + println!( + "file_post({:?}) - X-Span-ID: {:?}", + body, + context.x_span_id.unwrap_or(String::from("<none>")).clone() + ); + Box::new(futures::failed("Generic failure".into())) + } + + fn release_id_get( + &self, + id: String, + context: &Context, + ) -> Box<Future<Item = ReleaseIdGetResponse, Error = ApiError>> { + let context = context.clone(); + println!( + "release_id_get(\"{}\") - X-Span-ID: {:?}", + id, + context.x_span_id.unwrap_or(String::from("<none>")).clone() + ); + Box::new(futures::failed("Generic failure".into())) + } + + fn release_lookup_get( + &self, + doi: String, + context: &Context, + ) -> Box<Future<Item = ReleaseLookupGetResponse, Error = ApiError>> { + let context = context.clone(); + println!( + "release_lookup_get(\"{}\") - X-Span-ID: {:?}", + doi, + context.x_span_id.unwrap_or(String::from("<none>")).clone() + ); + Box::new(futures::failed("Generic failure".into())) + } + + fn release_post( + &self, + body: Option<models::ReleaseEntity>, + context: &Context, + ) -> Box<Future<Item = ReleasePostResponse, Error = ApiError>> { + let context = context.clone(); + println!( + "release_post({:?}) - X-Span-ID: {:?}", + body, + context.x_span_id.unwrap_or(String::from("<none>")).clone() + ); + Box::new(futures::failed("Generic failure".into())) + } + + fn work_id_get( + &self, + id: String, + context: &Context, + ) -> Box<Future<Item = WorkIdGetResponse, Error = ApiError>> { + let context = context.clone(); + println!( + "work_id_get(\"{}\") - X-Span-ID: {:?}", + id, + context.x_span_id.unwrap_or(String::from("<none>")).clone() + ); + Box::new(futures::failed("Generic failure".into())) + } + + fn work_post( + &self, + body: Option<models::WorkEntity>, + context: &Context, + ) -> Box<Future<Item = WorkPostResponse, Error = ApiError>> { + let context = context.clone(); + println!( + "work_post({:?}) - X-Span-ID: {:?}", + body, + context.x_span_id.unwrap_or(String::from("<none>")).clone() + ); + Box::new(futures::failed("Generic failure".into())) + } +} diff --git a/rust/src/bin/fatcat-tokio.rs b/rust/src/bin/fatcat-tokio.rs new file mode 100644 index 00000000..cc603908 --- /dev/null +++ b/rust/src/bin/fatcat-tokio.rs @@ -0,0 +1,67 @@ +//! Main binary entry point for fatcat implementation. + +#![allow(missing_docs)] + +// Imports required by this file. +extern crate fatcat; +extern crate fatcat_api; +extern crate hyper; +extern crate swagger; +//extern crate openssl; +//extern crate native_tls; +extern crate tokio_proto; +//extern crate tokio_tls; +extern crate clap; + +//use openssl::x509::X509_FILETYPE_PEM; +//use openssl::ssl::{SslAcceptorBuilder, SslMethod}; +//use openssl::error::ErrorStack; +use clap::{App, Arg}; +use hyper::server::Http; +use swagger::auth::AllowAllAuthenticator; +use tokio_proto::TcpServer; + +// Builds an SSL implementation for Simple HTTPS from some hard-coded file names +/* +fn ssl() -> Result<SslAcceptorBuilder, ErrorStack> { + let mut ssl = SslAcceptorBuilder::mozilla_intermediate_raw(SslMethod::tls())?; + + // Server authentication + ssl.set_private_key_file("examples/server-key.pem", X509_FILETYPE_PEM)?; + ssl.set_certificate_chain_file("examples/server-chain.pem")?; + ssl.check_private_key()?; + + Ok(ssl) +} +*/ + +/// Create custom server, wire it to the autogenerated router, +/// and pass it to the web server. +fn main() { + let matches = App::new("server") + .arg( + Arg::with_name("https") + .long("https") + .help("Whether to use HTTPS or not"), + ) + .get_matches(); + + let service_fn = fatcat_api::server::auth::NewService::new(AllowAllAuthenticator::new( + fatcat::NewService, + "cosmo", + )); + + let addr = "127.0.0.1:8080" + .parse() + .expect("Failed to parse bind address"); + if matches.is_present("https") { + unimplemented!() + //let ssl = ssl().expect("Failed to load SSL keys"); + //let builder: native_tls::TlsAcceptorBuilder = native_tls::backend::openssl::TlsAcceptorBuilderExt::from_openssl(ssl); + //let tls_acceptor = builder.build().expect("Failed to build TLS acceptor"); + //TcpServer::new(tokio_tls::proto::Server::new(Http::new(), tls_acceptor), addr).serve(service_fn); + } else { + // Using HTTP + TcpServer::new(Http::new(), addr).serve(service_fn); + } +} diff --git a/rust/src/bin/show_creators.rs b/rust/src/bin/show_creators.rs deleted file mode 100644 index 968d2542..00000000 --- a/rust/src/bin/show_creators.rs +++ /dev/null @@ -1,24 +0,0 @@ - -extern crate fatcat; -extern crate diesel; - -use self::fatcat::*; -use self::models::*; -use self::diesel::prelude::*; - -fn main() { - use diesel_demo::database_schema::creators::dsl::*; - - let connection = establish_connection(); - let results = creators.filter(published.eq(true)) - .limit(5) - .load::<CreatorRev>(&connection) - .expect("Error loading creators"); - - println!("Displaying {} creators", results.len()); - for creator in results { - println!("{}", creator.title); - println!("----------\n"); - println!("{}", creator.body); - } -} diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 679b9ed3..ec7193e6 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -1,18 +1,46 @@ - #[macro_use] +extern crate fatcat_api; +extern crate chrono; extern crate diesel; extern crate dotenv; +extern crate futures; +extern crate hyper; +extern crate swagger; +#[macro_use] +extern crate error_chain; -use diesel::prelude::*; +pub mod api_server; + +mod errors { + error_chain!{} +} + +pub use self::errors::*; use diesel::pg::PgConnection; +use diesel::prelude::*; use dotenv::dotenv; use std::env; +use std::io; +//use hyper; +//use fatcat_api; pub fn establish_connection() -> PgConnection { dotenv().ok(); - let database_url = env::var("DATABASE_URL") - .expect("DATABASE_URL must be set"); - PgConnection::establish(&database_url) - .expect(&format!("Error connecting to {}", database_url)) + let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set"); + PgConnection::establish(&database_url).expect(&format!("Error connecting to {}", database_url)) +} + +pub struct NewService; + +impl hyper::server::NewService for NewService { + type Request = (hyper::Request, fatcat_api::Context); + type Response = hyper::Response; + type Error = hyper::Error; + type Instance = fatcat_api::server::Service<api_server::Server>; + + /// Instantiate a new server. + fn new_service(&self) -> io::Result<Self::Instance> { + Ok(fatcat_api::server::Service::new(api_server::Server)) + } } |