From 6d6c8e8fb27c5d8db47c4f59582e765ecad6efd8 Mon Sep 17 00:00:00 2001 From: Quentin de Quelen Date: Tue, 7 Apr 2020 17:47:35 +0200 Subject: [PATCH 01/21] Start change http server; finish document endpoint --- Cargo.lock | 1167 +++++++++++++++-------- meilisearch-core/src/database.rs | 16 +- meilisearch-core/src/store/mod.rs | 6 +- meilisearch-http/Cargo.toml | 8 +- meilisearch-http/src/data.rs | 4 +- meilisearch-http/src/error.rs | 220 ++--- meilisearch-http/src/helpers/mod.rs | 4 +- meilisearch-http/src/main.rs | 45 +- meilisearch-http/src/routes/document.rs | 263 ++--- meilisearch-http/src/routes/mod.rs | 205 ++-- 10 files changed, 1125 insertions(+), 813 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 399b0387e..9c3988d17 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,10 +1,274 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] -name = "ahash" -version = "0.3.2" +name = "actix-codec" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0989268a37e128d4d7a8028f1c60099430113fdbc70419010601ce51a228e4fe" +checksum = "09e55f0a5c2ca15795035d90c46bd0e73a5123b72f68f12596d6ba5282051380" +dependencies = [ + "bitflags", + "bytes 0.5.4", + "futures-core", + "futures-sink", + "log", + "tokio", + "tokio-util 0.2.0", +] + +[[package]] +name = "actix-connect" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c95cc9569221e9802bf4c377f6c18b90ef10227d787611decf79fd47d2a8e76c" +dependencies = [ + "actix-codec", + "actix-rt", + "actix-service", + "actix-utils", + "derive_more", + "either", + "futures", + "http 0.2.1", + "log", + "trust-dns-proto", + "trust-dns-resolver", +] + +[[package]] +name = "actix-http" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c16664cc4fdea8030837ad5a845eb231fb93fc3c5c171edfefb52fad92ce9019" +dependencies = [ + "actix-codec", + "actix-connect", + "actix-rt", + "actix-service", + "actix-threadpool", + "actix-utils", + "base64 0.11.0", + "bitflags", + "brotli2", + "bytes 0.5.4", + "chrono", + "copyless", + "derive_more", + "either", + "encoding_rs", + "failure", + "flate2", + "futures-channel", + "futures-core", + "futures-util", + "fxhash", + "h2", + "http 0.2.1", + "httparse", + "indexmap", + "language-tags", + "lazy_static", + "log", + "mime", + "percent-encoding 2.1.0", + "pin-project", + "rand 0.7.3", + "regex", + "serde", + "serde_json", + "serde_urlencoded", + "sha1", + "slab", + "time", +] + +[[package]] +name = "actix-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21705adc76bbe4bc98434890e73a89cd00c6015e5704a60bb6eea6c3b72316b6" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "actix-router" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d7a10ca4d94e8c8e7a87c5173aba1b97ba9a6563ca02b0e1cd23531093d3ec8" +dependencies = [ + "bytestring", + "http 0.2.1", + "log", + "regex", + "serde", +] + +[[package]] +name = "actix-rt" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20066d9200ef8d441ac156c76dd36c3f1e9a15976c34e69ae97f7f570b331882" +dependencies = [ + "actix-macros", + "actix-threadpool", + "copyless", + "futures-channel", + "futures-util", + "tokio", +] + +[[package]] +name = "actix-server" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "582a7173c281a4f46b5aa168a11e7f37183dcb71177a39312cc2264da7a632c9" +dependencies = [ + "actix-codec", + "actix-rt", + "actix-service", + "actix-utils", + "futures", + "log", + "mio", + "mio-uds", + "net2", + "num_cpus", + "slab", +] + +[[package]] +name = "actix-service" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e4fc95dfa7e24171b2d0bb46b85f8ab0e8499e4e3caec691fc4ea65c287564" +dependencies = [ + "futures-util", + "pin-project", +] + +[[package]] +name = "actix-testing" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48494745b72d0ea8ff0cf874aaf9b622a3ee03d7081ee0c04edea4f26d32c911" +dependencies = [ + "actix-macros", + "actix-rt", + "actix-server", + "actix-service", + "futures", + "log", + "net2", +] + +[[package]] +name = "actix-threadpool" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4082192601de5f303013709ff84d81ca6a1bc4af7fb24f367a500a23c6e84e" +dependencies = [ + "derive_more", + "futures-channel", + "lazy_static", + "log", + "num_cpus", + "parking_lot", + "threadpool", +] + +[[package]] +name = "actix-tls" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4e5b4faaf105e9a6d389c606c298dcdb033061b00d532af9df56ff3a54995a8" +dependencies = [ + "actix-codec", + "actix-rt", + "actix-service", + "actix-utils", + "derive_more", + "either", + "futures", + "log", +] + +[[package]] +name = "actix-utils" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcf8f5631bf01adec2267808f00e228b761c60c0584cc9fa0b5364f41d147f4e" +dependencies = [ + "actix-codec", + "actix-rt", + "actix-service", + "bitflags", + "bytes 0.5.4", + "either", + "futures", + "log", + "pin-project", + "slab", +] + +[[package]] +name = "actix-web" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3158e822461040822f0dbf1735b9c2ce1f95f93b651d7a7aded00b1efbb1f635" +dependencies = [ + "actix-codec", + "actix-http", + "actix-macros", + "actix-router", + "actix-rt", + "actix-server", + "actix-service", + "actix-testing", + "actix-threadpool", + "actix-tls", + "actix-utils", + "actix-web-codegen", + "awc", + "bytes 0.5.4", + "derive_more", + "encoding_rs", + "futures", + "fxhash", + "log", + "mime", + "net2", + "pin-project", + "regex", + "serde", + "serde_json", + "serde_urlencoded", + "time", + "url", +] + +[[package]] +name = "actix-web-codegen" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f00371942083469785f7e28c540164af1913ee7c96a4534acb9cea92c39f057" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "adler32" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2" + +[[package]] +name = "ahash" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35b909d1c126f78ace756fc337133356c499eebeefcce930fa5fb018823f2b2d" dependencies = [ "const-random", ] @@ -29,9 +293,9 @@ dependencies = [ [[package]] name = "arc-swap" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d663a8e9a99154b5fb793032533f6328da35e23aac63d5c152279aa8ba356825" +checksum = "b585a98a234c46fc563103e9278c9391fde1f4e6850334da895d27edb9580f62" [[package]] name = "assert-json-diff" @@ -48,37 +312,16 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7deb0a829ca7bcfaf5da70b073a8d128619259a7be8216a355e23f00763059e5" -[[package]] -name = "async-attributes" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd3d156917d94862e779f356c5acae312b08fd3121e792c857d7928c8088423" -dependencies = [ - "quote", - "syn", -] - [[package]] name = "async-std" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "538ecb01eb64eecd772087e5b6f7540cbc917f047727339a472dafed2185b267" dependencies = [ - "async-attributes", - "async-task", - "broadcaster", - "crossbeam-channel", - "crossbeam-deque", "crossbeam-utils", "futures-core", "futures-io", - "futures-timer", - "kv-log-macro", - "log", "memchr", - "mio", - "mio-uds", - "num_cpus", "once_cell", "pin-project-lite", "pin-utils", @@ -86,13 +329,14 @@ dependencies = [ ] [[package]] -name = "async-task" -version = "1.3.1" +name = "async-trait" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ac2c016b079e771204030951c366db398864f5026f84a44dafb0ff20f02085d" +checksum = "da71fef07bc806586090247e971229289f64c210a278ee5ae419314eb386b31d" dependencies = [ - "libc", - "winapi 0.3.8", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -112,6 +356,29 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" +[[package]] +name = "awc" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7601d4d1d7ef2335d6597a41b5fe069f6ab799b85f53565ab390e7b7065aac5" +dependencies = [ + "actix-codec", + "actix-http", + "actix-rt", + "actix-service", + "base64 0.11.0", + "bytes 0.5.4", + "derive_more", + "futures-core", + "log", + "mime", + "percent-encoding 2.1.0", + "rand 0.7.3", + "serde", + "serde_json", + "serde_urlencoded", +] + [[package]] name = "backtrace" version = "0.3.46" @@ -126,9 +393,9 @@ dependencies = [ [[package]] name = "backtrace-sys" -version = "0.1.35" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7de8aba10a69c8e8d7622c5710229485ec32e9d55fdad160ea559c086fdcd118" +checksum = "78848718ee1255a2485d1309ad9cdecfc2e7d0362dd11c6829364c6b35ae1bc7" dependencies = [ "cc", "libc", @@ -184,17 +451,23 @@ dependencies = [ ] [[package]] -name = "broadcaster" -version = "1.0.0" +name = "brotli-sys" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c972e21e0d055a36cf73e4daae870941fe7a8abcd5ac3396aab9e4c126bd87" +checksum = "4445dea95f4c2b41cde57cc9fee236ae4dbae88d8fcbdb4750fc1bb5d86aaecd" dependencies = [ - "futures-channel", - "futures-core", - "futures-sink", - "futures-util", - "parking_lot 0.10.0", - "slab", + "cc", + "libc", +] + +[[package]] +name = "brotli2" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cb036c3eade309815c15ddbacec5b22c4d1f3983a774ab2eac2e3e9ea85568e" +dependencies = [ + "brotli-sys", + "libc", ] [[package]] @@ -234,10 +507,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" dependencies = [ "byteorder", - "either", "iovec", ] +[[package]] +name = "bytes" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "130aac562c0dd69c56b3b1cc8ffd2e17be31d0b6c25b61c96b76231aa23e39e1" + +[[package]] +name = "bytestring" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7c05fa5172da78a62d9949d662d2ac89d4cc7355d7b49adee5163f1fb3f363" +dependencies = [ + "bytes 0.5.4", +] + [[package]] name = "cast" version = "0.2.3" @@ -249,9 +536,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.50" +version = "1.0.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd" +checksum = "c3d87b23d6a92cd03af510a5ade527033f6aa6fa92161e2d5863a907d4c5e31d" [[package]] name = "cfg-if" @@ -273,9 +560,9 @@ dependencies = [ [[package]] name = "chunked_transfer" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f98beb6554de08a14bd7b5c6014963c79d6a25a1c66b1d4ecb9e733ccba51d6c" +checksum = "5b89647f09b9f4c838cb622799b2843e4e13bff64661dab9a0362bb92985addd" [[package]] name = "clap" @@ -328,13 +615,18 @@ dependencies = [ ] [[package]] -name = "cookie" -version = "0.12.0" +name = "copyless" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "888604f00b3db336d2af898ec3c1d5d0ddf5e6d462220f2ededc33a87ac4bbd5" +checksum = "6ff9c56c9fb2a49c05ef0e431485a22400af20d33226dc0764d891d09e724127" + +[[package]] +name = "crc32fast" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" dependencies = [ - "time", - "url 1.7.2", + "cfg-if", ] [[package]] @@ -458,10 +750,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11c0346158a19b3627234e15596f5e465c360fcdb97d817bcb255e0510f5a788" [[package]] -name = "deunicode" -version = "1.1.0" +name = "derive_more" +version = "0.99.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "307dde1a517939465bc4042b47377284a56cee6160f8066f1f5035eb7b25a3fc" +checksum = "e2323f3f47db9a0e77ce7a300605d8d2098597fc451ed1a97bb1f6411bb550a7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "deunicode" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80115a2dfde04491e181c2440a39e4be26e52d9ca4e92bed213f65b94e0b8db1" [[package]] name = "digest" @@ -478,12 +781,39 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" +[[package]] +name = "dtoa" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4358a9e11b9a09cf52383b451b49a169e8d797b68aa02301ff586d70d9661ea3" + [[package]] name = "either" version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" +[[package]] +name = "encoding_rs" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd8d03faa7fe0c1431609dfad7bbe827af30f82e1e2ae6f7ee4fca6bd764bc28" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "enum-as-inner" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc4bfcfacb61d231109d1d55202c1f33263319668b168843e02ad4652725ec9c" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "env_logger" version = "0.7.1" @@ -507,12 +837,46 @@ dependencies = [ "version_check", ] +[[package]] +name = "failure" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8529c2421efa3066a5cbd8063d2244603824daccb6936b079010bb2aa89464b" +dependencies = [ + "backtrace", + "failure_derive", +] + +[[package]] +name = "failure_derive" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "030a733c8287d6213886dd487564ff5c8f6aae10278b3588ed177f9d18f8d231" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + [[package]] name = "fake-simd" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" +[[package]] +name = "flate2" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cfff41391129e0a856d6d822600b8d71179d46879e310417eb9c762eb178b42" +dependencies = [ + "cfg-if", + "crc32fast", + "libc", + "miniz_oxide", +] + [[package]] name = "fnv" version = "1.0.6" @@ -556,12 +920,6 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" -[[package]] -name = "futures" -version = "0.1.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b980f2816d6ee8673b6517b52cb0e808a180efc92e5c19d02cdda79066703ef" - [[package]] name = "futures" version = "0.3.4" @@ -593,16 +951,6 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f25592f769825e89b92358db00d26f965761e094951ac44d3663ef25b7ac464a" -[[package]] -name = "futures-cpupool" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" -dependencies = [ - "futures 0.1.29", - "num_cpus", -] - [[package]] name = "futures-executor" version = "0.3.4" @@ -644,19 +992,12 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b0a34e53cf6cdcd0178aa573aed466b646eb3db769570841fda0c7ede375a27" -[[package]] -name = "futures-timer" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1de7508b218029b0f01662ed8f61b1c964b3ae99d6f25462d0f55a595109df6" - [[package]] name = "futures-util" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22766cf25d64306bedf0384da004d05c9974ab104fcc4528f1236181c18004c5" dependencies = [ - "futures 0.1.29", "futures-channel", "futures-core", "futures-io", @@ -668,7 +1009,15 @@ dependencies = [ "proc-macro-hack", "proc-macro-nested", "slab", - "tokio-io", +] + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", ] [[package]] @@ -702,20 +1051,21 @@ dependencies = [ [[package]] name = "h2" -version = "0.1.26" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5b34c246847f938a410a03c5458c7fee2274436675e76d8b903c08efc29c462" +checksum = "377038bf3c89d18d6ca1431e7a5027194fbd724ca10592b9487ede5e8e144f42" dependencies = [ - "byteorder", - "bytes", + "bytes 0.5.4", "fnv", - "futures 0.1.29", - "http", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.1", "indexmap", "log", "slab", - "string", - "tokio-io", + "tokio", + "tokio-util 0.3.1", ] [[package]] @@ -740,52 +1090,80 @@ dependencies = [ [[package]] name = "heed" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f73881598a84bdb0d7bcf40496c1a2a6e10dc06cbb73fb56989b950037bff63c" +checksum = "5d432af60d4dada5bc2d391b98ed258a16e659a4b5ec88b0c90e148062ed1a5c" dependencies = [ - "bincode", "byteorder", + "heed-traits", + "heed-types", "libc", "lmdb-rkv-sys", "once_cell", "page_size", + "url", + "zerocopy", +] + +[[package]] +name = "heed-traits" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b328f6260a7e51bdb0ca6b68e6ea27ee3d11fba5dee930896ee7ff6ad5fc072c" + +[[package]] +name = "heed-types" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e100387815256b00dbb4f48db990f7fa03e9b88b4a89c2a1661b7d9d77b77c46" +dependencies = [ + "bincode", + "heed-traits", "serde", "serde_json", - "url 2.1.1", "zerocopy", ] [[package]] name = "hermit-abi" -version = "0.1.10" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "725cf19794cf90aa94e65050cb4191ff5d8fa87a498383774c47b332e3af952e" +checksum = "61565ff7aaace3525556587bd2dc31d4a07071957be715e63ce7b1eccf51a8f4" dependencies = [ "libc", ] +[[package]] +name = "hostname" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +dependencies = [ + "libc", + "match_cfg", + "winapi 0.3.8", +] + [[package]] name = "http" version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6ccf5ede3a895d8856620237b2f02972c1bbc78d2965ad7fe8838d4a0ed41f0" dependencies = [ - "bytes", + "bytes 0.4.12", "fnv", "itoa", ] [[package]] -name = "http-body" -version = "0.1.0" +name = "http" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6741c859c1b2463a423a1dbce98d418e6c3c3fc720fb0d45528657320920292d" +checksum = "28d569972648b2c512421b5f2a405ad6ac9666547189d0c5477a3f200f3e02f9" dependencies = [ - "bytes", - "futures 0.1.29", - "http", - "tokio-buf", + "bytes 0.5.4", + "fnv", + "itoa", ] [[package]] @@ -795,31 +1173,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9625f605ddfaf894bf78a544a7b8e31f562dc843654723a49892d9c7e75ac708" dependencies = [ "async-std", - "bytes", - "futures 0.3.4", - "http", + "bytes 0.4.12", + "futures", + "http 0.1.21", "pin-project-lite", ] -[[package]] -name = "http-service-hyper" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33d5dae94e0fdb82f9524ea2f2b98458b3d8448526d8cc8beccb3d3fded8aff" -dependencies = [ - "futures 0.3.4", - "http", - "http-service", - "hyper", -] - [[package]] name = "http-service-mock" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "893e67bcfcd552717ddfcc3af17fa5397d39a2b1a64a8266271efff619d6af45" dependencies = [ - "futures 0.3.4", + "futures", "http-service", ] @@ -838,47 +1204,6 @@ dependencies = [ "quick-error", ] -[[package]] -name = "hyper" -version = "0.12.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dbe6ed1438e1f8ad955a4701e9a944938e9519f6888d12d8558b645e247d5f6" -dependencies = [ - "bytes", - "futures 0.1.29", - "futures-cpupool", - "h2", - "http", - "http-body", - "httparse", - "iovec", - "itoa", - "log", - "net2", - "rustc_version", - "time", - "tokio", - "tokio-buf", - "tokio-executor", - "tokio-io", - "tokio-reactor", - "tokio-tcp", - "tokio-threadpool", - "tokio-timer", - "want", -] - -[[package]] -name = "idna" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" -dependencies = [ - "matches", - "unicode-bidi", - "unicode-normalization", -] - [[package]] name = "idna" version = "0.2.0" @@ -906,7 +1231,7 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8254add2ea664734c9d001f8151cc3d7696b135f7e40e5a2efa814a662cb3a44" dependencies = [ - "smallvec 1.3.0", + "smallvec", ] [[package]] @@ -918,6 +1243,18 @@ dependencies = [ "libc", ] +[[package]] +name = "ipconfig" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa79fa216fbe60834a9c0737d7fcd30425b32d1c58854663e24d4c4b328ed83f" +dependencies = [ + "socket2", + "widestring", + "winapi 0.3.8", + "winreg", +] + [[package]] name = "itertools" version = "0.8.2" @@ -983,13 +1320,10 @@ dependencies = [ ] [[package]] -name = "kv-log-macro" -version = "1.0.4" +name = "language-tags" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c54d9f465d530a752e6ebdc217e081a7a614b48cb200f6f0aee21ba6bc9aabb" -dependencies = [ - "log", -] +checksum = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" [[package]] name = "lazy_static" @@ -1008,9 +1342,15 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.68" +version = "0.2.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dea0c0405123bba743ee3f91f49b1c7cfb684eef0da0a50110f758ccf24cdff0" +checksum = "99e85c08494b21a9054e7fe1374a732aeadaff3980b6990b94bfd3a70f690005" + +[[package]] +name = "linked-hash-map" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83" [[package]] name = "lmdb-rkv-sys" @@ -1025,9 +1365,9 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79b2de95ecb4691949fea4716ca53cdbcfccb2c612e19644a8bad05edcf9f47b" +checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" dependencies = [ "scopeguard", ] @@ -1041,6 +1381,15 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "lru-cache" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" +dependencies = [ + "linked-hash-map", +] + [[package]] name = "main_error" version = "0.1.0" @@ -1053,6 +1402,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" +[[package]] +name = "match_cfg" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" + [[package]] name = "matches" version = "0.1.8" @@ -1114,14 +1469,16 @@ dependencies = [ name = "meilisearch-http" version = "0.10.1" dependencies = [ + "actix-http", + "actix-rt", + "actix-web", "assert-json-diff", - "async-std", "chrono", "crossbeam-channel", "env_logger", - "futures 0.3.4", + "futures", "heed", - "http", + "http 0.1.21", "http-service", "http-service-mock", "indexmap", @@ -1145,7 +1502,7 @@ dependencies = [ "structopt", "sysinfo", "tempdir", - "tide", + "tokio", "ureq", "vergen", "walkdir", @@ -1200,6 +1557,15 @@ version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +[[package]] +name = "miniz_oxide" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa679ff6578b1cddee93d7e82e263b94a575e0bfced07284eb0c037c1d2416a5" +dependencies = [ + "adler32", +] + [[package]] name = "mio" version = "0.6.21" @@ -1296,9 +1662,9 @@ dependencies = [ [[package]] name = "num_cpus" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46203554f085ff89c235cd12f7075f3233af9b11ed7c9e16dfe2560d03313ce6" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" dependencies = [ "hermit-abi", "libc", @@ -1344,51 +1710,25 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.9.0" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" +checksum = "d3a704eb390aafdc107b0e392f56a82b668e3a71366993b5340f5833fd62505e" dependencies = [ "lock_api", - "parking_lot_core 0.6.2", - "rustc_version", -] - -[[package]] -name = "parking_lot" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92e98c49ab0b7ce5b222f2cc9193fc4efe11c6d0bd4f648e374684a6857b1cfc" -dependencies = [ - "lock_api", - "parking_lot_core 0.7.0", + "parking_lot_core", ] [[package]] name = "parking_lot_core" -version = "0.6.2" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" +checksum = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3" dependencies = [ "cfg-if", "cloudabi", "libc", "redox_syscall", - "rustc_version", - "smallvec 0.6.13", - "winapi 0.3.8", -] - -[[package]] -name = "parking_lot_core" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7582838484df45743c8434fbff785e8edf260c28748353d44bc0da32e0ceabf1" -dependencies = [ - "cfg-if", - "cloudabi", - "libc", - "redox_syscall", - "smallvec 1.3.0", + "smallvec", "winapi 0.3.8", ] @@ -1455,6 +1795,26 @@ dependencies = [ "sha-1", ] +[[package]] +name = "pin-project" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f6a7f5eee6292c559c793430c55c00aea9d3b3d1905e855806ca4d7253426a2" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8988430ce790d8682672117bc06dda364c0be32d3abd738234f19f3240bad99a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "pin-project-lite" version = "0.1.4" @@ -1463,9 +1823,9 @@ checksum = "237844750cfbb86f67afe27eee600dfbbcb6188d734139b534cbfbf4f96792ae" [[package]] name = "pin-utils" -version = "0.1.0-alpha.4" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" @@ -1682,9 +2042,9 @@ checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" [[package]] name = "regex" -version = "1.3.6" +version = "1.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6946991529684867e47d86474e3a6d0c0ab9b82d5821e314b1ede31fa3a4b3" +checksum = "a6020f034922e3194c711b82a627453881bc4682166cabb07134a10c26ba7692" dependencies = [ "aho-corasick", "memchr", @@ -1716,6 +2076,16 @@ dependencies = [ "winapi 0.3.8", ] +[[package]] +name = "resolv-conf" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11834e137f3b14e309437a8276714eed3a80d1ef894869e510f2c0c0b98b9f4a" +dependencies = [ + "hostname", + "quick-error", +] + [[package]] name = "ring" version = "0.16.12" @@ -1731,12 +2101,6 @@ dependencies = [ "winapi 0.3.8", ] -[[package]] -name = "route-recognizer" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea509065eb0b3c446acdd0102f0d46567dc30902dc0be91d6552035d92b0f4f8" - [[package]] name = "rustc-demangle" version = "0.1.16" @@ -1767,15 +2131,16 @@ dependencies = [ [[package]] name = "rustyline" -version = "6.1.0" +version = "6.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6155a1169c281a8b75ee943a83eef0e2bb63aff057688c2111be319e51fc9f63" +checksum = "1cd20b28d972040c627e209eb29f19c24a71a19d661cc5a220089176e20ee202" dependencies = [ "cfg-if", "libc", "log", "memchr", "nix", + "scopeguard", "unicode-segmentation", "unicode-width", "utf8parse", @@ -1784,9 +2149,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "535622e6be132bccd223f4bb2b8ac8d53cda3c7a6394944d3b2b33fb974f9d76" +checksum = "ed3d612bc64430efeb3f7ee6ef26d590dce0c43249217bddc62112540c7941e1" [[package]] name = "same-file" @@ -1878,6 +2243,18 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_urlencoded" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ec5d77e2d4c73717816afac02670d5c4f534ea95ed430442cad02e7a6e32c97" +dependencies = [ + "dtoa", + "itoa", + "serde", + "url", +] + [[package]] name = "sha-1" version = "0.8.2" @@ -1890,6 +2267,12 @@ dependencies = [ "opaque-debug", ] +[[package]] +name = "sha1" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" + [[package]] name = "sha2" version = "0.8.1" @@ -1903,10 +2286,20 @@ dependencies = [ ] [[package]] -name = "siphasher" -version = "0.3.2" +name = "signal-hook-registry" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e88f89a550c01e4cd809f3df4f52dc9e939f3273a2017eabd5c6d12fd98bb23" +checksum = "94f478ede9f64724c5d173d7bb56099ec3e2d9fc2774aac65d34b8b890405f41" +dependencies = [ + "arc-swap", + "libc", +] + +[[package]] +name = "siphasher" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa8f3741c7372e75519bd9346068370c9cdaabcc1f9599cbcf2a2719352286b7" [[package]] name = "slab" @@ -1922,18 +2315,21 @@ checksum = "1f7474f0b646d228360ab62ed974744617bc869d959eac8403bfa3665931a7fb" [[package]] name = "smallvec" -version = "0.6.13" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7b0758c52e15a8b5e3691eae6cc559f08eee9406e548a4477ba4e67770a82b6" -dependencies = [ - "maybe-uninit", -] +checksum = "c7cb5678e1615754284ec264d9bb5b4c27d2018577fd90ac0ceb578591ed5ee4" [[package]] -name = "smallvec" -version = "1.3.0" +name = "socket2" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05720e22615919e4734f6a99ceae50d00226c3c5aca406e102ebc33298214e0a" +checksum = "03088793f677dce356f3ccc2edb1b314ad191ab702a5de3faf49304f7e104918" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "winapi 0.3.8", +] [[package]] name = "spin" @@ -1941,15 +2337,6 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" -[[package]] -name = "string" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d24114bfcceb867ca7f71a0d3fe45d45619ec47a6fbfa98cb14e14250bfa5d6d" -dependencies = [ - "bytes", -] - [[package]] name = "strsim" version = "0.8.0" @@ -1958,9 +2345,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" [[package]] name = "structopt" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff6da2e8d107dfd7b74df5ef4d205c6aebee0706c647f6bc6a2d5789905c00fb" +checksum = "863246aaf5ddd0d6928dfeb1a9ca65f505599e4e1b399935ef7e75107516b4ef" dependencies = [ "clap", "lazy_static", @@ -1969,9 +2356,9 @@ dependencies = [ [[package]] name = "structopt-derive" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a489c87c08fbaf12e386665109dd13470dcc9c4583ea3e10dd2b4523e5ebd9ac" +checksum = "d239ca4b13aee7a2142e6795cbd69e457665ff8037aed33b3effdc430d2f927a" dependencies = [ "heck", "proc-macro-error", @@ -1982,9 +2369,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0df0eb663f387145cab623dea85b09c2c5b4b0aef44e945d928e682fce71bb03" +checksum = "410a7488c0a728c7ceb4ad59b9567eb4053d02e8cc7f5c0e0eeeb39518369213" dependencies = [ "proc-macro2", "quote", @@ -2081,42 +2468,29 @@ dependencies = [ ] [[package]] -name = "tide" -version = "0.6.0" +name = "threadpool" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e619c99048ae107912703d0efeec4ff4fbff704f064e51d3eee614b28ea7b739" +checksum = "e8dae184447c15d5a6916d973c642aec485105a13cd238192a6927ae3e077d66" dependencies = [ - "async-std", - "cookie", - "futures 0.3.4", - "http", - "http-service", - "http-service-hyper", - "log", - "mime", - "pin-project-lite", - "route-recognizer", - "serde", - "serde_json", - "serde_qs", + "num_cpus", ] [[package]] name = "time" -version = "0.1.42" +version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" +checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" dependencies = [ "libc", - "redox_syscall", "winapi 0.3.8", ] [[package]] name = "tinytemplate" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57a3c6667d3e65eb1bc3aed6fd14011c6cbc3a0665218ab7f5daf040b9ec371a" +checksum = "45e4bc5ac99433e0dcb8b9f309dd271a165ae37dde129b9e0ce1bfdd8bfe4891" dependencies = [ "serde", "serde_json", @@ -2124,134 +2498,63 @@ dependencies = [ [[package]] name = "tokio" -version = "0.1.22" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a09c0b5bb588872ab2f09afa13ee6e9dac11e10a0ec9e8e3ba39a5a5d530af6" -dependencies = [ - "bytes", - "futures 0.1.29", - "mio", - "num_cpus", - "tokio-current-thread", - "tokio-executor", - "tokio-io", - "tokio-reactor", - "tokio-threadpool", - "tokio-timer", -] - -[[package]] -name = "tokio-buf" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fb220f46c53859a4b7ec083e41dec9778ff0b1851c0942b211edb89e0ccdc46" -dependencies = [ - "bytes", - "either", - "futures 0.1.29", -] - -[[package]] -name = "tokio-current-thread" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1de0e32a83f131e002238d7ccde18211c0a5397f60cbfffcb112868c2e0e20e" -dependencies = [ - "futures 0.1.29", - "tokio-executor", -] - -[[package]] -name = "tokio-executor" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb2d1b8f4548dbf5e1f7818512e9c406860678f29c300cdf0ebac72d1a3a1671" -dependencies = [ - "crossbeam-utils", - "futures 0.1.29", -] - -[[package]] -name = "tokio-io" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57fc868aae093479e3131e3d165c93b1c7474109d13c90ec0dda2a1bbfff0674" -dependencies = [ - "bytes", - "futures 0.1.29", - "log", -] - -[[package]] -name = "tokio-reactor" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09bc590ec4ba8ba87652da2068d150dcada2cfa2e07faae270a5e0409aa51351" -dependencies = [ - "crossbeam-utils", - "futures 0.1.29", - "lazy_static", - "log", - "mio", - "num_cpus", - "parking_lot 0.9.0", - "slab", - "tokio-executor", - "tokio-io", - "tokio-sync", -] - -[[package]] -name = "tokio-sync" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edfe50152bc8164fcc456dab7891fa9bf8beaf01c5ee7e1dd43a397c3cf87dee" +checksum = "7d9c43f1bb96970e153bcbae39a65e249ccb942bd9d36dbdf086024920417c9c" dependencies = [ + "bytes 0.5.4", "fnv", - "futures 0.1.29", -] - -[[package]] -name = "tokio-tcp" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98df18ed66e3b72e742f185882a9e201892407957e45fbff8da17ae7a7c51f72" -dependencies = [ - "bytes", - "futures 0.1.29", + "futures-core", "iovec", - "mio", - "tokio-io", - "tokio-reactor", -] - -[[package]] -name = "tokio-threadpool" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df720b6581784c118f0eb4310796b12b1d242a7eb95f716a8367855325c25f89" -dependencies = [ - "crossbeam-deque", - "crossbeam-queue", - "crossbeam-utils", - "futures 0.1.29", "lazy_static", - "log", - "num_cpus", + "libc", + "memchr", + "mio", + "mio-uds", + "pin-project-lite", + "signal-hook-registry", "slab", - "tokio-executor", + "tokio-macros", + "winapi 0.3.8", ] [[package]] -name = "tokio-timer" -version = "0.2.13" +name = "tokio-macros" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93044f2d313c95ff1cb7809ce9a7a05735b012288a888b62d4434fd58c94f296" +checksum = "f0c3acc6aa564495a0f2e1d59fab677cd7f81a19994cfc7f3ad0e64301560389" dependencies = [ - "crossbeam-utils", - "futures 0.1.29", - "slab", - "tokio-executor", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-util" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "571da51182ec208780505a32528fc5512a8fe1443ab960b3f2f3ef093cd16930" +dependencies = [ + "bytes 0.5.4", + "futures-core", + "futures-sink", + "log", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be8242891f2b6cbef26a2d7e8605133c2c554cd35b3e4948ea892d6d68436499" +dependencies = [ + "bytes 0.5.4", + "futures-core", + "futures-sink", + "log", + "pin-project-lite", + "tokio", ] [[package]] @@ -2265,16 +2568,49 @@ dependencies = [ ] [[package]] -name = "try-lock" -version = "0.2.2" +name = "trust-dns-proto" +version = "0.18.0-alpha.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382" +checksum = "2a7f3a2ab8a919f5eca52a468866a67ed7d3efa265d48a652a9a3452272b413f" +dependencies = [ + "async-trait", + "enum-as-inner", + "failure", + "futures", + "idna", + "lazy_static", + "log", + "rand 0.7.3", + "smallvec", + "socket2", + "tokio", + "url", +] + +[[package]] +name = "trust-dns-resolver" +version = "0.18.0-alpha.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f90b1502b226f8b2514c6d5b37bafa8c200d7ca4102d57dc36ee0f3b7a04a2f" +dependencies = [ + "cfg-if", + "failure", + "futures", + "ipconfig", + "lazy_static", + "log", + "lru-cache", + "resolv-conf", + "smallvec", + "tokio", + "trust-dns-proto", +] [[package]] name = "typenum" -version = "1.11.2" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" +checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" [[package]] name = "ucd-trie" @@ -2306,7 +2642,7 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5479532badd04e128284890390c1e876ef7a993d0570b3597ae43dfa1d59afa4" dependencies = [ - "smallvec 1.3.0", + "smallvec", ] [[package]] @@ -2335,38 +2671,27 @@ checksum = "60369ef7a31de49bcb3f6ca728d4ba7300d9a1658f94c727d4cab8c8d9f4aece" [[package]] name = "ureq" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1c16042f50ea0cf94c451ea80b933875747e9e27ba238824e962e719885d958" +checksum = "cd754afd5f60388b4188210c3795392c5f2fd69a1cc947ec4505dbfee955b902" dependencies = [ "base64 0.12.0", "chunked_transfer", "lazy_static", "qstring", "rustls", - "url 2.1.1", + "url", "webpki", "webpki-roots", ] -[[package]] -name = "url" -version = "1.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a" -dependencies = [ - "idna 0.1.5", - "matches", - "percent-encoding 1.0.1", -] - [[package]] name = "url" version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb" dependencies = [ - "idna 0.2.0", + "idna", "matches", "percent-encoding 2.1.0", ] @@ -2416,17 +2741,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "want" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6395efa4784b027708f7451087e647ec73cc74f5d9bc2e418404248d679a230" -dependencies = [ - "futures 0.1.29", - "log", - "try-lock", -] - [[package]] name = "wasi" version = "0.9.0+wasi-snapshot-preview1" @@ -2522,6 +2836,12 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a08eb844b158ea881e81b94556eede7f7e306e4c7b976aad88f49e6e36dec391" +[[package]] +name = "widestring" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "effc0e4ff8085673ea7b9b2e3c73f6bd4d118810c9009ed8f1e16bd96c331db6" + [[package]] name = "winapi" version = "0.2.8" @@ -2552,9 +2872,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa515c5163a99cc82bab70fd3bfdd36d827be85de63737b40fcef2ce084a436e" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" dependencies = [ "winapi 0.3.8", ] @@ -2565,6 +2885,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "winreg" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2986deb581c4fe11b621998a5e53361efe6b48a151178d0cd9eeffa4dc6acc9" +dependencies = [ + "winapi 0.3.8", +] + [[package]] name = "ws2_32-sys" version = "0.2.1" diff --git a/meilisearch-core/src/database.rs b/meilisearch-core/src/database.rs index 87fc24c11..b2fdfce3a 100644 --- a/meilisearch-core/src/database.rs +++ b/meilisearch-core/src/database.rs @@ -756,16 +756,16 @@ mod tests { update_reader.abort(); let reader = db.main_read_txn().unwrap(); - let document: Option = index.document(&reader, None, DocumentId(25)).unwrap(); + let document: Option = index.document::<_, String>(&reader, None, DocumentId(25)).unwrap(); assert!(document.is_none()); let document: Option = index - .document(&reader, None, DocumentId(7_900_334_843_754_999_545)) + .document::<_, String>(&reader, None, DocumentId(7_900_334_843_754_999_545)) .unwrap(); assert!(document.is_some()); let document: Option = index - .document(&reader, None, DocumentId(8_367_468_610_878_465_872)) + .document::<_, String>(&reader, None, DocumentId(8_367_468_610_878_465_872)) .unwrap(); assert!(document.is_some()); } @@ -836,16 +836,16 @@ mod tests { update_reader.abort(); let reader = db.main_read_txn().unwrap(); - let document: Option = index.document(&reader, None, DocumentId(25)).unwrap(); + let document: Option = index.document::<_, String>(&reader, None, DocumentId(25)).unwrap(); assert!(document.is_none()); let document: Option = index - .document(&reader, None, DocumentId(7_900_334_843_754_999_545)) + .document::<_, String>(&reader, None, DocumentId(7_900_334_843_754_999_545)) .unwrap(); assert!(document.is_some()); let document: Option = index - .document(&reader, None, DocumentId(8_367_468_610_878_465_872)) + .document::<_, String>(&reader, None, DocumentId(8_367_468_610_878_465_872)) .unwrap(); assert!(document.is_some()); @@ -882,7 +882,7 @@ mod tests { let reader = db.main_read_txn().unwrap(); let document: Option = index - .document(&reader, None, DocumentId(7_900_334_843_754_999_545)) + .document::<_, String>(&reader, None, DocumentId(7_900_334_843_754_999_545)) .unwrap(); let new_doc1 = serde_json::json!({ @@ -893,7 +893,7 @@ mod tests { assert_eq!(document, Some(new_doc1)); let document: Option = index - .document(&reader, None, DocumentId(8_367_468_610_878_465_872)) + .document::<_, String>(&reader, None, DocumentId(8_367_468_610_878_465_872)) .unwrap(); let new_doc2 = serde_json::json!({ diff --git a/meilisearch-core/src/store/mod.rs b/meilisearch-core/src/store/mod.rs index 48bcfcef8..342f6c266 100644 --- a/meilisearch-core/src/store/mod.rs +++ b/meilisearch-core/src/store/mod.rs @@ -214,17 +214,17 @@ pub struct Index { } impl Index { - pub fn document( + pub fn document>( &self, reader: &heed::RoTxn, - attributes: Option<&HashSet<&str>>, + attributes: Option>, document_id: DocumentId, ) -> MResult> { let schema = self.main.schema(reader)?; let schema = schema.ok_or(Error::SchemaMissing)?; let attributes = match attributes { - Some(attributes) => Some(attributes.iter().filter_map(|name| schema.id(*name)).collect()), + Some(attributes) => Some(attributes.iter().filter_map(|name| schema.id(name.as_ref())).collect()), None => None, }; diff --git a/meilisearch-http/Cargo.toml b/meilisearch-http/Cargo.toml index b2bac5852..1c716e726 100644 --- a/meilisearch-http/Cargo.toml +++ b/meilisearch-http/Cargo.toml @@ -14,7 +14,7 @@ name = "meilisearch" path = "src/main.rs" [dependencies] -async-std = { version = "1.5.0", features = ["attributes"] } +# async-std = { version = "1.5.0", features = ["attributes"] } chrono = { version = "0.4.11", features = ["serde"] } crossbeam-channel = "0.4.2" env_logger = "0.7.1" @@ -38,11 +38,15 @@ sha2 = "0.8.1" siphasher = "0.3.2" structopt = "0.3.12" sysinfo = "0.12.0" -tide = "0.6.0" +# tide = "0.6.0" ureq = { version = "0.12.0", features = ["tls"], default-features = false } walkdir = "2.3.1" whoami = "0.8.1" slice-group-by = "0.2.6" +actix-rt = "1" +actix-web = "2" +actix-http = "1" +tokio = { version = "0.2.0", features = ["macros"] } [dev-dependencies] http-service = "0.4.0" diff --git a/meilisearch-http/src/data.rs b/meilisearch-http/src/data.rs index 5d05c02dd..3132a8dcc 100644 --- a/meilisearch-http/src/data.rs +++ b/meilisearch-http/src/data.rs @@ -10,7 +10,7 @@ use sha2::Digest; use sysinfo::Pid; use crate::option::Opt; -use crate::routes::index::index_update_callback; +// use crate::routes::index::index_update_callback; const LAST_UPDATE_KEY: &str = "last-update"; @@ -155,7 +155,7 @@ impl Data { let callback_context = data.clone(); db.set_update_callback(Box::new(move |index_uid, status| { - index_update_callback(&index_uid, &callback_context, status); + // index_update_callback(&index_uid, &callback_context, status); })); data diff --git a/meilisearch-http/src/error.rs b/meilisearch-http/src/error.rs index a4120b2ee..bd7260f3d 100644 --- a/meilisearch-http/src/error.rs +++ b/meilisearch-http/src/error.rs @@ -1,16 +1,14 @@ -use std::fmt::Display; - -use http::status::StatusCode; -use log::{error, warn}; +use std::fmt; use meilisearch_core::{FstError, HeedError}; -use serde::{Deserialize, Serialize}; -use tide::IntoResponse; -use tide::Response; +use serde_json::json; +use actix_http::{ResponseBuilder, Response}; +use actix_web::http::StatusCode; +use actix_web::*; +use futures::future::{ok, Ready}; -use crate::helpers::meilisearch::Error as SearchError; - -pub type SResult = Result; +// use crate::helpers::meilisearch::Error as SearchError; +#[derive(Debug)] pub enum ResponseError { Internal(String), BadRequest(String), @@ -23,169 +21,113 @@ pub enum ResponseError { BadParameter(String, String), OpenIndex(String), CreateIndex(String), + CreateTransaction, + CommitTransaction, + Schema, + InferPrimaryKey, InvalidIndexUid, Maintenance, } -impl ResponseError { - pub fn internal(message: impl Display) -> ResponseError { - ResponseError::Internal(message.to_string()) - } - - pub fn bad_request(message: impl Display) -> ResponseError { - ResponseError::BadRequest(message.to_string()) - } - - pub fn invalid_token(message: impl Display) -> ResponseError { - ResponseError::InvalidToken(message.to_string()) - } - - pub fn not_found(message: impl Display) -> ResponseError { - ResponseError::NotFound(message.to_string()) - } - - pub fn index_not_found(message: impl Display) -> ResponseError { - ResponseError::IndexNotFound(message.to_string()) - } - - pub fn document_not_found(message: impl Display) -> ResponseError { - ResponseError::DocumentNotFound(message.to_string()) - } - - pub fn missing_header(message: impl Display) -> ResponseError { - ResponseError::MissingHeader(message.to_string()) - } - - pub fn bad_parameter(name: impl Display, message: impl Display) -> ResponseError { - ResponseError::BadParameter(name.to_string(), message.to_string()) - } - - pub fn open_index(message: impl Display) -> ResponseError { - ResponseError::OpenIndex(message.to_string()) - } - - pub fn create_index(message: impl Display) -> ResponseError { - ResponseError::CreateIndex(message.to_string()) - } -} - -impl IntoResponse for ResponseError { - fn into_response(self) -> Response { +impl fmt::Display for ResponseError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - ResponseError::Internal(err) => { - error!("internal server error: {}", err); - error("Internal server error".to_string(), - StatusCode::INTERNAL_SERVER_ERROR, - ) - } - ResponseError::FilterParsing(err) => { - warn!("error paring filter: {}", err); - error(format!("parsing error: {}", err), - StatusCode::BAD_REQUEST) - } - ResponseError::BadRequest(err) => { - warn!("bad request: {}", err); - error(err, StatusCode::BAD_REQUEST) - } - ResponseError::InvalidToken(err) => { - error(format!("Invalid API key: {}", err), StatusCode::FORBIDDEN) - } - ResponseError::NotFound(err) => error(err, StatusCode::NOT_FOUND), - ResponseError::IndexNotFound(index) => { - error(format!("Index {} not found", index), StatusCode::NOT_FOUND) - } - ResponseError::DocumentNotFound(id) => error( - format!("Document with id {} not found", id), - StatusCode::NOT_FOUND, - ), - ResponseError::MissingHeader(header) => error( - format!("Header {} is missing", header), - StatusCode::UNAUTHORIZED, - ), - ResponseError::BadParameter(param, e) => error( - format!("Url parameter {} error: {}", param, e), - StatusCode::BAD_REQUEST, - ), - ResponseError::CreateIndex(err) => error( - format!("Impossible to create index; {}", err), - StatusCode::BAD_REQUEST, - ), - ResponseError::OpenIndex(err) => error( - format!("Impossible to open index; {}", err), - StatusCode::BAD_REQUEST, - ), - ResponseError::InvalidIndexUid => error( - "Index must have a valid uid; Index uid can be of type integer or string only composed of alphanumeric characters, hyphens (-) and underscores (_).".to_string(), - StatusCode::BAD_REQUEST, - ), - ResponseError::Maintenance => error( - String::from("Server is in maintenance, please try again later"), - StatusCode::SERVICE_UNAVAILABLE, - ), + Self::Internal(err) => write!(f, "Internal server error: {}", err), + Self::BadRequest(err) => write!(f, "Bad request: {}", err), + Self::InvalidToken(err) => write!(f, "Invalid API key: {}", err), + Self::NotFound(err) => write!(f, "{} not found", err), + Self::IndexNotFound(index_uid) => write!(f, "Index {} not found", index_uid), + Self::DocumentNotFound(document_id) => write!(f, "Document with id {} not found", document_id), + Self::MissingHeader(header) => write!(f, "Header {} is missing", header), + Self::BadParameter(param, err) => write!(f, "Url parameter {} error: {}", param, err), + Self::OpenIndex(err) => write!(f, "Impossible to open index; {}", err), + Self::CreateIndex(err) => write!(f, "Impossible to create index; {}", err), + Self::CreateTransaction => write!(f, "Impossible to create transaction"), + Self::CommitTransaction => write!(f, "Impossible to commit transaction"), + Self::Schema => write!(f, "Internal schema is innaccessible"), + Self::InferPrimaryKey => write!(f, "Could not infer primary key"), + Self::InvalidIndexUid => write!(f, "Index must have a valid uid; Index uid can be of type integer or string only composed of alphanumeric characters, hyphens (-) and underscores (_)."), + Self::Maintenance => write!(f, "Server is in maintenance, please try again later"), + Self::FilterParsing(err) => write!(f, "parsing error: {}", err), } } } -#[derive(Serialize, Deserialize)] -struct ErrorMessage { - message: String, +impl error::ResponseError for ResponseError { + fn error_response(&self) -> HttpResponse { + ResponseBuilder::new(self.status_code()).json(json!({ + "message": self.to_string(), + })) + } + + fn status_code(&self) -> StatusCode { + match *self { + Self::Internal(_) => StatusCode::INTERNAL_SERVER_ERROR, + Self::BadRequest(_) => StatusCode::BAD_REQUEST, + Self::InvalidToken(_) => StatusCode::FORBIDDEN, + Self::NotFound(_) => StatusCode::NOT_FOUND, + Self::IndexNotFound(_) => StatusCode::NOT_FOUND, + Self::DocumentNotFound(_) => StatusCode::NOT_FOUND, + Self::MissingHeader(_) => StatusCode::UNAUTHORIZED, + Self::BadParameter(_, _) => StatusCode::BAD_REQUEST, + Self::OpenIndex(_) => StatusCode::BAD_REQUEST, + Self::CreateIndex(_) => StatusCode::BAD_REQUEST, + Self::CreateTransaction => StatusCode::INTERNAL_SERVER_ERROR, + Self::CommitTransaction => StatusCode::INTERNAL_SERVER_ERROR, + Self::Schema => StatusCode::INTERNAL_SERVER_ERROR, + Self::InferPrimaryKey => StatusCode::BAD_REQUEST, + Self::InvalidIndexUid => StatusCode::BAD_REQUEST, + Self::Maintenance => StatusCode::SERVICE_UNAVAILABLE, + Self::FilterParsing(_) => StatusCode::BAD_REQUEST, + } + } } -fn error(message: String, status: StatusCode) -> Response { - let message = ErrorMessage { message }; - tide::Response::new(status.as_u16()) - .body_json(&message) - .unwrap() -} +// impl Responder for ResponseError { +// type Error = Error; +// type Future = Ready>; + +// #[inline] +// fn respond_to(self, req: &HttpRequest) -> Self::Future { +// ok(self.error_response()) +// } +// } impl From for ResponseError { fn from(err: serde_json::Error) -> ResponseError { - ResponseError::internal(err) + ResponseError::Internal(err.to_string()) } } impl From for ResponseError { fn from(err: meilisearch_core::Error) -> ResponseError { - ResponseError::internal(err) + ResponseError::Internal(err.to_string()) } } impl From for ResponseError { fn from(err: HeedError) -> ResponseError { - ResponseError::internal(err) + ResponseError::Internal(err.to_string()) } } impl From for ResponseError { fn from(err: FstError) -> ResponseError { - ResponseError::internal(err) + ResponseError::Internal(err.to_string()) } } -impl From for ResponseError { - fn from(err: SearchError) -> ResponseError { - match err { - SearchError::FilterParsing(s) => ResponseError::FilterParsing(s), - _ => ResponseError::internal(err), - } - } -} +// impl From for ResponseError { +// fn from(err: SearchError) -> ResponseError { +// match err { +// SearchError::FilterParsing(s) => ResponseError::FilterParsing(s), +// _ => ResponseError::Internal(err), +// } +// } +// } impl From for ResponseError { fn from(err: meilisearch_core::settings::RankingRuleConversionError) -> ResponseError { - ResponseError::internal(err) - } -} - -pub trait IntoInternalError { - fn into_internal_error(self) -> SResult; -} - -impl IntoInternalError for Option { - fn into_internal_error(self) -> SResult { - match self { - Some(value) => Ok(value), - None => Err(ResponseError::internal("Heed cannot find requested value")), - } + ResponseError::Internal(err.to_string()) } } diff --git a/meilisearch-http/src/helpers/mod.rs b/meilisearch-http/src/helpers/mod.rs index 640453b8a..f42cb57c3 100644 --- a/meilisearch-http/src/helpers/mod.rs +++ b/meilisearch-http/src/helpers/mod.rs @@ -1,2 +1,2 @@ -pub mod meilisearch; -pub mod tide; +// pub mod meilisearch; +// pub mod tide; diff --git a/meilisearch-http/src/main.rs b/meilisearch-http/src/main.rs index 0703e65c9..7f44bf954 100644 --- a/meilisearch-http/src/main.rs +++ b/meilisearch-http/src/main.rs @@ -1,16 +1,14 @@ use std::{env, thread}; -use async_std::task; use log::info; use main_error::MainError; use structopt::StructOpt; -use tide::middleware::{Cors, RequestLogger, Origin}; -use http::header::HeaderValue; - +use actix_web::middleware::Logger; +use actix_web::{post, web, App, HttpServer, HttpResponse, Responder}; use meilisearch_http::data::Data; use meilisearch_http::option::Opt; use meilisearch_http::routes; -use meilisearch_http::routes::index::index_update_callback; +// use meilisearch_http::routes::index::index_update_callback; mod analytics; @@ -18,8 +16,11 @@ mod analytics; #[global_allocator] static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; -pub fn main() -> Result<(), MainError> { +#[tokio::main] +async fn main() -> Result<(), MainError> { let opt = Opt::from_args(); + let local = tokio::task::LocalSet::new(); + let _sys = actix_rt::System::run_in_tokio("server", &local); match opt.env.as_ref() { "production" => { @@ -29,8 +30,7 @@ pub fn main() -> Result<(), MainError> { .into(), ); } - env_logger::init(); - } + }, "development" => { env_logger::from_env(env_logger::Env::default().default_filter_or("info")).init(); } @@ -45,22 +45,27 @@ pub fn main() -> Result<(), MainError> { let data_cloned = data.clone(); data.db.set_update_callback(Box::new(move |name, status| { - index_update_callback(name, &data_cloned, status); + // index_update_callback(name, &data_cloned, status); })); print_launch_resume(&opt, &data); - let mut app = tide::with_state(data); + HttpServer::new(move || + App::new() + .wrap(Logger::default()) + .app_data(web::Data::new(data.clone())) + .service(routes::document::get_document) + .service(routes::document::delete_document) + .service(routes::document::get_all_documents) + .service(routes::document::add_documents) + .service(routes::document::update_documents) + .service(routes::document::delete_documents) + .service(routes::document::clear_all_documents) + ) + .bind(opt.http_addr)? + .run() + .await?; - app.middleware(Cors::new() - .allow_methods(HeaderValue::from_static("GET, POST, PUT, DELETE, OPTIONS")) - .allow_headers(HeaderValue::from_static("X-Meili-API-Key")) - .allow_origin(Origin::from("*"))); - app.middleware(RequestLogger::new()); - - routes::load_routes(&mut app); - - task::block_on(app.listen(opt.http_addr))?; Ok(()) } @@ -76,7 +81,7 @@ pub fn print_launch_resume(opt: &Opt, data: &Data) { 888 888 "Y8888 888 888 888 "Y8888P" "Y8888 "Y888888 888 "Y8888P 888 888 "#; - println!("{}", ascii_name); + info!("{}", ascii_name); info!("Database path: {:?}", opt.db_path); info!("Start server on: {:?}", opt.http_addr); diff --git a/meilisearch-http/src/routes/document.rs b/meilisearch-http/src/routes/document.rs index 4babe530f..a28f9a73c 100644 --- a/meilisearch-http/src/routes/document.rs +++ b/meilisearch-http/src/routes/document.rs @@ -1,111 +1,104 @@ -use std::collections::{BTreeSet, HashSet}; - +use std::collections::BTreeSet; use indexmap::IndexMap; -use serde::{Deserialize, Serialize}; +use serde::Deserialize; use serde_json::Value; -use tide::{Request, Response}; +use actix_web::*; -use crate::error::{ResponseError, SResult}; -use crate::helpers::tide::RequestExt; -use crate::helpers::tide::ACL::*; +use crate::error::ResponseError; use crate::Data; +use crate::routes::IndexUpdateResponse; -pub async fn get_document(ctx: Request) -> SResult { - ctx.is_allowed(Public)?; +type Document = IndexMap; - let index = ctx.index()?; +#[get("/indexes/{index_uid}/documents/{document_id}")] +pub async fn get_document( + data: web::Data, + path: web::Path<(String, String)>, +) -> Result { + let index = data.db.open_index(&path.0) + .ok_or(ResponseError::IndexNotFound(path.0.clone()))?; + let document_id = meilisearch_core::serde::compute_document_id(path.1.clone()); - let original_document_id = ctx.document_id()?; - let document_id = meilisearch_core::serde::compute_document_id(original_document_id.clone()); + let reader = data.db.main_read_txn() + .map_err(|_| ResponseError::CreateTransaction)?; - let db = &ctx.state().db; - let reader = db.main_read_txn()?; + let response = index.document::(&reader, None, document_id) + .map_err(|_| ResponseError::DocumentNotFound(path.1.clone()))? + .ok_or(ResponseError::DocumentNotFound(path.1.clone()))?; - let response = index - .document::>(&reader, None, document_id)? - .ok_or(ResponseError::document_not_found(&original_document_id))?; - - if response.is_empty() { - return Err(ResponseError::document_not_found(&original_document_id)); - } - - Ok(tide::Response::new(200).body_json(&response)?) + Ok(HttpResponse::Ok().json(response)) } -#[derive(Default, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct IndexUpdateResponse { - pub update_id: u64, -} +#[delete("/indexes/{index_uid}/documents/{document_id}")] +pub async fn delete_document( + data: web::Data, + path: web::Path<(String, String)>, +) -> Result { + let index = data.db.open_index(&path.0) + .ok_or(ResponseError::IndexNotFound(path.0.clone()))?; + let document_id = meilisearch_core::serde::compute_document_id(path.1.clone()); -pub async fn delete_document(ctx: Request) -> SResult { - ctx.is_allowed(Private)?; + let mut update_writer = data.db.update_write_txn() + .map_err(|_| ResponseError::CreateTransaction)?; - let index = ctx.index()?; - let document_id = ctx.document_id()?; - let document_id = meilisearch_core::serde::compute_document_id(document_id); - let db = &ctx.state().db; - let mut update_writer = db.update_write_txn()?; let mut documents_deletion = index.documents_deletion(); documents_deletion.delete_document_by_id(document_id); - let update_id = documents_deletion.finalize(&mut update_writer)?; - update_writer.commit()?; + let update_id = documents_deletion.finalize(&mut update_writer) + .map_err(|_| ResponseError::Internal(path.1.clone()))?; - let response_body = IndexUpdateResponse { update_id }; - Ok(tide::Response::new(202).body_json(&response_body)?) + update_writer.commit() + .map_err(|_| ResponseError::CommitTransaction)?; + + Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } + #[derive(Default, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] -struct BrowseQuery { +pub struct BrowseQuery { offset: Option, limit: Option, attributes_to_retrieve: Option, } -pub async fn get_all_documents(ctx: Request) -> SResult { - ctx.is_allowed(Private)?; +#[get("/indexes/{index_uid}/documents")] +pub async fn get_all_documents( + data: web::Data, + path: web::Path, + params: web::Query, +) -> Result { - let index = ctx.index()?; - let query: BrowseQuery = ctx.query().unwrap_or_default(); + let index = data.db.open_index(path.clone()) + .ok_or(ResponseError::IndexNotFound(path.clone()))?; - let offset = query.offset.unwrap_or(0); - let limit = query.limit.unwrap_or(20); + let offset = params.offset.unwrap_or(0); + let limit = params.limit.unwrap_or(20); - let db = &ctx.state().db; - let reader = db.main_read_txn()?; + let reader = data.db.main_read_txn() + .map_err(|_| ResponseError::CreateTransaction)?; let documents_ids: Result, _> = index .documents_fields_counts - .documents_ids(&reader)? + .documents_ids(&reader) + .map_err(|_| ResponseError::Internal(path.clone()))? .skip(offset) .take(limit) .collect(); - let documents_ids = match documents_ids { - Ok(documents_ids) => documents_ids, - Err(e) => return Err(ResponseError::internal(e)), - }; + let documents_ids = documents_ids.map_err(|err| ResponseError::Internal(err.to_string()))?; + + let attributes = params.attributes_to_retrieve.clone() + .map(|a| a.split(',').map(|a| a.to_string()).collect()); let mut response_body = Vec::>::new(); - - if let Some(attributes) = query.attributes_to_retrieve { - let attributes = attributes.split(',').collect::>(); - for document_id in documents_ids { - if let Ok(Some(document)) = index.document(&reader, Some(&attributes), document_id) { - response_body.push(document); - } - } - } else { - for document_id in documents_ids { - if let Ok(Some(document)) = index.document(&reader, None, document_id) { - response_body.push(document); - } + for document_id in documents_ids { + if let Ok(Some(document)) = index.document(&reader, attributes.clone(), document_id) { + response_body.push(document); } } - Ok(tide::Response::new(200).body_json(&response_body)?) + Ok(HttpResponse::Ok().json(response_body)) } fn find_primary_key(document: &IndexMap) -> Option { @@ -119,40 +112,49 @@ fn find_primary_key(document: &IndexMap) -> Option { #[derive(Default, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] -struct UpdateDocumentsQuery { +pub struct UpdateDocumentsQuery { primary_key: Option, } -async fn update_multiple_documents(mut ctx: Request, is_partial: bool) -> SResult { - ctx.is_allowed(Private)?; +async fn update_multiple_documents( + data: web::Data, + path: web::Path, + params: web::Query, + body: web::Json>, + is_partial: bool +) -> Result { - let index = ctx.index()?; + let index = data.db.open_index(path.clone()) + .ok_or(ResponseError::IndexNotFound(path.clone()))?; - let data: Vec> = - ctx.body_json().await.map_err(ResponseError::bad_request)?; - let query: UpdateDocumentsQuery = ctx.query().unwrap_or_default(); + let reader = data.db.main_read_txn() + .map_err(|_| ResponseError::CreateTransaction)?; - let db = &ctx.state().db; - - let reader = db.main_read_txn()?; let mut schema = index .main - .schema(&reader)? - .ok_or(ResponseError::internal("schema not found"))?; + .schema(&reader) + .map_err(|_| ResponseError::Schema)? + .ok_or(ResponseError::Schema)?; if schema.primary_key().is_none() { - let id = match query.primary_key { + let id = match params.primary_key.clone() { Some(id) => id, - None => match data.first().and_then(|docs| find_primary_key(docs)) { - Some(id) => id, - None => return Err(ResponseError::bad_request("Could not infer a primary key")), - }, + None => { + body.first() + .and_then(|docs| find_primary_key(docs)) + .ok_or(ResponseError::InferPrimaryKey)? + } }; - let mut writer = db.main_write_txn()?; - schema.set_primary_key(&id).map_err(ResponseError::bad_request)?; - index.main.put_schema(&mut writer, &schema)?; - writer.commit()?; + let mut writer = data.db.main_write_txn() + .map_err(|_| ResponseError::CreateTransaction)?; + + schema.set_primary_key(&id) + .map_err(|e| ResponseError::Internal(e.to_string()))?; + index.main.put_schema(&mut writer, &schema) + .map_err(|e| ResponseError::Internal(e.to_string()))?; + writer.commit() + .map_err(|_| ResponseError::CommitTransaction)?; } let mut document_addition = if is_partial { @@ -161,63 +163,88 @@ async fn update_multiple_documents(mut ctx: Request, is_partial: bool) -> index.documents_addition() }; - for document in data { + for document in body.into_inner() { document_addition.update_document(document); } - let mut update_writer = db.update_write_txn()?; - let update_id = document_addition.finalize(&mut update_writer)?; - update_writer.commit()?; + let mut update_writer = data.db.update_write_txn() + .map_err(|_| ResponseError::CreateTransaction)?; + let update_id = document_addition.finalize(&mut update_writer) + .map_err(|e| ResponseError::Internal(e.to_string()))?; + update_writer.commit() + .map_err(|_| ResponseError::CommitTransaction)?; - let response_body = IndexUpdateResponse { update_id }; - Ok(tide::Response::new(202).body_json(&response_body)?) + Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } -pub async fn add_or_replace_multiple_documents(ctx: Request) -> SResult { - update_multiple_documents(ctx, false).await +#[post("/indexes/{index_uid}/documents")] +pub async fn add_documents( + data: web::Data, + path: web::Path, + params: web::Query, + body: web::Json> +) -> Result { + update_multiple_documents(data, path, params, body, false).await } -pub async fn add_or_update_multiple_documents(ctx: Request) -> SResult { - update_multiple_documents(ctx, true).await +#[put("/indexes/{index_uid}/documents")] +pub async fn update_documents( + data: web::Data, + path: web::Path, + params: web::Query, + body: web::Json> +) -> Result { + update_multiple_documents(data, path, params, body, true).await } -pub async fn delete_multiple_documents(mut ctx: Request) -> SResult { - ctx.is_allowed(Private)?; +#[post("/indexes/{index_uid}/documents/delete-batch")] +pub async fn delete_documents( + data: web::Data, + path: web::Path, + body: web::Json> +) -> Result { - let data: Vec = ctx.body_json().await.map_err(ResponseError::bad_request)?; - let index = ctx.index()?; + let index = data.db.open_index(path.clone()) + .ok_or(ResponseError::IndexNotFound(path.clone()))?; - let db = &ctx.state().db; - let mut writer = db.update_write_txn()?; + let mut writer = data.db.update_write_txn() + .map_err(|_| ResponseError::CreateTransaction)?; let mut documents_deletion = index.documents_deletion(); - for document_id in data { + for document_id in body.into_inner() { if let Some(document_id) = meilisearch_core::serde::value_to_string(&document_id) { documents_deletion .delete_document_by_id(meilisearch_core::serde::compute_document_id(document_id)); } } - let update_id = documents_deletion.finalize(&mut writer)?; + let update_id = documents_deletion.finalize(&mut writer) + .map_err(|e| ResponseError::Internal(e.to_string()))?; - writer.commit()?; + writer.commit() + .map_err(|_| ResponseError::CommitTransaction)?; - let response_body = IndexUpdateResponse { update_id }; - Ok(tide::Response::new(202).body_json(&response_body)?) + Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } -pub async fn clear_all_documents(ctx: Request) -> SResult { - ctx.is_allowed(Private)?; +#[delete("/indexes/{index_uid}/documents")] +pub async fn clear_all_documents( + data: web::Data, + path: web::Path, +) -> Result { - let index = ctx.index()?; + let index = data.db.open_index(path.clone()) + .ok_or(ResponseError::IndexNotFound(path.clone()))?; - let db = &ctx.state().db; - let mut writer = db.update_write_txn()?; + let mut writer = data.db.update_write_txn() + .map_err(|_| ResponseError::CreateTransaction)?; - let update_id = index.clear_all(&mut writer)?; - writer.commit()?; + let update_id = index.clear_all(&mut writer) + .map_err(|e| ResponseError::Internal(e.to_string()))?; - let response_body = IndexUpdateResponse { update_id }; - Ok(tide::Response::new(202).body_json(&response_body)?) + writer.commit() + .map_err(|_| ResponseError::CommitTransaction)?; + + Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } diff --git a/meilisearch-http/src/routes/mod.rs b/meilisearch-http/src/routes/mod.rs index 1d5942a08..65e09c490 100644 --- a/meilisearch-http/src/routes/mod.rs +++ b/meilisearch-http/src/routes/mod.rs @@ -1,130 +1,135 @@ -use crate::data::Data; -use std::future::Future; -use tide::IntoResponse; -use tide::Response; + +use serde::Serialize; pub mod document; -pub mod health; -pub mod index; -pub mod key; -pub mod search; -pub mod setting; -pub mod stats; -pub mod stop_words; -pub mod synonym; +// pub mod health; +// pub mod index; +// pub mod key; +// pub mod search; +// pub mod setting; +// pub mod stats; +// pub mod stop_words; +// pub mod synonym; -async fn into_response( - x: impl Future>, -) -> Response { - match x.await { - Ok(resp) => resp.into_response(), - Err(resp) => resp.into_response(), +#[derive(Default, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct IndexUpdateResponse { + pub update_id: u64, + pub see_more: String, +} + +impl IndexUpdateResponse { + pub fn with_id(update_id: u64) -> Self { + Self { + update_id, + see_more: "https://docs.meilisearch.com/guides/advanced_guides/asynchronous_updates.html".to_string() + } } } -pub fn load_routes(app: &mut tide::Server) { - app.at("/").get(|_| async { - tide::Response::new(200) - .body_string(include_str!("../../public/interface.html").to_string()) - .set_mime(mime::TEXT_HTML_UTF_8) - }); - app.at("/bulma.min.css").get(|_| async { - tide::Response::new(200) - .body_string(include_str!("../../public/bulma.min.css").to_string()) - .set_mime(mime::TEXT_CSS_UTF_8) - }); +// pub fn load_routes(app: &mut tide::Server) { +// app.at("/").get(|_| async { +// tide::Response::new(200) +// .body_string(include_str!("../../public/interface.html").to_string()) +// .set_mime(mime::TEXT_HTML_UTF_8) +// }); +// app.at("/bulma.min.css").get(|_| async { +// tide::Response::new(200) +// .body_string(include_str!("../../public/bulma.min.css").to_string()) +// .set_mime(mime::TEXT_CSS_UTF_8) +// }); - app.at("/indexes") - .get(|ctx| into_response(index::list_indexes(ctx))) - .post(|ctx| into_response(index::create_index(ctx))); +// app.at("/indexes") +// .get(|ctx| into_response(index::list_indexes(ctx))) +// .post(|ctx| into_response(index::create_index(ctx))); - app.at("/indexes/search") - .post(|ctx| into_response(search::search_multi_index(ctx))); +// app.at("/indexes/search") +// .post(|ctx| into_response(search::search_multi_index(ctx))); - app.at("/indexes/:index") - .get(|ctx| into_response(index::get_index(ctx))) - .put(|ctx| into_response(index::update_index(ctx))) - .delete(|ctx| into_response(index::delete_index(ctx))); +// app.at("/indexes/:index") +// .get(|ctx| into_response(index::get_index(ctx))) +// .put(|ctx| into_response(index::update_index(ctx))) +// .delete(|ctx| into_response(index::delete_index(ctx))); - app.at("/indexes/:index/search") - .get(|ctx| into_response(search::search_with_url_query(ctx))); +// app.at("/indexes/:index/search") +// .get(|ctx| into_response(search::search_with_url_query(ctx))); - app.at("/indexes/:index/updates") - .get(|ctx| into_response(index::get_all_updates_status(ctx))); +// app.at("/indexes/:index/updates") +// .get(|ctx| into_response(index::get_all_updates_status(ctx))); - app.at("/indexes/:index/updates/:update_id") - .get(|ctx| into_response(index::get_update_status(ctx))); +// app.at("/indexes/:index/updates/:update_id") +// .get(|ctx| into_response(index::get_update_status(ctx))); - app.at("/indexes/:index/documents") - .get(|ctx| into_response(document::get_all_documents(ctx))) - .post(|ctx| into_response(document::add_or_replace_multiple_documents(ctx))) - .put(|ctx| into_response(document::add_or_update_multiple_documents(ctx))) - .delete(|ctx| into_response(document::clear_all_documents(ctx))); +// app.at("/indexes/:index/documents") +// .get(|ctx| into_response(document::get_all_documents(ctx))) +// .post(|ctx| into_response(document::add_or_replace_multiple_documents(ctx))) +// .put(|ctx| into_response(document::add_or_update_multiple_documents(ctx))) +// .delete(|ctx| into_response(document::clear_all_documents(ctx))); - app.at("/indexes/:index/documents/:document_id") - .get(|ctx| into_response(document::get_document(ctx))) - .delete(|ctx| into_response(document::delete_document(ctx))); +// app.at("/indexes/:index/documents/:document_id") +// .get(|ctx| into_response(document::get_document(ctx))) +// .delete(|ctx| into_response(document::delete_document(ctx))); - app.at("/indexes/:index/documents/delete-batch") - .post(|ctx| into_response(document::delete_multiple_documents(ctx))); +// app.at("/indexes/:index/documents/delete-batch") +// .post(|ctx| into_response(document::delete_multiple_documents(ctx))); - app.at("/indexes/:index/settings") - .get(|ctx| into_response(setting::get_all(ctx))) - .post(|ctx| into_response(setting::update_all(ctx))) - .delete(|ctx| into_response(setting::delete_all(ctx))); +// app.at("/indexes/:index/settings") +// .get(|ctx| into_response(setting::get_all(ctx))) +// .post(|ctx| into_response(setting::update_all(ctx))) +// .delete(|ctx| into_response(setting::delete_all(ctx))); - app.at("/indexes/:index/settings/ranking-rules") - .get(|ctx| into_response(setting::get_rules(ctx))) - .post(|ctx| into_response(setting::update_rules(ctx))) - .delete(|ctx| into_response(setting::delete_rules(ctx))); +// app.at("/indexes/:index/settings/ranking-rules") +// .get(|ctx| into_response(setting::get_rules(ctx))) +// .post(|ctx| into_response(setting::update_rules(ctx))) +// .delete(|ctx| into_response(setting::delete_rules(ctx))); - app.at("/indexes/:index/settings/distinct-attribute") - .get(|ctx| into_response(setting::get_distinct(ctx))) - .post(|ctx| into_response(setting::update_distinct(ctx))) - .delete(|ctx| into_response(setting::delete_distinct(ctx))); +// app.at("/indexes/:index/settings/distinct-attribute") +// .get(|ctx| into_response(setting::get_distinct(ctx))) +// .post(|ctx| into_response(setting::update_distinct(ctx))) +// .delete(|ctx| into_response(setting::delete_distinct(ctx))); - app.at("/indexes/:index/settings/searchable-attributes") - .get(|ctx| into_response(setting::get_searchable(ctx))) - .post(|ctx| into_response(setting::update_searchable(ctx))) - .delete(|ctx| into_response(setting::delete_searchable(ctx))); +// app.at("/indexes/:index/settings/searchable-attributes") +// .get(|ctx| into_response(setting::get_searchable(ctx))) +// .post(|ctx| into_response(setting::update_searchable(ctx))) +// .delete(|ctx| into_response(setting::delete_searchable(ctx))); - app.at("/indexes/:index/settings/displayed-attributes") - .get(|ctx| into_response(setting::displayed(ctx))) - .post(|ctx| into_response(setting::update_displayed(ctx))) - .delete(|ctx| into_response(setting::delete_displayed(ctx))); +// app.at("/indexes/:index/settings/displayed-attributes") +// .get(|ctx| into_response(setting::displayed(ctx))) +// .post(|ctx| into_response(setting::update_displayed(ctx))) +// .delete(|ctx| into_response(setting::delete_displayed(ctx))); - app.at("/indexes/:index/settings/accept-new-fields") - .get(|ctx| into_response(setting::get_accept_new_fields(ctx))) - .post(|ctx| into_response(setting::update_accept_new_fields(ctx))); +// app.at("/indexes/:index/settings/accept-new-fields") +// .get(|ctx| into_response(setting::get_accept_new_fields(ctx))) +// .post(|ctx| into_response(setting::update_accept_new_fields(ctx))); - app.at("/indexes/:index/settings/synonyms") - .get(|ctx| into_response(synonym::get(ctx))) - .post(|ctx| into_response(synonym::update(ctx))) - .delete(|ctx| into_response(synonym::delete(ctx))); +// app.at("/indexes/:index/settings/synonyms") +// .get(|ctx| into_response(synonym::get(ctx))) +// .post(|ctx| into_response(synonym::update(ctx))) +// .delete(|ctx| into_response(synonym::delete(ctx))); - app.at("/indexes/:index/settings/stop-words") - .get(|ctx| into_response(stop_words::get(ctx))) - .post(|ctx| into_response(stop_words::update(ctx))) - .delete(|ctx| into_response(stop_words::delete(ctx))); +// app.at("/indexes/:index/settings/stop-words") +// .get(|ctx| into_response(stop_words::get(ctx))) +// .post(|ctx| into_response(stop_words::update(ctx))) +// .delete(|ctx| into_response(stop_words::delete(ctx))); - app.at("/indexes/:index/stats") - .get(|ctx| into_response(stats::index_stats(ctx))); +// app.at("/indexes/:index/stats") +// .get(|ctx| into_response(stats::index_stats(ctx))); - app.at("/keys").get(|ctx| into_response(key::list(ctx))); +// app.at("/keys").get(|ctx| into_response(key::list(ctx))); - app.at("/health") - .get(|ctx| into_response(health::get_health(ctx))) - .put(|ctx| into_response(health::change_healthyness(ctx))); +// app.at("/health") +// .get(|ctx| into_response(health::get_health(ctx))) +// .put(|ctx| into_response(health::change_healthyness(ctx))); - app.at("/stats") - .get(|ctx| into_response(stats::get_stats(ctx))); +// app.at("/stats") +// .get(|ctx| into_response(stats::get_stats(ctx))); - app.at("/version") - .get(|ctx| into_response(stats::get_version(ctx))); +// app.at("/version") +// .get(|ctx| into_response(stats::get_version(ctx))); - app.at("/sys-info") - .get(|ctx| into_response(stats::get_sys_info(ctx))); +// app.at("/sys-info") +// .get(|ctx| into_response(stats::get_sys_info(ctx))); - app.at("/sys-info/pretty") - .get(|ctx| into_response(stats::get_sys_info_pretty(ctx))); -} +// app.at("/sys-info/pretty") +// .get(|ctx| into_response(stats::get_sys_info_pretty(ctx))); +// } From 540308dc63605a6098140082821d18996c868958 Mon Sep 17 00:00:00 2001 From: Quentin de Quelen Date: Tue, 7 Apr 2020 18:30:38 +0200 Subject: [PATCH 02/21] add interface endpoint & health endpoint --- Cargo.lock | 86 +++++++++++++++++++++++++-- meilisearch-http/Cargo.toml | 1 + meilisearch-http/src/main.rs | 6 +- meilisearch-http/src/routes/health.rs | 74 +++++++++++++---------- meilisearch-http/src/routes/mod.rs | 19 +++++- 5 files changed, 146 insertions(+), 40 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9c3988d17..b224ef1e5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -34,6 +34,26 @@ dependencies = [ "trust-dns-resolver", ] +[[package]] +name = "actix-files" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "301482841d3d74483a446ead63cb7d362e187d2c8b603f13d91995621ea53c46" +dependencies = [ + "actix-http", + "actix-service", + "actix-web", + "bitflags", + "bytes 0.5.4", + "derive_more", + "futures", + "log", + "mime", + "mime_guess", + "percent-encoding 2.1.0", + "v_htmlescape", +] + [[package]] name = "actix-http" version = "1.0.1" @@ -834,7 +854,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d371106cc88ffdfb1eabd7111e432da544f16f3e2d7bf1dfe8bf575f1df045cd" dependencies = [ "backtrace", - "version_check", + "version_check 0.9.1", ] [[package]] @@ -1469,6 +1489,7 @@ dependencies = [ name = "meilisearch-http" version = "0.10.1" dependencies = [ + "actix-files", "actix-http", "actix-rt", "actix-web", @@ -1557,6 +1578,16 @@ version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +[[package]] +name = "mime_guess" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2684d4c2e97d99848d30b324b00c8fcc7e5c897b7cbb5819b09e7c90e8baf212" +dependencies = [ + "mime", + "unicase", +] + [[package]] name = "miniz_oxide" version = "0.3.6" @@ -1632,6 +1663,16 @@ dependencies = [ "void", ] +[[package]] +name = "nom" +version = "4.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" +dependencies = [ + "memchr", + "version_check 0.1.5", +] + [[package]] name = "ntapi" version = "0.3.3" @@ -1871,7 +1912,7 @@ dependencies = [ "proc-macro2", "quote", "syn", - "version_check", + "version_check 0.9.1", ] [[package]] @@ -1884,7 +1925,7 @@ dependencies = [ "quote", "syn", "syn-mid", - "version_check", + "version_check 0.9.1", ] [[package]] @@ -2624,7 +2665,7 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" dependencies = [ - "version_check", + "version_check 0.9.1", ] [[package]] @@ -2702,6 +2743,37 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372" +[[package]] +name = "v_escape" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "660b101c07b5d0863deb9e7fb3138777e858d6d2a79f9e6049a27d1cc77c6da6" +dependencies = [ + "v_escape_derive", +] + +[[package]] +name = "v_escape_derive" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2ca2a14bc3fc5b64d188b087a7d3a927df87b152e941ccfbc66672e20c467ae" +dependencies = [ + "nom", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "v_htmlescape" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e33e939c0d8cf047514fb6ba7d5aac78bc56677a6938b2ee67000b91f2e97e41" +dependencies = [ + "cfg-if", + "v_escape", +] + [[package]] name = "vec_map" version = "0.8.1" @@ -2718,6 +2790,12 @@ dependencies = [ "chrono", ] +[[package]] +name = "version_check" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" + [[package]] name = "version_check" version = "0.9.1" diff --git a/meilisearch-http/Cargo.toml b/meilisearch-http/Cargo.toml index 1c716e726..e78dedc50 100644 --- a/meilisearch-http/Cargo.toml +++ b/meilisearch-http/Cargo.toml @@ -46,6 +46,7 @@ slice-group-by = "0.2.6" actix-rt = "1" actix-web = "2" actix-http = "1" +actix-files = "0.2.1" tokio = { version = "0.2.0", features = ["macros"] } [dev-dependencies] diff --git a/meilisearch-http/src/main.rs b/meilisearch-http/src/main.rs index 7f44bf954..6217c40e5 100644 --- a/meilisearch-http/src/main.rs +++ b/meilisearch-http/src/main.rs @@ -1,4 +1,4 @@ -use std::{env, thread}; +use std::{env, thread, fs}; use log::info; use main_error::MainError; @@ -54,6 +54,8 @@ async fn main() -> Result<(), MainError> { App::new() .wrap(Logger::default()) .app_data(web::Data::new(data.clone())) + .service(routes::load_html) + .service(routes::load_css) .service(routes::document::get_document) .service(routes::document::delete_document) .service(routes::document::get_all_documents) @@ -61,6 +63,8 @@ async fn main() -> Result<(), MainError> { .service(routes::document::update_documents) .service(routes::document::delete_documents) .service(routes::document::clear_all_documents) + .service(routes::health::get_health) + .service(routes::health::change_healthyness) ) .bind(opt.http_addr)? .run() diff --git a/meilisearch-http/src/routes/health.rs b/meilisearch-http/src/routes/health.rs index 66a59627a..5d6654214 100644 --- a/meilisearch-http/src/routes/health.rs +++ b/meilisearch-http/src/routes/health.rs @@ -1,60 +1,68 @@ -use crate::error::{ResponseError, SResult}; -use crate::helpers::tide::RequestExt; -use crate::helpers::tide::ACL::*; +use crate::error::ResponseError; +use actix_web::*; use crate::Data; - use heed::types::{Str, Unit}; use serde::Deserialize; -use tide::{Request, Response}; const UNHEALTHY_KEY: &str = "_is_unhealthy"; -pub async fn get_health(ctx: Request) -> SResult { - let db = &ctx.state().db; - let reader = db.main_read_txn()?; +#[get("/health")] +pub async fn get_health( + data: web::Data, +) -> Result { + let reader = data.db.main_read_txn() + .map_err(|_| ResponseError::CreateTransaction)?; - let common_store = ctx.state().db.common_store(); + let common_store = data.db.common_store(); if let Ok(Some(_)) = common_store.get::<_, Str, Unit>(&reader, UNHEALTHY_KEY) { - return Err(ResponseError::Maintenance); + return Err(ResponseError::Maintenance)?; } - Ok(tide::Response::new(200)) + Ok(HttpResponse::Ok().finish()) } -pub async fn set_healthy(ctx: Request) -> SResult { - ctx.is_allowed(Admin)?; - let db = &ctx.state().db; - let mut writer = db.main_write_txn()?; - let common_store = ctx.state().db.common_store(); - common_store.delete::<_, Str>(&mut writer, UNHEALTHY_KEY)?; - writer.commit()?; +pub async fn set_healthy( + data: web::Data, +) -> Result { + let mut writer = data.db.main_write_txn() + .map_err(|_| ResponseError::CreateTransaction)?; + let common_store = data.db.common_store(); + common_store.delete::<_, Str>(&mut writer, UNHEALTHY_KEY) + .map_err(|e| ResponseError::Internal(e.to_string()))?; + writer.commit() + .map_err(|_| ResponseError::CommitTransaction)?; - Ok(tide::Response::new(200)) + Ok(HttpResponse::Ok().finish()) } -pub async fn set_unhealthy(ctx: Request) -> SResult { - ctx.is_allowed(Admin)?; - let db = &ctx.state().db; - let mut writer = db.main_write_txn()?; - let common_store = ctx.state().db.common_store(); - common_store.put::<_, Str, Unit>(&mut writer, UNHEALTHY_KEY, &())?; - writer.commit()?; +pub async fn set_unhealthy( + data: web::Data, +) -> Result { + let mut writer = data.db.main_write_txn() + .map_err(|_| ResponseError::CreateTransaction)?; + let common_store = data.db.common_store(); + common_store.put::<_, Str, Unit>(&mut writer, UNHEALTHY_KEY, &()) + .map_err(|e| ResponseError::Internal(e.to_string()))?; + writer.commit() + .map_err(|_| ResponseError::CommitTransaction)?; - Ok(tide::Response::new(200)) + Ok(HttpResponse::Ok().finish()) } #[derive(Deserialize, Clone)] -struct HealtBody { +pub struct HealtBody { health: bool, } -pub async fn change_healthyness(mut ctx: Request) -> SResult { - let body: HealtBody = ctx.body_json().await.map_err(ResponseError::bad_request)?; - +#[put("/health")] +pub async fn change_healthyness( + data: web::Data, + body: web::Json, +) -> Result { if body.health { - set_healthy(ctx).await + set_healthy(data).await } else { - set_unhealthy(ctx).await + set_unhealthy(data).await } } diff --git a/meilisearch-http/src/routes/mod.rs b/meilisearch-http/src/routes/mod.rs index 65e09c490..b518517cc 100644 --- a/meilisearch-http/src/routes/mod.rs +++ b/meilisearch-http/src/routes/mod.rs @@ -1,8 +1,9 @@ +use actix_web::*; use serde::Serialize; pub mod document; -// pub mod health; +pub mod health; // pub mod index; // pub mod key; // pub mod search; @@ -27,10 +28,24 @@ impl IndexUpdateResponse { } } +#[get("/")] +pub async fn load_html() -> HttpResponse { + HttpResponse::Ok() + .content_type("text/html; charset=utf-8") + .body(include_str!("../../public/interface.html").to_string()) +} + +#[get("/bulma.min.css")] +pub async fn load_css() -> HttpResponse { + HttpResponse::Ok() + .content_type("text/css; charset=utf-8") + .body(include_str!("../../public/bulma.min.css").to_string()) +} + // pub fn load_routes(app: &mut tide::Server) { // app.at("/").get(|_| async { // tide::Response::new(200) -// .body_string(include_str!("../../public/interface.html").to_string()) +// .body_string() // .set_mime(mime::TEXT_HTML_UTF_8) // }); // app.at("/bulma.min.css").get(|_| async { From 0aa16dd3b1eddfda51a06e60d9c6c68decdb7e6b Mon Sep 17 00:00:00 2001 From: Quentin de Quelen Date: Tue, 7 Apr 2020 19:10:32 +0200 Subject: [PATCH 03/21] add key endpoint --- meilisearch-http/src/main.rs | 1 + meilisearch-http/src/routes/key.rs | 18 ++++++++---------- meilisearch-http/src/routes/mod.rs | 2 +- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/meilisearch-http/src/main.rs b/meilisearch-http/src/main.rs index 6217c40e5..34baae454 100644 --- a/meilisearch-http/src/main.rs +++ b/meilisearch-http/src/main.rs @@ -63,6 +63,7 @@ async fn main() -> Result<(), MainError> { .service(routes::document::update_documents) .service(routes::document::delete_documents) .service(routes::document::clear_all_documents) + .service(routes::key::list) .service(routes::health::get_health) .service(routes::health::change_healthyness) ) diff --git a/meilisearch-http/src/routes/key.rs b/meilisearch-http/src/routes/key.rs index fe0feacf4..52265bc2f 100644 --- a/meilisearch-http/src/routes/key.rs +++ b/meilisearch-http/src/routes/key.rs @@ -1,17 +1,15 @@ -use crate::error::SResult; -use crate::helpers::tide::RequestExt; -use crate::helpers::tide::ACL::*; use crate::Data; use serde_json::json; -use tide::{Request, Response}; +use actix_web::*; -pub async fn list(ctx: Request) -> SResult { - ctx.is_allowed(Admin)?; +#[get("/keys")] +pub async fn list( + data: web::Data, +) -> Result { + let keys = &data.api_keys; - let keys = &ctx.state().api_keys; - - Ok(tide::Response::new(200).body_json(&json!({ + HttpResponse::Ok().json(&json!({ "private": keys.private, "public": keys.public, - }))?) + })).await } diff --git a/meilisearch-http/src/routes/mod.rs b/meilisearch-http/src/routes/mod.rs index b518517cc..8851d26f3 100644 --- a/meilisearch-http/src/routes/mod.rs +++ b/meilisearch-http/src/routes/mod.rs @@ -5,7 +5,7 @@ use serde::Serialize; pub mod document; pub mod health; // pub mod index; -// pub mod key; +pub mod key; // pub mod search; // pub mod setting; // pub mod stats; From 73b5c87cbb020ffc6656550e3254df91c92f1319 Mon Sep 17 00:00:00 2001 From: Quentin de Quelen Date: Tue, 7 Apr 2020 19:34:57 +0200 Subject: [PATCH 04/21] add search endpoint; warn unwrap --- meilisearch-http/src/helpers/meilisearch.rs | 4 +- meilisearch-http/src/helpers/mod.rs | 2 +- meilisearch-http/src/routes/document.rs | 2 +- meilisearch-http/src/routes/mod.rs | 2 +- meilisearch-http/src/routes/search.rs | 108 ++++++++++---------- 5 files changed, 58 insertions(+), 60 deletions(-) diff --git a/meilisearch-http/src/helpers/meilisearch.rs b/meilisearch-http/src/helpers/meilisearch.rs index a925562be..d75fdcced 100644 --- a/meilisearch-http/src/helpers/meilisearch.rs +++ b/meilisearch-http/src/helpers/meilisearch.rs @@ -75,7 +75,7 @@ impl From for Error { }; let message = format!("parsing error on line {} at column {}: {}", line, column, e.variant.message()); - Error::FilterParsing(message) + Error::FilterParsing(message) }, _ => Error::Internal(error.to_string()), } @@ -257,7 +257,7 @@ impl<'a> SearchBuilder<'a> { for doc in docs { let mut document: IndexMap = self .index - .document(reader, Some(&all_attributes), doc.id) + .document(reader, Some(all_attributes.clone()), doc.id) .map_err(|e| Error::RetrieveDocument(doc.id.0, e.to_string()))? .ok_or(Error::DocumentNotFound(doc.id.0))?; diff --git a/meilisearch-http/src/helpers/mod.rs b/meilisearch-http/src/helpers/mod.rs index f42cb57c3..eb40f27e6 100644 --- a/meilisearch-http/src/helpers/mod.rs +++ b/meilisearch-http/src/helpers/mod.rs @@ -1,2 +1,2 @@ -// pub mod meilisearch; +pub mod meilisearch; // pub mod tide; diff --git a/meilisearch-http/src/routes/document.rs b/meilisearch-http/src/routes/document.rs index a28f9a73c..9c4b88142 100644 --- a/meilisearch-http/src/routes/document.rs +++ b/meilisearch-http/src/routes/document.rs @@ -45,7 +45,7 @@ pub async fn delete_document( documents_deletion.delete_document_by_id(document_id); let update_id = documents_deletion.finalize(&mut update_writer) - .map_err(|_| ResponseError::Internal(path.1.clone()))?; + .map_err(|err| ResponseError::Internal(err.to_string()))?; update_writer.commit() .map_err(|_| ResponseError::CommitTransaction)?; diff --git a/meilisearch-http/src/routes/mod.rs b/meilisearch-http/src/routes/mod.rs index 8851d26f3..adf4f0e63 100644 --- a/meilisearch-http/src/routes/mod.rs +++ b/meilisearch-http/src/routes/mod.rs @@ -6,7 +6,7 @@ pub mod document; pub mod health; // pub mod index; pub mod key; -// pub mod search; +pub mod search; // pub mod setting; // pub mod stats; // pub mod stop_words; diff --git a/meilisearch-http/src/routes/search.rs b/meilisearch-http/src/routes/search.rs index 4d547d268..2efd24580 100644 --- a/meilisearch-http/src/routes/search.rs +++ b/meilisearch-http/src/routes/search.rs @@ -6,12 +6,12 @@ use log::warn; use meilisearch_core::Index; use rayon::iter::{IntoParallelIterator, ParallelIterator}; use serde::{Deserialize, Serialize}; -use tide::{Request, Response}; +use actix_web::*; -use crate::error::{ResponseError, SResult}; -use crate::helpers::meilisearch::{Error, IndexSearchExt, SearchHit}; -use crate::helpers::tide::RequestExt; -use crate::helpers::tide::ACL::*; +use crate::error::ResponseError; +use crate::helpers::meilisearch::{Error, IndexSearchExt, SearchHit, SearchResult}; +// use crate::helpers::tide::RequestExt; +// use crate::helpers::tide::ACL::*; use crate::Data; #[derive(Deserialize)] @@ -29,34 +29,37 @@ struct SearchQuery { matches: Option, } -pub async fn search_with_url_query(ctx: Request) -> SResult { - ctx.is_allowed(Public)?; +#[get("/indexes/{index_uid}/search")] +pub async fn search_with_url_query( + data: web::Data, + path: web::Path, + params: web::Query, +) -> Result> { - let index = ctx.index()?; - let db = &ctx.state().db; - let reader = db.main_read_txn()?; + let index = data.db.open_index(path.clone()) + .ok_or(ResponseError::IndexNotFound(path.clone()))?; + + let reader = data.db.main_read_txn() + .map_err(|_| ResponseError::CreateTransaction)?; let schema = index .main - .schema(&reader)? - .ok_or(ResponseError::open_index("No Schema found"))?; + .schema(&reader) + .map_err(|_| ResponseError::Schema)? + .ok_or(ResponseError::Schema)?; - let query: SearchQuery = ctx - .query() - .map_err(|_| ResponseError::bad_request("invalid query parameter"))?; + let mut search_builder = index.new_search(params.q.clone()); - let mut search_builder = index.new_search(query.q.clone()); - - if let Some(offset) = query.offset { + if let Some(offset) = params.offset { search_builder.offset(offset); } - if let Some(limit) = query.limit { + if let Some(limit) = params.limit { search_builder.limit(limit); } let available_attributes = schema.displayed_name(); let mut restricted_attributes: HashSet<&str>; - match &query.attributes_to_retrieve { + match ¶ms.attributes_to_retrieve { Some(attributes_to_retrieve) => { let attributes_to_retrieve: HashSet<&str> = attributes_to_retrieve.split(',').collect(); if attributes_to_retrieve.contains("*") { @@ -78,8 +81,8 @@ pub async fn search_with_url_query(ctx: Request) -> SResult { } } - if let Some(attributes_to_crop) = query.attributes_to_crop { - let default_length = query.crop_length.unwrap_or(200); + if let Some(attributes_to_crop) = ¶ms.attributes_to_crop { + let default_length = params.crop_length.unwrap_or(200); let mut final_attributes: HashMap = HashMap::new(); for attribute in attributes_to_crop.split(',') { @@ -106,7 +109,7 @@ pub async fn search_with_url_query(ctx: Request) -> SResult { search_builder.attributes_to_crop(final_attributes); } - if let Some(attributes_to_highlight) = query.attributes_to_highlight { + if let Some(attributes_to_highlight) = ¶ms.attributes_to_highlight { let mut final_attributes: HashSet = HashSet::new(); for attribute in attributes_to_highlight.split(',') { if attribute == "*" { @@ -125,15 +128,15 @@ pub async fn search_with_url_query(ctx: Request) -> SResult { search_builder.attributes_to_highlight(final_attributes); } - if let Some(filters) = query.filters { - search_builder.filters(filters); + if let Some(filters) = ¶ms.filters { + search_builder.filters(filters.to_string()); } - if let Some(timeout_ms) = query.timeout_ms { + if let Some(timeout_ms) = params.timeout_ms { search_builder.timeout(Duration::from_millis(timeout_ms)); } - if let Some(matches) = query.matches { + if let Some(matches) = params.matches { if matches { search_builder.get_matches(); } @@ -141,11 +144,11 @@ pub async fn search_with_url_query(ctx: Request) -> SResult { let response = match search_builder.search(&reader) { Ok(response) => response, - Err(Error::Internal(message)) => return Err(ResponseError::Internal(message)), - Err(others) => return Err(ResponseError::bad_request(others)), + Err(Error::Internal(message)) => return Err(ResponseError::Internal(message))?, + Err(others) => return Err(ResponseError::BadRequest(others.to_string()))?, }; - Ok(tide::Response::new(200).body_json(&response).unwrap()) + Ok(web::Json(response)) } #[derive(Clone, Deserialize)] @@ -174,24 +177,24 @@ struct SearchMultiBodyResponse { query: String, } -pub async fn search_multi_index(mut ctx: Request) -> SResult { - ctx.is_allowed(Public)?; - let body = ctx - .body_json::() - .await - .map_err(ResponseError::bad_request)?; +#[post("/indexes/search")] +pub async fn search_multi_index( + data: web::Data, + body: web::Json, +) -> Result> { let mut index_list = body.clone().indexes; for index in index_list.clone() { if index == "*" { - index_list = ctx.state().db.indexes_uids().into_iter().collect(); + index_list = data.db.indexes_uids().into_iter().collect(); break; } } let mut offset = 0; let mut count = 20; + let query = body.query.clone(); if let Some(body_offset) = body.offset { if let Some(limit) = body.limit { @@ -200,16 +203,12 @@ pub async fn search_multi_index(mut ctx: Request) -> SResult { } } - let offset = offset; - let count = count; - let db = &ctx.state().db; + let par_body = body.clone(); - let responses_per_index: Vec> = index_list + let responses_per_index: Vec<(String, SearchResult)> = index_list .into_par_iter() .map(move |index_uid| { - let index: Index = db - .open_index(&index_uid) - .ok_or(ResponseError::index_not_found(&index_uid))?; + let index = data.db.open_index(&index_uid).unwrap(); let mut search_builder = index.new_search(par_body.query.clone()); @@ -237,9 +236,10 @@ pub async fn search_multi_index(mut ctx: Request) -> SResult { } } - let reader = db.main_read_txn()?; - let response = search_builder.search(&reader)?; - Ok((index_uid, response)) + let reader = data.db.main_read_txn().unwrap(); + let response = search_builder.search(&reader).unwrap(); + + (index_uid, response) }) .collect(); @@ -247,13 +247,11 @@ pub async fn search_multi_index(mut ctx: Request) -> SResult { let mut max_query_time = 0; - for response in responses_per_index { - if let Ok((index_uid, response)) = response { - if response.processing_time_ms > max_query_time { - max_query_time = response.processing_time_ms; - } - hits_map.insert(index_uid, response.hits); + for (index_uid, response) in responses_per_index { + if response.processing_time_ms > max_query_time { + max_query_time = response.processing_time_ms; } + hits_map.insert(index_uid, response.hits); } let response = SearchMultiBodyResponse { @@ -261,8 +259,8 @@ pub async fn search_multi_index(mut ctx: Request) -> SResult { offset, hits_per_page: count, processing_time_ms: max_query_time, - query: body.query, + query, }; - Ok(tide::Response::new(200).body_json(&response).unwrap()) + Ok(web::Json(response)) } From 6c581fb3bd7f40158d89017d2ee76cab8511d4de Mon Sep 17 00:00:00 2001 From: Quentin de Quelen Date: Wed, 8 Apr 2020 14:13:45 +0200 Subject: [PATCH 05/21] add index endpoint & key endpoint & stats endpoint --- meilisearch-core/examples/from_file.rs | 2 +- meilisearch-http/src/data.rs | 4 +- meilisearch-http/src/error.rs | 58 +---- meilisearch-http/src/main.rs | 22 +- meilisearch-http/src/routes/document.rs | 13 +- meilisearch-http/src/routes/index.rs | 323 ++++++++++-------------- meilisearch-http/src/routes/key.rs | 22 +- meilisearch-http/src/routes/mod.rs | 45 +++- meilisearch-http/src/routes/search.rs | 8 +- meilisearch-http/src/routes/stats.rs | 165 ++++++------ meilisearch-http/src/routes/update.rs | 44 ++++ 11 files changed, 358 insertions(+), 348 deletions(-) create mode 100644 meilisearch-http/src/routes/update.rs diff --git a/meilisearch-core/examples/from_file.rs b/meilisearch-core/examples/from_file.rs index 2a30d1ce4..4312cd2af 100644 --- a/meilisearch-core/examples/from_file.rs +++ b/meilisearch-core/examples/from_file.rs @@ -381,7 +381,7 @@ fn search_command(command: SearchCommand, database: Database) -> Result<(), Box< .sort_unstable_by_key(|m| (m.char_index, m.char_length)); let start_retrieve = Instant::now(); - let result = index.document::(&reader, Some(&fields), doc.id); + let result = index.document::(&reader, Some(fields.clone()), doc.id); retrieve_duration += start_retrieve.elapsed(); match result { diff --git a/meilisearch-http/src/data.rs b/meilisearch-http/src/data.rs index 3132a8dcc..d6836258a 100644 --- a/meilisearch-http/src/data.rs +++ b/meilisearch-http/src/data.rs @@ -10,7 +10,7 @@ use sha2::Digest; use sysinfo::Pid; use crate::option::Opt; -// use crate::routes::index::index_update_callback; +use crate::routes::index_update_callback; const LAST_UPDATE_KEY: &str = "last-update"; @@ -155,7 +155,7 @@ impl Data { let callback_context = data.clone(); db.set_update_callback(Box::new(move |index_uid, status| { - // index_update_callback(&index_uid, &callback_context, status); + index_update_callback(&index_uid, &callback_context, status); })); data diff --git a/meilisearch-http/src/error.rs b/meilisearch-http/src/error.rs index bd7260f3d..d684406d3 100644 --- a/meilisearch-http/src/error.rs +++ b/meilisearch-http/src/error.rs @@ -1,12 +1,8 @@ use std::fmt; -use meilisearch_core::{FstError, HeedError}; use serde_json::json; -use actix_http::{ResponseBuilder, Response}; +use actix_http::ResponseBuilder; use actix_web::http::StatusCode; use actix_web::*; -use futures::future::{ok, Ready}; - -// use crate::helpers::meilisearch::Error as SearchError; #[derive(Debug)] pub enum ResponseError { @@ -16,6 +12,7 @@ pub enum ResponseError { NotFound(String), IndexNotFound(String), DocumentNotFound(String), + UpdateNotFound(u64), MissingHeader(String), FilterParsing(String), BadParameter(String, String), @@ -38,6 +35,7 @@ impl fmt::Display for ResponseError { Self::NotFound(err) => write!(f, "{} not found", err), Self::IndexNotFound(index_uid) => write!(f, "Index {} not found", index_uid), Self::DocumentNotFound(document_id) => write!(f, "Document with id {} not found", document_id), + Self::UpdateNotFound(update_id) => write!(f, "Update with id {} not found", update_id), Self::MissingHeader(header) => write!(f, "Header {} is missing", header), Self::BadParameter(param, err) => write!(f, "Url parameter {} error: {}", param, err), Self::OpenIndex(err) => write!(f, "Impossible to open index; {}", err), @@ -68,6 +66,7 @@ impl error::ResponseError for ResponseError { Self::NotFound(_) => StatusCode::NOT_FOUND, Self::IndexNotFound(_) => StatusCode::NOT_FOUND, Self::DocumentNotFound(_) => StatusCode::NOT_FOUND, + Self::UpdateNotFound(_) => StatusCode::NOT_FOUND, Self::MissingHeader(_) => StatusCode::UNAUTHORIZED, Self::BadParameter(_, _) => StatusCode::BAD_REQUEST, Self::OpenIndex(_) => StatusCode::BAD_REQUEST, @@ -82,52 +81,3 @@ impl error::ResponseError for ResponseError { } } } - -// impl Responder for ResponseError { -// type Error = Error; -// type Future = Ready>; - -// #[inline] -// fn respond_to(self, req: &HttpRequest) -> Self::Future { -// ok(self.error_response()) -// } -// } - -impl From for ResponseError { - fn from(err: serde_json::Error) -> ResponseError { - ResponseError::Internal(err.to_string()) - } -} - -impl From for ResponseError { - fn from(err: meilisearch_core::Error) -> ResponseError { - ResponseError::Internal(err.to_string()) - } -} - -impl From for ResponseError { - fn from(err: HeedError) -> ResponseError { - ResponseError::Internal(err.to_string()) - } -} - -impl From for ResponseError { - fn from(err: FstError) -> ResponseError { - ResponseError::Internal(err.to_string()) - } -} - -// impl From for ResponseError { -// fn from(err: SearchError) -> ResponseError { -// match err { -// SearchError::FilterParsing(s) => ResponseError::FilterParsing(s), -// _ => ResponseError::Internal(err), -// } -// } -// } - -impl From for ResponseError { - fn from(err: meilisearch_core::settings::RankingRuleConversionError) -> ResponseError { - ResponseError::Internal(err.to_string()) - } -} diff --git a/meilisearch-http/src/main.rs b/meilisearch-http/src/main.rs index 34baae454..dc03ad079 100644 --- a/meilisearch-http/src/main.rs +++ b/meilisearch-http/src/main.rs @@ -1,14 +1,14 @@ -use std::{env, thread, fs}; +use std::{env, thread}; use log::info; use main_error::MainError; use structopt::StructOpt; use actix_web::middleware::Logger; -use actix_web::{post, web, App, HttpServer, HttpResponse, Responder}; +use actix_web::*; use meilisearch_http::data::Data; use meilisearch_http::option::Opt; use meilisearch_http::routes; -// use meilisearch_http::routes::index::index_update_callback; +use meilisearch_http::routes::index_update_callback; mod analytics; @@ -45,7 +45,7 @@ async fn main() -> Result<(), MainError> { let data_cloned = data.clone(); data.db.set_update_callback(Box::new(move |name, status| { - // index_update_callback(name, &data_cloned, status); + index_update_callback(name, &data_cloned, status); })); print_launch_resume(&opt, &data); @@ -56,6 +56,13 @@ async fn main() -> Result<(), MainError> { .app_data(web::Data::new(data.clone())) .service(routes::load_html) .service(routes::load_css) + .service(routes::index::list_indexes) + .service(routes::index::get_index) + .service(routes::index::create_index) + .service(routes::index::update_index) + .service(routes::index::delete_index) + .service(routes::search::search_with_url_query) + .service(routes::search::search_multi_index) .service(routes::document::get_document) .service(routes::document::delete_document) .service(routes::document::get_all_documents) @@ -63,7 +70,14 @@ async fn main() -> Result<(), MainError> { .service(routes::document::update_documents) .service(routes::document::delete_documents) .service(routes::document::clear_all_documents) + .service(routes::update::get_update_status) + .service(routes::update::get_all_updates_status) .service(routes::key::list) + .service(routes::stats::index_stats) + .service(routes::stats::get_stats) + .service(routes::stats::get_version) + .service(routes::stats::get_sys_info) + .service(routes::stats::get_sys_info_pretty) .service(routes::health::get_health) .service(routes::health::change_healthyness) ) diff --git a/meilisearch-http/src/routes/document.rs b/meilisearch-http/src/routes/document.rs index 9c4b88142..6240c89be 100644 --- a/meilisearch-http/src/routes/document.rs +++ b/meilisearch-http/src/routes/document.rs @@ -14,7 +14,7 @@ type Document = IndexMap; pub async fn get_document( data: web::Data, path: web::Path<(String, String)>, -) -> Result { +) -> Result> { let index = data.db.open_index(&path.0) .ok_or(ResponseError::IndexNotFound(path.0.clone()))?; let document_id = meilisearch_core::serde::compute_document_id(path.1.clone()); @@ -26,7 +26,7 @@ pub async fn get_document( .map_err(|_| ResponseError::DocumentNotFound(path.1.clone()))? .ok_or(ResponseError::DocumentNotFound(path.1.clone()))?; - Ok(HttpResponse::Ok().json(response)) + Ok(web::Json(response)) } #[delete("/indexes/{index_uid}/documents/{document_id}")] @@ -67,7 +67,7 @@ pub async fn get_all_documents( data: web::Data, path: web::Path, params: web::Query, -) -> Result { +) -> Result>> { let index = data.db.open_index(path.clone()) .ok_or(ResponseError::IndexNotFound(path.clone()))?; @@ -86,19 +86,20 @@ pub async fn get_all_documents( .take(limit) .collect(); - let documents_ids = documents_ids.map_err(|err| ResponseError::Internal(err.to_string()))?; + let documents_ids = documents_ids + .map_err(|err| ResponseError::Internal(err.to_string()))?; let attributes = params.attributes_to_retrieve.clone() .map(|a| a.split(',').map(|a| a.to_string()).collect()); - let mut response_body = Vec::>::new(); + let mut response_body = Vec::::new(); for document_id in documents_ids { if let Ok(Some(document)) = index.document(&reader, attributes.clone(), document_id) { response_body.push(document); } } - Ok(HttpResponse::Ok().json(response_body)) + Ok(web::Json(response_body)) } fn find_primary_key(document: &IndexMap) -> Option { diff --git a/meilisearch-http/src/routes/index.rs b/meilisearch-http/src/routes/index.rs index 5666ff0da..e9449784d 100644 --- a/meilisearch-http/src/routes/index.rs +++ b/meilisearch-http/src/routes/index.rs @@ -1,14 +1,10 @@ use chrono::{DateTime, Utc}; use log::error; -use meilisearch_core::ProcessedUpdateResult; use rand::seq::SliceRandom; use serde::{Deserialize, Serialize}; -use serde_json::json; -use tide::{Request, Response}; +use actix_web::*; -use crate::error::{IntoInternalError, ResponseError, SResult}; -use crate::helpers::tide::RequestExt; -use crate::helpers::tide::ACL::*; +use crate::error::ResponseError; use crate::Data; fn generate_uid() -> String { @@ -20,24 +16,40 @@ fn generate_uid() -> String { .collect() } -pub async fn list_indexes(ctx: Request) -> SResult { - ctx.is_allowed(Private)?; +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct IndexResponse { + name: String, + uid: String, + created_at: DateTime, + updated_at: DateTime, + primary_key: Option, +} - let indexes_uids = ctx.state().db.indexes_uids(); +#[get("/indexes")] +pub async fn list_indexes( + data: web::Data, +) -> Result>> { - let db = &ctx.state().db; - let reader = db.main_read_txn()?; + let reader = data.db.main_read_txn() + .map_err(|_| ResponseError::CreateTransaction)?; let mut response_body = Vec::new(); - for index_uid in indexes_uids { - let index = ctx.state().db.open_index(&index_uid); + for index_uid in data.db.indexes_uids() { + let index = data.db.open_index(&index_uid); match index { Some(index) => { - let name = index.main.name(&reader)?.into_internal_error()?; - let created_at = index.main.created_at(&reader)?.into_internal_error()?; - let updated_at = index.main.updated_at(&reader)?.into_internal_error()?; + let name = index.main.name(&reader) + .map_err(|e| ResponseError::Internal(e.to_string()))? + .ok_or(ResponseError::Internal("Impossible to get the name of an index".to_string()))?; + let created_at = index.main.created_at(&reader) + .map_err(|e| ResponseError::Internal(e.to_string()))? + .ok_or(ResponseError::Internal("Impossible to get the create date of an index".to_string()))?; + let updated_at = index.main.updated_at(&reader) + .map_err(|e| ResponseError::Internal(e.to_string()))? + .ok_or(ResponseError::Internal("Impossible to get the last update date of an index".to_string()))?; let primary_key = match index.main.schema(&reader) { Ok(Some(schema)) => match schema.primary_key() { @@ -63,31 +75,30 @@ pub async fn list_indexes(ctx: Request) -> SResult { } } - Ok(tide::Response::new(200).body_json(&response_body)?) + Ok(web::Json(response_body)) } -#[derive(Debug, Serialize)] -#[serde(rename_all = "camelCase")] -struct IndexResponse { - name: String, - uid: String, - created_at: DateTime, - updated_at: DateTime, - primary_key: Option, -} +#[get("/indexes/{index_uid}")] +pub async fn get_index( + data: web::Data, + path: web::Path, +) -> Result> { -pub async fn get_index(ctx: Request) -> SResult { - ctx.is_allowed(Private)?; + let index = data.db.open_index(path.clone()) + .ok_or(ResponseError::IndexNotFound(path.clone()))?; - let index = ctx.index()?; + let reader = data.db.main_read_txn() + .map_err(|_| ResponseError::CreateTransaction)?; - let db = &ctx.state().db; - let reader = db.main_read_txn()?; - - let uid = ctx.url_param("index")?; - let name = index.main.name(&reader)?.into_internal_error()?; - let created_at = index.main.created_at(&reader)?.into_internal_error()?; - let updated_at = index.main.updated_at(&reader)?.into_internal_error()?; + let name = index.main.name(&reader) + .map_err(|e| ResponseError::Internal(e.to_string()))? + .ok_or(ResponseError::Internal("Impossible to get the name of an index".to_string()))?; + let created_at = index.main.created_at(&reader) + .map_err(|e| ResponseError::Internal(e.to_string()))? + .ok_or(ResponseError::Internal("Impossible to get the create date of an index".to_string()))?; + let updated_at = index.main.updated_at(&reader) + .map_err(|e| ResponseError::Internal(e.to_string()))? + .ok_or(ResponseError::Internal("Impossible to get the last update date of an index".to_string()))?; let primary_key = match index.main.schema(&reader) { Ok(Some(schema)) => match schema.primary_key() { @@ -97,52 +108,34 @@ pub async fn get_index(ctx: Request) -> SResult { _ => None, }; - let response_body = IndexResponse { + Ok(web::Json(IndexResponse { name, - uid, + uid: path.to_string(), created_at, updated_at, primary_key, - }; - - Ok(tide::Response::new(200).body_json(&response_body)?) + })) } #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] -struct IndexCreateRequest { +pub struct IndexCreateRequest { name: Option, uid: Option, primary_key: Option, } -#[derive(Debug, Serialize)] -#[serde(rename_all = "camelCase")] -struct IndexCreateResponse { - name: String, - uid: String, - created_at: DateTime, - updated_at: DateTime, - primary_key: Option, -} - -pub async fn create_index(mut ctx: Request) -> SResult { - ctx.is_allowed(Private)?; - - let body = ctx - .body_json::() - .await - .map_err(ResponseError::bad_request)?; +#[post("/indexes")] +pub async fn create_index( + data: web::Data, + body: web::Json +) -> Result> { if let (None, None) = (body.name.clone(), body.uid.clone()) { - return Err(ResponseError::bad_request( - "Index creation must have an uid", - )); + return Err(ResponseError::BadRequest("Index creation must have an uid".to_string()))?; } - let db = &ctx.state().db; - - let uid = match body.uid { + let uid = match body.uid.clone() { Some(uid) => { if uid .chars() @@ -150,64 +143,71 @@ pub async fn create_index(mut ctx: Request) -> SResult { { uid } else { - return Err(ResponseError::InvalidIndexUid); + return Err(ResponseError::InvalidIndexUid)?; } } None => loop { let uid = generate_uid(); - if db.open_index(&uid).is_none() { + if data.db.open_index(&uid).is_none() { break uid; } }, }; - let created_index = match db.create_index(&uid) { - Ok(index) => index, - Err(e) => return Err(ResponseError::create_index(e)), - }; + let created_index = data.db.create_index(&uid) + .map_err(|e| ResponseError::CreateIndex(e.to_string()))?; + + let mut writer = data.db.main_write_txn() + .map_err(|_| ResponseError::CreateTransaction)?; + + let name = body.name.clone().unwrap_or(uid.clone()); + created_index.main.put_name(&mut writer, &name) + .map_err(|e| ResponseError::Internal(e.to_string()))?; - let mut writer = db.main_write_txn()?; - let name = body.name.unwrap_or(uid.clone()); - created_index.main.put_name(&mut writer, &name)?; let created_at = created_index .main - .created_at(&writer)? - .into_internal_error()?; + .created_at(&writer) + .map_err(|e| ResponseError::Internal(e.to_string()))? + .ok_or(ResponseError::Internal("".to_string()))?; + let updated_at = created_index .main - .updated_at(&writer)? - .into_internal_error()?; + .updated_at(&writer) + .map_err(|e| ResponseError::Internal(e.to_string()))? + .ok_or(ResponseError::Internal("".to_string()))?; if let Some(id) = body.primary_key.clone() { - if let Some(mut schema) = created_index.main.schema(&mut writer)? { - schema.set_primary_key(&id).map_err(ResponseError::bad_request)?; - created_index.main.put_schema(&mut writer, &schema)?; + if let Some(mut schema) = created_index.main.schema(&mut writer) + .map_err(|e| ResponseError::Internal(e.to_string()))? { + schema.set_primary_key(&id) + .map_err(|e| ResponseError::BadRequest(e.to_string()))?; + created_index.main.put_schema(&mut writer, &schema) + .map_err(|e| ResponseError::Internal(e.to_string()))?; } } - writer.commit()?; + writer.commit() + .map_err(|_| ResponseError::CommitTransaction)?; - let response_body = IndexCreateResponse { + Ok(web::Json(IndexResponse { name, uid, created_at, updated_at, - primary_key: body.primary_key, - }; - - Ok(tide::Response::new(201).body_json(&response_body)?) + primary_key: body.primary_key.clone(), + })) } #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] -struct UpdateIndexRequest { +pub struct UpdateIndexRequest { name: Option, primary_key: Option, } #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] -struct UpdateIndexResponse { +pub struct UpdateIndexResponse { name: String, uid: String, created_at: DateTime, @@ -215,49 +215,59 @@ struct UpdateIndexResponse { primary_key: Option, } -pub async fn update_index(mut ctx: Request) -> SResult { - ctx.is_allowed(Private)?; +#[post("/indexes/{index_uid}")] +pub async fn update_index( + data: web::Data, + path: web::Path, + body: web::Json +) -> Result> { - let body = ctx - .body_json::() - .await - .map_err(ResponseError::bad_request)?; + let index = data.db.open_index(path.clone()) + .ok_or(ResponseError::IndexNotFound(path.clone()))?; - let index_uid = ctx.url_param("index")?; - let index = ctx.index()?; + let mut writer = data.db.main_write_txn() + .map_err(|_| ResponseError::CreateTransaction)?; - let db = &ctx.state().db; - let mut writer = db.main_write_txn()?; - - if let Some(name) = body.name { - index.main.put_name(&mut writer, &name)?; + if let Some(name) = body.name.clone() { + index.main.put_name(&mut writer, &name) + .map_err(|e| ResponseError::Internal(e.to_string()))?; } if let Some(id) = body.primary_key.clone() { - if let Some(mut schema) = index.main.schema(&mut writer)? { + if let Some(mut schema) = index.main.schema(&mut writer) + .map_err(|e| ResponseError::Internal(e.to_string()))? { match schema.primary_key() { Some(_) => { - return Err(ResponseError::bad_request( - "The primary key cannot be updated", - )); + return Err(ResponseError::BadRequest("The primary key cannot be updated".to_string()))?; } None => { schema .set_primary_key(&id) - .map_err(ResponseError::bad_request)?; - index.main.put_schema(&mut writer, &schema)?; + .map_err(|e| ResponseError::Internal(e.to_string()))?; + index.main.put_schema(&mut writer, &schema) + .map_err(|e| ResponseError::Internal(e.to_string()))?; } } } } - index.main.put_updated_at(&mut writer)?; - writer.commit()?; + index.main.put_updated_at(&mut writer) + .map_err(|e| ResponseError::Internal(e.to_string()))?; + writer.commit() + .map_err(|_| ResponseError::CommitTransaction)?; - let reader = db.main_read_txn()?; - let name = index.main.name(&reader)?.into_internal_error()?; - let created_at = index.main.created_at(&reader)?.into_internal_error()?; - let updated_at = index.main.updated_at(&reader)?.into_internal_error()?; + let reader = data.db.main_read_txn() + .map_err(|_| ResponseError::CreateTransaction)?; + + let name = index.main.name(&reader) + .map_err(|e| ResponseError::Internal(e.to_string()))? + .ok_or(ResponseError::Internal("Impossible to get the name of an index".to_string()))?; + let created_at = index.main.created_at(&reader) + .map_err(|e| ResponseError::Internal(e.to_string()))? + .ok_or(ResponseError::Internal("Impossible to get the create date of an index".to_string()))?; + let updated_at = index.main.updated_at(&reader) + .map_err(|e| ResponseError::Internal(e.to_string()))? + .ok_or(ResponseError::Internal("Impossible to get the last update date of an index".to_string()))?; let primary_key = match index.main.schema(&reader) { Ok(Some(schema)) => match schema.primary_key() { @@ -267,86 +277,23 @@ pub async fn update_index(mut ctx: Request) -> SResult { _ => None, }; - let response_body = UpdateIndexResponse { + Ok(web::Json(IndexResponse { name, - uid: index_uid, + uid: path.clone(), created_at, updated_at, primary_key, - }; - - Ok(tide::Response::new(200).body_json(&response_body)?) + })) } -pub async fn get_update_status(ctx: Request) -> SResult { - ctx.is_allowed(Private)?; +#[delete("/indexes/{index_uid}")] +pub async fn delete_index( + data: web::Data, + path: web::Path, +) -> Result { - let db = &ctx.state().db; - let reader = db.update_read_txn()?; + data.db.delete_index(&path.to_string()) + .map_err(|e| ResponseError::Internal(e.to_string()))?; - let update_id = ctx - .param::("update_id") - .map_err(|e| ResponseError::bad_parameter("update_id", e))?; - - let index = ctx.index()?; - let status = index.update_status(&reader, update_id)?; - - let response = match status { - Some(status) => tide::Response::new(200).body_json(&status).unwrap(), - None => tide::Response::new(404) - .body_json(&json!({ "message": "unknown update id" })) - .unwrap(), - }; - - Ok(response) -} - -pub async fn get_all_updates_status(ctx: Request) -> SResult { - ctx.is_allowed(Private)?; - let db = &ctx.state().db; - let reader = db.update_read_txn()?; - let index = ctx.index()?; - let response = index.all_updates_status(&reader)?; - Ok(tide::Response::new(200).body_json(&response).unwrap()) -} - -pub async fn delete_index(ctx: Request) -> SResult { - ctx.is_allowed(Private)?; - let _ = ctx.index()?; - let index_uid = ctx.url_param("index")?; - ctx.state().db.delete_index(&index_uid)?; - Ok(tide::Response::new(204)) -} - -pub fn index_update_callback(index_uid: &str, data: &Data, status: ProcessedUpdateResult) { - if status.error.is_some() { - return; - } - - if let Some(index) = data.db.open_index(&index_uid) { - let db = &data.db; - let mut writer = match db.main_write_txn() { - Ok(writer) => writer, - Err(e) => { - error!("Impossible to get write_txn; {}", e); - return; - } - }; - - if let Err(e) = data.compute_stats(&mut writer, &index_uid) { - error!("Impossible to compute stats; {}", e) - } - - if let Err(e) = data.set_last_update(&mut writer) { - error!("Impossible to update last_update; {}", e) - } - - if let Err(e) = index.main.put_updated_at(&mut writer) { - error!("Impossible to update updated_at; {}", e) - } - - if let Err(e) = writer.commit() { - error!("Impossible to get write_txn; {}", e); - } - } + HttpResponse::NoContent().await } diff --git a/meilisearch-http/src/routes/key.rs b/meilisearch-http/src/routes/key.rs index 52265bc2f..a7926d9dc 100644 --- a/meilisearch-http/src/routes/key.rs +++ b/meilisearch-http/src/routes/key.rs @@ -1,15 +1,21 @@ use crate::Data; -use serde_json::json; use actix_web::*; +use serde::Serialize; + + +#[derive(Default, Serialize)] +pub struct KeysResponse { + private: Option, + public: Option, +} #[get("/keys")] pub async fn list( data: web::Data, -) -> Result { - let keys = &data.api_keys; - - HttpResponse::Ok().json(&json!({ - "private": keys.private, - "public": keys.public, - })).await +) -> web::Json { + let api_keys = data.api_keys.clone(); + web::Json(KeysResponse{ + private: api_keys.private, + public: api_keys.public, + }) } diff --git a/meilisearch-http/src/routes/mod.rs b/meilisearch-http/src/routes/mod.rs index adf4f0e63..e824f925e 100644 --- a/meilisearch-http/src/routes/mod.rs +++ b/meilisearch-http/src/routes/mod.rs @@ -1,16 +1,20 @@ - use actix_web::*; use serde::Serialize; +use log::error; +use meilisearch_core::ProcessedUpdateResult; + +use crate::Data; pub mod document; pub mod health; -// pub mod index; +pub mod index; pub mod key; pub mod search; // pub mod setting; -// pub mod stats; +pub mod stats; // pub mod stop_words; // pub mod synonym; +pub mod update; #[derive(Default, Serialize)] #[serde(rename_all = "camelCase")] @@ -42,6 +46,41 @@ pub async fn load_css() -> HttpResponse { .body(include_str!("../../public/bulma.min.css").to_string()) } + +pub fn index_update_callback(index_uid: &str, data: &Data, status: ProcessedUpdateResult) { + if status.error.is_some() { + return; + } + + if let Some(index) = data.db.open_index(&index_uid) { + let db = &data.db; + let mut writer = match db.main_write_txn() { + Ok(writer) => writer, + Err(e) => { + error!("Impossible to get write_txn; {}", e); + return; + } + }; + + if let Err(e) = data.compute_stats(&mut writer, &index_uid) { + error!("Impossible to compute stats; {}", e) + } + + if let Err(e) = data.set_last_update(&mut writer) { + error!("Impossible to update last_update; {}", e) + } + + if let Err(e) = index.main.put_updated_at(&mut writer) { + error!("Impossible to update updated_at; {}", e) + } + + if let Err(e) = writer.commit() { + error!("Impossible to get write_txn; {}", e); + } + } +} + + // pub fn load_routes(app: &mut tide::Server) { // app.at("/").get(|_| async { // tide::Response::new(200) diff --git a/meilisearch-http/src/routes/search.rs b/meilisearch-http/src/routes/search.rs index 2efd24580..15040592c 100644 --- a/meilisearch-http/src/routes/search.rs +++ b/meilisearch-http/src/routes/search.rs @@ -10,13 +10,11 @@ use actix_web::*; use crate::error::ResponseError; use crate::helpers::meilisearch::{Error, IndexSearchExt, SearchHit, SearchResult}; -// use crate::helpers::tide::RequestExt; -// use crate::helpers::tide::ACL::*; use crate::Data; #[derive(Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] -struct SearchQuery { +pub struct SearchQuery { q: String, offset: Option, limit: Option, @@ -153,7 +151,7 @@ pub async fn search_with_url_query( #[derive(Clone, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] -struct SearchMultiBody { +pub struct SearchMultiBody { indexes: HashSet, query: String, offset: Option, @@ -169,7 +167,7 @@ struct SearchMultiBody { #[derive(Debug, Clone, Serialize)] #[serde(rename_all = "camelCase")] -struct SearchMultiBodyResponse { +pub struct SearchMultiBodyResponse { hits: HashMap>, offset: usize, hits_per_page: usize, diff --git a/meilisearch-http/src/routes/stats.rs b/meilisearch-http/src/routes/stats.rs index 16d449c74..e2e82122d 100644 --- a/meilisearch-http/src/routes/stats.rs +++ b/meilisearch-http/src/routes/stats.rs @@ -1,79 +1,93 @@ use std::collections::HashMap; +use actix_web::*; use chrono::{DateTime, Utc}; use log::error; use pretty_bytes::converter::convert; use serde::Serialize; -use sysinfo::{NetworkExt, Pid, ProcessExt, ProcessorExt, System, SystemExt}; -use tide::{Request, Response}; +use sysinfo::{NetworkExt, ProcessExt, ProcessorExt, System, SystemExt}; use walkdir::WalkDir; -use crate::error::{IntoInternalError, SResult}; -use crate::helpers::tide::RequestExt; -use crate::helpers::tide::ACL::*; use crate::Data; +use crate::error::ResponseError; #[derive(Serialize)] #[serde(rename_all = "camelCase")] -struct IndexStatsResponse { +pub struct IndexStatsResponse { number_of_documents: u64, is_indexing: bool, fields_frequency: HashMap, } -pub async fn index_stats(ctx: Request) -> SResult { - ctx.is_allowed(Admin)?; - let index_uid = ctx.url_param("index")?; - let index = ctx.index()?; - let db = &ctx.state().db; - let reader = db.main_read_txn()?; - let update_reader = db.update_read_txn()?; - let number_of_documents = index.main.number_of_documents(&reader)?; - let fields_frequency = index.main.fields_frequency(&reader)?.unwrap_or_default(); - let is_indexing = ctx - .state() - .is_indexing(&update_reader, &index_uid)? - .into_internal_error()?; +#[get("/indexes/{index_uid}/stats")] +pub async fn index_stats( + data: web::Data, + path: web::Path, +) -> Result> { + let index = data.db.open_index(path.clone()) + .ok_or(ResponseError::IndexNotFound(path.clone()))?; - let response = IndexStatsResponse { + let reader = data.db.main_read_txn() + .map_err(|_| ResponseError::CreateTransaction)?; + + let number_of_documents = index.main.number_of_documents(&reader) + .map_err(|err| ResponseError::Internal(err.to_string()))?; + + let fields_frequency = index.main.fields_frequency(&reader) + .map_err(|err| ResponseError::Internal(err.to_string()))? + .unwrap_or_default(); + + let update_reader = data.db.update_read_txn() + .map_err(|_| ResponseError::CreateTransaction)?; + + let is_indexing = data + .is_indexing(&update_reader, &path) + .map_err(|err| ResponseError::Internal(err.to_string()))? + .unwrap_or_default(); + + Ok(web::Json(IndexStatsResponse { number_of_documents, is_indexing, fields_frequency, - }; - Ok(tide::Response::new(200).body_json(&response).unwrap()) + })) } #[derive(Serialize)] #[serde(rename_all = "camelCase")] -struct StatsResult { +pub struct StatsResult { database_size: u64, last_update: Option>, indexes: HashMap, } -pub async fn get_stats(ctx: Request) -> SResult { - ctx.is_allowed(Admin)?; +#[get("/stats")] +pub async fn get_stats( + data: web::Data, +) -> Result> { let mut index_list = HashMap::new(); - let db = &ctx.state().db; - let reader = db.main_read_txn()?; - let update_reader = db.update_read_txn()?; + let reader = data.db.main_read_txn() + .map_err(|_| ResponseError::CreateTransaction)?; + let update_reader = data.db.update_read_txn() + .map_err(|_| ResponseError::CreateTransaction)?; - let indexes_set = ctx.state().db.indexes_uids(); + let indexes_set = data.db.indexes_uids(); for index_uid in indexes_set { - let index = ctx.state().db.open_index(&index_uid); - + let index = data.db.open_index(&index_uid); match index { Some(index) => { - let number_of_documents = index.main.number_of_documents(&reader)?; + let number_of_documents = index.main.number_of_documents(&reader) + .map_err(|err| ResponseError::Internal(err.to_string()))?; - let fields_frequency = index.main.fields_frequency(&reader)?.unwrap_or_default(); + let fields_frequency = index.main.fields_frequency(&reader) + .map_err(|err| ResponseError::Internal(err.to_string()))? + .unwrap_or_default(); - let is_indexing = ctx - .state() - .is_indexing(&update_reader, &index_uid)? - .into_internal_error()?; + let is_indexing = data + .is_indexing(&update_reader, &index_uid) + .map_err(|err| ResponseError::Internal(err.to_string()))? + .unwrap_or_default(); let response = IndexStatsResponse { number_of_documents, @@ -89,46 +103,43 @@ pub async fn get_stats(ctx: Request) -> SResult { } } - let database_size = WalkDir::new(ctx.state().db_path.clone()) + let database_size = WalkDir::new(data.db_path.clone()) .into_iter() .filter_map(|entry| entry.ok()) .filter_map(|entry| entry.metadata().ok()) .filter(|metadata| metadata.is_file()) .fold(0, |acc, m| acc + m.len()); - let last_update = ctx.state().last_update(&reader)?; + let last_update = data.last_update(&reader) + .map_err(|err| ResponseError::Internal(err.to_string()))?; - let response = StatsResult { + Ok(web::Json(StatsResult { database_size, last_update, indexes: index_list, - }; - - Ok(tide::Response::new(200).body_json(&response).unwrap()) + })) } #[derive(Serialize)] #[serde(rename_all = "camelCase")] -struct VersionResponse { +pub struct VersionResponse { commit_sha: String, build_date: String, pkg_version: String, } -pub async fn get_version(ctx: Request) -> SResult { - ctx.is_allowed(Admin)?; - let response = VersionResponse { +#[get("/version")] +pub async fn get_version() -> web::Json { + web::Json(VersionResponse { commit_sha: env!("VERGEN_SHA").to_string(), build_date: env!("VERGEN_BUILD_TIMESTAMP").to_string(), pkg_version: env!("CARGO_PKG_VERSION").to_string(), - }; - - Ok(tide::Response::new(200).body_json(&response).unwrap()) + }) } #[derive(Serialize)] #[serde(rename_all = "camelCase")] -pub(crate) struct SysGlobal { +pub struct SysGlobal { total_memory: u64, used_memory: u64, total_swap: u64, @@ -152,7 +163,7 @@ impl SysGlobal { #[derive(Serialize)] #[serde(rename_all = "camelCase")] -pub(crate) struct SysProcess { +pub struct SysProcess { memory: u64, cpu: f32, } @@ -168,7 +179,7 @@ impl SysProcess { #[derive(Serialize)] #[serde(rename_all = "camelCase")] -pub(crate) struct SysInfo { +pub struct SysInfo { memory_usage: f64, processor_usage: Vec, global: SysGlobal, @@ -186,7 +197,10 @@ impl SysInfo { } } -pub(crate) fn report(pid: Pid) -> SysInfo { +#[get("/sys-info")] +pub async fn get_sys_info( + data: web::Data, +) -> web::Json { let mut sys = System::new(); let mut info = SysInfo::new(); @@ -200,28 +214,27 @@ pub(crate) fn report(pid: Pid) -> SysInfo { info.global.used_memory = sys.get_used_memory(); info.global.total_swap = sys.get_total_swap(); info.global.used_swap = sys.get_used_swap(); - info.global.input_data = sys.get_networks().into_iter().map(|(_, n)| n.get_received()).sum::(); - info.global.output_data = sys.get_networks().into_iter().map(|(_, n)| n.get_transmitted()).sum::(); + info.global.input_data = sys.get_networks() + .into_iter() + .map(|(_, n)| n.get_received()) + .sum::(); + info.global.output_data = sys.get_networks() + .into_iter() + .map(|(_, n)| n.get_transmitted()) + .sum::(); - if let Some(process) = sys.get_process(pid) { + if let Some(process) = sys.get_process(data.server_pid) { info.process.memory = process.memory(); info.process.cpu = process.cpu_usage() * 100.0; } sys.refresh_all(); - - info -} - -pub async fn get_sys_info(ctx: Request) -> SResult { - ctx.is_allowed(Admin)?; - let response = report(ctx.state().server_pid); - Ok(tide::Response::new(200).body_json(&response).unwrap()) + web::Json(info) } #[derive(Serialize)] #[serde(rename_all = "camelCase")] -pub(crate) struct SysGlobalPretty { +pub struct SysGlobalPretty { total_memory: String, used_memory: String, total_swap: String, @@ -245,7 +258,7 @@ impl SysGlobalPretty { #[derive(Serialize)] #[serde(rename_all = "camelCase")] -pub(crate) struct SysProcessPretty { +pub struct SysProcessPretty { memory: String, cpu: String, } @@ -261,7 +274,7 @@ impl SysProcessPretty { #[derive(Serialize)] #[serde(rename_all = "camelCase")] -pub(crate) struct SysInfoPretty { +pub struct SysInfoPretty { memory_usage: String, processor_usage: Vec, global: SysGlobalPretty, @@ -279,7 +292,11 @@ impl SysInfoPretty { } } -pub(crate) fn report_pretty(pid: Pid) -> SysInfoPretty { + +#[get("/sys-info/pretty")] +pub async fn get_sys_info_pretty( + data: web::Data, +) -> web::Json { let mut sys = System::new(); let mut info = SysInfoPretty::new(); @@ -300,18 +317,12 @@ pub(crate) fn report_pretty(pid: Pid) -> SysInfoPretty { info.global.input_data = convert(sys.get_networks().into_iter().map(|(_, n)| n.get_received()).sum::() as f64); info.global.output_data = convert(sys.get_networks().into_iter().map(|(_, n)| n.get_transmitted()).sum::() as f64); - if let Some(process) = sys.get_process(pid) { + if let Some(process) = sys.get_process(data.server_pid) { info.process.memory = convert(process.memory() as f64 * 1024.0); info.process.cpu = format!("{:.1} %", process.cpu_usage() * 100.0); } sys.refresh_all(); - info -} - -pub async fn get_sys_info_pretty(ctx: Request) -> SResult { - ctx.is_allowed(Admin)?; - let response = report_pretty(ctx.state().server_pid); - Ok(tide::Response::new(200).body_json(&response).unwrap()) + web::Json(info) } diff --git a/meilisearch-http/src/routes/update.rs b/meilisearch-http/src/routes/update.rs new file mode 100644 index 000000000..8a99ea6d4 --- /dev/null +++ b/meilisearch-http/src/routes/update.rs @@ -0,0 +1,44 @@ +use actix_web::*; +use meilisearch_core::UpdateStatus; + +use crate::error::ResponseError; +use crate::Data; + +#[get("/indexes/{index_uid}/updates/{update_id}")] +pub async fn get_update_status( + data: web::Data, + path: web::Path<(String, u64)>, +) -> Result> { + + let index = data.db.open_index(path.0.clone()) + .ok_or(ResponseError::IndexNotFound(path.0.clone()))?; + + let reader = data.db.update_read_txn() + .map_err(|_| ResponseError::CreateTransaction)?; + + let status = index.update_status(&reader, path.1) + .map_err(|e| ResponseError::Internal(e.to_string()))?; + + match status { + Some(status) => Ok(web::Json(status)), + None => Err(ResponseError::UpdateNotFound(path.1))? + } +} + +#[get("/indexes/{index_uid}/updates")] +pub async fn get_all_updates_status( + data: web::Data, + path: web::Path, +) -> Result>> { + + let index = data.db.open_index(path.clone()) + .ok_or(ResponseError::IndexNotFound(path.clone()))?; + + let reader = data.db.update_read_txn() + .map_err(|_| ResponseError::CreateTransaction)?; + + let response = index.all_updates_status(&reader) + .map_err(|err| ResponseError::Internal(err.to_string()))?; + + Ok(web::Json(response)) +} From 5ec130e6dca5eeebf30b9414bc900c00ea3fdc47 Mon Sep 17 00:00:00 2001 From: Quentin de Quelen Date: Thu, 9 Apr 2020 10:39:34 +0200 Subject: [PATCH 06/21] cleanup --- meilisearch-http/Cargo.toml | 2 - meilisearch-http/src/error.rs | 21 +------ meilisearch-http/src/main.rs | 6 +- meilisearch-http/src/routes/document.rs | 51 ++++++++--------- meilisearch-http/src/routes/health.rs | 23 ++++---- meilisearch-http/src/routes/index.rs | 74 +++++++++++++++++++------ meilisearch-http/src/routes/key.rs | 2 +- meilisearch-http/src/routes/mod.rs | 7 +-- meilisearch-http/src/routes/search.rs | 17 +++--- meilisearch-http/src/routes/stats.rs | 15 ++--- meilisearch-http/src/routes/update.rs | 44 --------------- 11 files changed, 122 insertions(+), 140 deletions(-) delete mode 100644 meilisearch-http/src/routes/update.rs diff --git a/meilisearch-http/Cargo.toml b/meilisearch-http/Cargo.toml index e78dedc50..cd506c092 100644 --- a/meilisearch-http/Cargo.toml +++ b/meilisearch-http/Cargo.toml @@ -14,7 +14,6 @@ name = "meilisearch" path = "src/main.rs" [dependencies] -# async-std = { version = "1.5.0", features = ["attributes"] } chrono = { version = "0.4.11", features = ["serde"] } crossbeam-channel = "0.4.2" env_logger = "0.7.1" @@ -38,7 +37,6 @@ sha2 = "0.8.1" siphasher = "0.3.2" structopt = "0.3.12" sysinfo = "0.12.0" -# tide = "0.6.0" ureq = { version = "0.12.0", features = ["tls"], default-features = false } walkdir = "2.3.1" whoami = "0.8.1" diff --git a/meilisearch-http/src/error.rs b/meilisearch-http/src/error.rs index d684406d3..57a996e84 100644 --- a/meilisearch-http/src/error.rs +++ b/meilisearch-http/src/error.rs @@ -1,8 +1,8 @@ use std::fmt; use serde_json::json; +use actix_web as aweb; use actix_http::ResponseBuilder; use actix_web::http::StatusCode; -use actix_web::*; #[derive(Debug)] pub enum ResponseError { @@ -12,16 +12,11 @@ pub enum ResponseError { NotFound(String), IndexNotFound(String), DocumentNotFound(String), - UpdateNotFound(u64), MissingHeader(String), FilterParsing(String), BadParameter(String, String), OpenIndex(String), CreateIndex(String), - CreateTransaction, - CommitTransaction, - Schema, - InferPrimaryKey, InvalidIndexUid, Maintenance, } @@ -35,15 +30,10 @@ impl fmt::Display for ResponseError { Self::NotFound(err) => write!(f, "{} not found", err), Self::IndexNotFound(index_uid) => write!(f, "Index {} not found", index_uid), Self::DocumentNotFound(document_id) => write!(f, "Document with id {} not found", document_id), - Self::UpdateNotFound(update_id) => write!(f, "Update with id {} not found", update_id), Self::MissingHeader(header) => write!(f, "Header {} is missing", header), Self::BadParameter(param, err) => write!(f, "Url parameter {} error: {}", param, err), Self::OpenIndex(err) => write!(f, "Impossible to open index; {}", err), Self::CreateIndex(err) => write!(f, "Impossible to create index; {}", err), - Self::CreateTransaction => write!(f, "Impossible to create transaction"), - Self::CommitTransaction => write!(f, "Impossible to commit transaction"), - Self::Schema => write!(f, "Internal schema is innaccessible"), - Self::InferPrimaryKey => write!(f, "Could not infer primary key"), Self::InvalidIndexUid => write!(f, "Index must have a valid uid; Index uid can be of type integer or string only composed of alphanumeric characters, hyphens (-) and underscores (_)."), Self::Maintenance => write!(f, "Server is in maintenance, please try again later"), Self::FilterParsing(err) => write!(f, "parsing error: {}", err), @@ -51,8 +41,8 @@ impl fmt::Display for ResponseError { } } -impl error::ResponseError for ResponseError { - fn error_response(&self) -> HttpResponse { +impl aweb::error::ResponseError for ResponseError { + fn error_response(&self) -> aweb::HttpResponse { ResponseBuilder::new(self.status_code()).json(json!({ "message": self.to_string(), })) @@ -66,15 +56,10 @@ impl error::ResponseError for ResponseError { Self::NotFound(_) => StatusCode::NOT_FOUND, Self::IndexNotFound(_) => StatusCode::NOT_FOUND, Self::DocumentNotFound(_) => StatusCode::NOT_FOUND, - Self::UpdateNotFound(_) => StatusCode::NOT_FOUND, Self::MissingHeader(_) => StatusCode::UNAUTHORIZED, Self::BadParameter(_, _) => StatusCode::BAD_REQUEST, Self::OpenIndex(_) => StatusCode::BAD_REQUEST, Self::CreateIndex(_) => StatusCode::BAD_REQUEST, - Self::CreateTransaction => StatusCode::INTERNAL_SERVER_ERROR, - Self::CommitTransaction => StatusCode::INTERNAL_SERVER_ERROR, - Self::Schema => StatusCode::INTERNAL_SERVER_ERROR, - Self::InferPrimaryKey => StatusCode::BAD_REQUEST, Self::InvalidIndexUid => StatusCode::BAD_REQUEST, Self::Maintenance => StatusCode::SERVICE_UNAVAILABLE, Self::FilterParsing(_) => StatusCode::BAD_REQUEST, diff --git a/meilisearch-http/src/main.rs b/meilisearch-http/src/main.rs index dc03ad079..0073e029c 100644 --- a/meilisearch-http/src/main.rs +++ b/meilisearch-http/src/main.rs @@ -4,7 +4,7 @@ use log::info; use main_error::MainError; use structopt::StructOpt; use actix_web::middleware::Logger; -use actix_web::*; +use actix_web::{web, HttpServer, App}; use meilisearch_http::data::Data; use meilisearch_http::option::Opt; use meilisearch_http::routes; @@ -61,6 +61,8 @@ async fn main() -> Result<(), MainError> { .service(routes::index::create_index) .service(routes::index::update_index) .service(routes::index::delete_index) + .service(routes::index::get_update_status) + .service(routes::index::get_all_updates_status) .service(routes::search::search_with_url_query) .service(routes::search::search_multi_index) .service(routes::document::get_document) @@ -70,8 +72,6 @@ async fn main() -> Result<(), MainError> { .service(routes::document::update_documents) .service(routes::document::delete_documents) .service(routes::document::clear_all_documents) - .service(routes::update::get_update_status) - .service(routes::update::get_all_updates_status) .service(routes::key::list) .service(routes::stats::index_stats) .service(routes::stats::get_stats) diff --git a/meilisearch-http/src/routes/document.rs b/meilisearch-http/src/routes/document.rs index 6240c89be..825637526 100644 --- a/meilisearch-http/src/routes/document.rs +++ b/meilisearch-http/src/routes/document.rs @@ -2,7 +2,8 @@ use std::collections::BTreeSet; use indexmap::IndexMap; use serde::Deserialize; use serde_json::Value; -use actix_web::*; +use actix_web::{web, get, post, put, delete, HttpResponse}; +use actix_web as aweb; use crate::error::ResponseError; use crate::Data; @@ -14,13 +15,13 @@ type Document = IndexMap; pub async fn get_document( data: web::Data, path: web::Path<(String, String)>, -) -> Result> { +) -> aweb::Result> { let index = data.db.open_index(&path.0) .ok_or(ResponseError::IndexNotFound(path.0.clone()))?; let document_id = meilisearch_core::serde::compute_document_id(path.1.clone()); let reader = data.db.main_read_txn() - .map_err(|_| ResponseError::CreateTransaction)?; + .map_err(|err| ResponseError::Internal(err.to_string()))?; let response = index.document::(&reader, None, document_id) .map_err(|_| ResponseError::DocumentNotFound(path.1.clone()))? @@ -33,13 +34,13 @@ pub async fn get_document( pub async fn delete_document( data: web::Data, path: web::Path<(String, String)>, -) -> Result { +) -> aweb::Result { let index = data.db.open_index(&path.0) .ok_or(ResponseError::IndexNotFound(path.0.clone()))?; let document_id = meilisearch_core::serde::compute_document_id(path.1.clone()); let mut update_writer = data.db.update_write_txn() - .map_err(|_| ResponseError::CreateTransaction)?; + .map_err(|err| ResponseError::Internal(err.to_string()))?; let mut documents_deletion = index.documents_deletion(); documents_deletion.delete_document_by_id(document_id); @@ -48,7 +49,7 @@ pub async fn delete_document( .map_err(|err| ResponseError::Internal(err.to_string()))?; update_writer.commit() - .map_err(|_| ResponseError::CommitTransaction)?; + .map_err(|err| ResponseError::Internal(err.to_string()))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -67,7 +68,7 @@ pub async fn get_all_documents( data: web::Data, path: web::Path, params: web::Query, -) -> Result>> { +) -> aweb::Result>> { let index = data.db.open_index(path.clone()) .ok_or(ResponseError::IndexNotFound(path.clone()))?; @@ -76,7 +77,7 @@ pub async fn get_all_documents( let limit = params.limit.unwrap_or(20); let reader = data.db.main_read_txn() - .map_err(|_| ResponseError::CreateTransaction)?; + .map_err(|err| ResponseError::Internal(err.to_string()))?; let documents_ids: Result, _> = index .documents_fields_counts @@ -123,19 +124,19 @@ async fn update_multiple_documents( params: web::Query, body: web::Json>, is_partial: bool -) -> Result { +) -> aweb::Result { let index = data.db.open_index(path.clone()) .ok_or(ResponseError::IndexNotFound(path.clone()))?; let reader = data.db.main_read_txn() - .map_err(|_| ResponseError::CreateTransaction)?; + .map_err(|err| ResponseError::Internal(err.to_string()))?; let mut schema = index .main .schema(&reader) - .map_err(|_| ResponseError::Schema)? - .ok_or(ResponseError::Schema)?; + .map_err(|err| ResponseError::Internal(err.to_string()))? + .ok_or(ResponseError::Internal("Impossible to retrieve the schema".to_string()))?; if schema.primary_key().is_none() { let id = match params.primary_key.clone() { @@ -143,19 +144,19 @@ async fn update_multiple_documents( None => { body.first() .and_then(|docs| find_primary_key(docs)) - .ok_or(ResponseError::InferPrimaryKey)? + .ok_or(ResponseError::BadRequest("Impossible to infer the primary key".to_string()))? } }; let mut writer = data.db.main_write_txn() - .map_err(|_| ResponseError::CreateTransaction)?; + .map_err(|err| ResponseError::Internal(err.to_string()))?; schema.set_primary_key(&id) .map_err(|e| ResponseError::Internal(e.to_string()))?; index.main.put_schema(&mut writer, &schema) .map_err(|e| ResponseError::Internal(e.to_string()))?; writer.commit() - .map_err(|_| ResponseError::CommitTransaction)?; + .map_err(|err| ResponseError::Internal(err.to_string()))?; } let mut document_addition = if is_partial { @@ -169,11 +170,11 @@ async fn update_multiple_documents( } let mut update_writer = data.db.update_write_txn() - .map_err(|_| ResponseError::CreateTransaction)?; + .map_err(|err| ResponseError::Internal(err.to_string()))?; let update_id = document_addition.finalize(&mut update_writer) .map_err(|e| ResponseError::Internal(e.to_string()))?; update_writer.commit() - .map_err(|_| ResponseError::CommitTransaction)?; + .map_err(|err| ResponseError::Internal(err.to_string()))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -184,7 +185,7 @@ pub async fn add_documents( path: web::Path, params: web::Query, body: web::Json> -) -> Result { +) -> aweb::Result { update_multiple_documents(data, path, params, body, false).await } @@ -194,7 +195,7 @@ pub async fn update_documents( path: web::Path, params: web::Query, body: web::Json> -) -> Result { +) -> aweb::Result { update_multiple_documents(data, path, params, body, true).await } @@ -203,13 +204,13 @@ pub async fn delete_documents( data: web::Data, path: web::Path, body: web::Json> -) -> Result { +) -> aweb::Result { let index = data.db.open_index(path.clone()) .ok_or(ResponseError::IndexNotFound(path.clone()))?; let mut writer = data.db.update_write_txn() - .map_err(|_| ResponseError::CreateTransaction)?; + .map_err(|err| ResponseError::Internal(err.to_string()))?; let mut documents_deletion = index.documents_deletion(); @@ -224,7 +225,7 @@ pub async fn delete_documents( .map_err(|e| ResponseError::Internal(e.to_string()))?; writer.commit() - .map_err(|_| ResponseError::CommitTransaction)?; + .map_err(|err| ResponseError::Internal(err.to_string()))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -233,19 +234,19 @@ pub async fn delete_documents( pub async fn clear_all_documents( data: web::Data, path: web::Path, -) -> Result { +) -> aweb::Result { let index = data.db.open_index(path.clone()) .ok_or(ResponseError::IndexNotFound(path.clone()))?; let mut writer = data.db.update_write_txn() - .map_err(|_| ResponseError::CreateTransaction)?; + .map_err(|err| ResponseError::Internal(err.to_string()))?; let update_id = index.clear_all(&mut writer) .map_err(|e| ResponseError::Internal(e.to_string()))?; writer.commit() - .map_err(|_| ResponseError::CommitTransaction)?; + .map_err(|err| ResponseError::Internal(err.to_string()))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } diff --git a/meilisearch-http/src/routes/health.rs b/meilisearch-http/src/routes/health.rs index 5d6654214..f1b92078d 100644 --- a/meilisearch-http/src/routes/health.rs +++ b/meilisearch-http/src/routes/health.rs @@ -1,5 +1,6 @@ use crate::error::ResponseError; -use actix_web::*; +use actix_web::{web, get, put, HttpResponse}; +use actix_web as aweb; use crate::Data; use heed::types::{Str, Unit}; use serde::Deserialize; @@ -9,14 +10,14 @@ const UNHEALTHY_KEY: &str = "_is_unhealthy"; #[get("/health")] pub async fn get_health( data: web::Data, -) -> Result { +) -> aweb::Result { let reader = data.db.main_read_txn() - .map_err(|_| ResponseError::CreateTransaction)?; + .map_err(|err| ResponseError::Internal(err.to_string()))?; let common_store = data.db.common_store(); if let Ok(Some(_)) = common_store.get::<_, Str, Unit>(&reader, UNHEALTHY_KEY) { - return Err(ResponseError::Maintenance)?; + return Err(ResponseError::Maintenance.into()); } Ok(HttpResponse::Ok().finish()) @@ -24,28 +25,28 @@ pub async fn get_health( pub async fn set_healthy( data: web::Data, -) -> Result { +) -> aweb::Result { let mut writer = data.db.main_write_txn() - .map_err(|_| ResponseError::CreateTransaction)?; + .map_err(|err| ResponseError::Internal(err.to_string()))?; let common_store = data.db.common_store(); common_store.delete::<_, Str>(&mut writer, UNHEALTHY_KEY) .map_err(|e| ResponseError::Internal(e.to_string()))?; writer.commit() - .map_err(|_| ResponseError::CommitTransaction)?; + .map_err(|err| ResponseError::Internal(err.to_string()))?; Ok(HttpResponse::Ok().finish()) } pub async fn set_unhealthy( data: web::Data, -) -> Result { +) -> aweb::Result { let mut writer = data.db.main_write_txn() - .map_err(|_| ResponseError::CreateTransaction)?; + .map_err(|err| ResponseError::Internal(err.to_string()))?; let common_store = data.db.common_store(); common_store.put::<_, Str, Unit>(&mut writer, UNHEALTHY_KEY, &()) .map_err(|e| ResponseError::Internal(e.to_string()))?; writer.commit() - .map_err(|_| ResponseError::CommitTransaction)?; + .map_err(|err| ResponseError::Internal(err.to_string()))?; Ok(HttpResponse::Ok().finish()) } @@ -59,7 +60,7 @@ pub struct HealtBody { pub async fn change_healthyness( data: web::Data, body: web::Json, -) -> Result { +) -> aweb::Result { if body.health { set_healthy(data).await } else { diff --git a/meilisearch-http/src/routes/index.rs b/meilisearch-http/src/routes/index.rs index e9449784d..5b55d652d 100644 --- a/meilisearch-http/src/routes/index.rs +++ b/meilisearch-http/src/routes/index.rs @@ -2,7 +2,9 @@ use chrono::{DateTime, Utc}; use log::error; use rand::seq::SliceRandom; use serde::{Deserialize, Serialize}; -use actix_web::*; +use actix_web::{web, get, post, delete, HttpResponse}; +use actix_web as aweb; +use meilisearch_core::UpdateStatus; use crate::error::ResponseError; use crate::Data; @@ -29,10 +31,10 @@ pub struct IndexResponse { #[get("/indexes")] pub async fn list_indexes( data: web::Data, -) -> Result>> { +) -> aweb::Result>> { let reader = data.db.main_read_txn() - .map_err(|_| ResponseError::CreateTransaction)?; + .map_err(|err| ResponseError::Internal(err.to_string()))?; let mut response_body = Vec::new(); @@ -82,13 +84,13 @@ pub async fn list_indexes( pub async fn get_index( data: web::Data, path: web::Path, -) -> Result> { +) -> aweb::Result> { let index = data.db.open_index(path.clone()) .ok_or(ResponseError::IndexNotFound(path.clone()))?; let reader = data.db.main_read_txn() - .map_err(|_| ResponseError::CreateTransaction)?; + .map_err(|err| ResponseError::Internal(err.to_string()))?; let name = index.main.name(&reader) .map_err(|e| ResponseError::Internal(e.to_string()))? @@ -129,10 +131,10 @@ pub struct IndexCreateRequest { pub async fn create_index( data: web::Data, body: web::Json -) -> Result> { +) -> aweb::Result> { if let (None, None) = (body.name.clone(), body.uid.clone()) { - return Err(ResponseError::BadRequest("Index creation must have an uid".to_string()))?; + return Err(ResponseError::BadRequest("Index creation must have an uid".to_string()).into()); } let uid = match body.uid.clone() { @@ -143,7 +145,7 @@ pub async fn create_index( { uid } else { - return Err(ResponseError::InvalidIndexUid)?; + return Err(ResponseError::InvalidIndexUid.into()); } } None => loop { @@ -158,7 +160,7 @@ pub async fn create_index( .map_err(|e| ResponseError::CreateIndex(e.to_string()))?; let mut writer = data.db.main_write_txn() - .map_err(|_| ResponseError::CreateTransaction)?; + .map_err(|err| ResponseError::Internal(err.to_string()))?; let name = body.name.clone().unwrap_or(uid.clone()); created_index.main.put_name(&mut writer, &name) @@ -187,7 +189,7 @@ pub async fn create_index( } writer.commit() - .map_err(|_| ResponseError::CommitTransaction)?; + .map_err(|err| ResponseError::Internal(err.to_string()))?; Ok(web::Json(IndexResponse { name, @@ -220,13 +222,13 @@ pub async fn update_index( data: web::Data, path: web::Path, body: web::Json -) -> Result> { +) -> aweb::Result> { let index = data.db.open_index(path.clone()) .ok_or(ResponseError::IndexNotFound(path.clone()))?; let mut writer = data.db.main_write_txn() - .map_err(|_| ResponseError::CreateTransaction)?; + .map_err(|err| ResponseError::Internal(err.to_string()))?; if let Some(name) = body.name.clone() { index.main.put_name(&mut writer, &name) @@ -238,7 +240,7 @@ pub async fn update_index( .map_err(|e| ResponseError::Internal(e.to_string()))? { match schema.primary_key() { Some(_) => { - return Err(ResponseError::BadRequest("The primary key cannot be updated".to_string()))?; + return Err(ResponseError::BadRequest("The primary key cannot be updated".to_string()).into()); } None => { schema @@ -254,10 +256,10 @@ pub async fn update_index( index.main.put_updated_at(&mut writer) .map_err(|e| ResponseError::Internal(e.to_string()))?; writer.commit() - .map_err(|_| ResponseError::CommitTransaction)?; + .map_err(|err| ResponseError::Internal(err.to_string()))?; let reader = data.db.main_read_txn() - .map_err(|_| ResponseError::CreateTransaction)?; + .map_err(|err| ResponseError::Internal(err.to_string()))?; let name = index.main.name(&reader) .map_err(|e| ResponseError::Internal(e.to_string()))? @@ -290,10 +292,50 @@ pub async fn update_index( pub async fn delete_index( data: web::Data, path: web::Path, -) -> Result { +) -> aweb::Result { data.db.delete_index(&path.to_string()) .map_err(|e| ResponseError::Internal(e.to_string()))?; HttpResponse::NoContent().await } + + +#[get("/indexes/{index_uid}/updates/{update_id}")] +pub async fn get_update_status( + data: web::Data, + path: web::Path<(String, u64)>, +) -> aweb::Result> { + + let index = data.db.open_index(path.0.clone()) + .ok_or(ResponseError::IndexNotFound(path.0.clone()))?; + + let reader = data.db.update_read_txn() + .map_err(|err| ResponseError::Internal(err.to_string()))?; + + let status = index.update_status(&reader, path.1) + .map_err(|e| ResponseError::Internal(e.to_string()))?; + + match status { + Some(status) => Ok(web::Json(status)), + None => Err(ResponseError::NotFound(format!("Update {} not found", path.1)).into()) + } +} + +#[get("/indexes/{index_uid}/updates")] +pub async fn get_all_updates_status( + data: web::Data, + path: web::Path, +) -> aweb::Result>> { + + let index = data.db.open_index(path.clone()) + .ok_or(ResponseError::IndexNotFound(path.clone()))?; + + let reader = data.db.update_read_txn() + .map_err(|err| ResponseError::Internal(err.to_string()))?; + + let response = index.all_updates_status(&reader) + .map_err(|err| ResponseError::Internal(err.to_string()))?; + + Ok(web::Json(response)) +} diff --git a/meilisearch-http/src/routes/key.rs b/meilisearch-http/src/routes/key.rs index a7926d9dc..d807840b7 100644 --- a/meilisearch-http/src/routes/key.rs +++ b/meilisearch-http/src/routes/key.rs @@ -1,5 +1,5 @@ use crate::Data; -use actix_web::*; +use actix_web::{web, get}; use serde::Serialize; diff --git a/meilisearch-http/src/routes/mod.rs b/meilisearch-http/src/routes/mod.rs index e824f925e..165954b60 100644 --- a/meilisearch-http/src/routes/mod.rs +++ b/meilisearch-http/src/routes/mod.rs @@ -1,4 +1,4 @@ -use actix_web::*; +use actix_web::{get, HttpResponse}; use serde::Serialize; use log::error; use meilisearch_core::ProcessedUpdateResult; @@ -10,24 +10,21 @@ pub mod health; pub mod index; pub mod key; pub mod search; -// pub mod setting; pub mod stats; +// pub mod setting; // pub mod stop_words; // pub mod synonym; -pub mod update; #[derive(Default, Serialize)] #[serde(rename_all = "camelCase")] pub struct IndexUpdateResponse { pub update_id: u64, - pub see_more: String, } impl IndexUpdateResponse { pub fn with_id(update_id: u64) -> Self { Self { update_id, - see_more: "https://docs.meilisearch.com/guides/advanced_guides/asynchronous_updates.html".to_string() } } } diff --git a/meilisearch-http/src/routes/search.rs b/meilisearch-http/src/routes/search.rs index 15040592c..7671f0e57 100644 --- a/meilisearch-http/src/routes/search.rs +++ b/meilisearch-http/src/routes/search.rs @@ -6,7 +6,8 @@ use log::warn; use meilisearch_core::Index; use rayon::iter::{IntoParallelIterator, ParallelIterator}; use serde::{Deserialize, Serialize}; -use actix_web::*; +use actix_web::{web, get, post}; +use actix_web as aweb; use crate::error::ResponseError; use crate::helpers::meilisearch::{Error, IndexSearchExt, SearchHit, SearchResult}; @@ -32,19 +33,19 @@ pub async fn search_with_url_query( data: web::Data, path: web::Path, params: web::Query, -) -> Result> { +) -> aweb::Result> { let index = data.db.open_index(path.clone()) .ok_or(ResponseError::IndexNotFound(path.clone()))?; let reader = data.db.main_read_txn() - .map_err(|_| ResponseError::CreateTransaction)?; + .map_err(|err| ResponseError::Internal(err.to_string()))?; let schema = index .main .schema(&reader) - .map_err(|_| ResponseError::Schema)? - .ok_or(ResponseError::Schema)?; + .map_err(|err| ResponseError::Internal(err.to_string()))? + .ok_or(ResponseError::Internal("Impossible to retrieve the schema".to_string()))?; let mut search_builder = index.new_search(params.q.clone()); @@ -142,8 +143,8 @@ pub async fn search_with_url_query( let response = match search_builder.search(&reader) { Ok(response) => response, - Err(Error::Internal(message)) => return Err(ResponseError::Internal(message))?, - Err(others) => return Err(ResponseError::BadRequest(others.to_string()))?, + Err(Error::Internal(message)) => return Err(ResponseError::Internal(message).into()), + Err(others) => return Err(ResponseError::BadRequest(others.to_string()).into()), }; Ok(web::Json(response)) @@ -179,7 +180,7 @@ pub struct SearchMultiBodyResponse { pub async fn search_multi_index( data: web::Data, body: web::Json, -) -> Result> { +) -> aweb::Result> { let mut index_list = body.clone().indexes; diff --git a/meilisearch-http/src/routes/stats.rs b/meilisearch-http/src/routes/stats.rs index e2e82122d..47cda692a 100644 --- a/meilisearch-http/src/routes/stats.rs +++ b/meilisearch-http/src/routes/stats.rs @@ -1,6 +1,7 @@ use std::collections::HashMap; -use actix_web::*; +use actix_web as aweb; +use actix_web::{web, get}; use chrono::{DateTime, Utc}; use log::error; use pretty_bytes::converter::convert; @@ -23,12 +24,12 @@ pub struct IndexStatsResponse { pub async fn index_stats( data: web::Data, path: web::Path, -) -> Result> { +) -> aweb::Result> { let index = data.db.open_index(path.clone()) .ok_or(ResponseError::IndexNotFound(path.clone()))?; let reader = data.db.main_read_txn() - .map_err(|_| ResponseError::CreateTransaction)?; + .map_err(|err| ResponseError::Internal(err.to_string()))?; let number_of_documents = index.main.number_of_documents(&reader) .map_err(|err| ResponseError::Internal(err.to_string()))?; @@ -38,7 +39,7 @@ pub async fn index_stats( .unwrap_or_default(); let update_reader = data.db.update_read_txn() - .map_err(|_| ResponseError::CreateTransaction)?; + .map_err(|err| ResponseError::Internal(err.to_string()))?; let is_indexing = data .is_indexing(&update_reader, &path) @@ -63,14 +64,14 @@ pub struct StatsResult { #[get("/stats")] pub async fn get_stats( data: web::Data, -) -> Result> { +) -> aweb::Result> { let mut index_list = HashMap::new(); let reader = data.db.main_read_txn() - .map_err(|_| ResponseError::CreateTransaction)?; + .map_err(|err| ResponseError::Internal(err.to_string()))?; let update_reader = data.db.update_read_txn() - .map_err(|_| ResponseError::CreateTransaction)?; + .map_err(|err| ResponseError::Internal(err.to_string()))?; let indexes_set = data.db.indexes_uids(); for index_uid in indexes_set { diff --git a/meilisearch-http/src/routes/update.rs b/meilisearch-http/src/routes/update.rs deleted file mode 100644 index 8a99ea6d4..000000000 --- a/meilisearch-http/src/routes/update.rs +++ /dev/null @@ -1,44 +0,0 @@ -use actix_web::*; -use meilisearch_core::UpdateStatus; - -use crate::error::ResponseError; -use crate::Data; - -#[get("/indexes/{index_uid}/updates/{update_id}")] -pub async fn get_update_status( - data: web::Data, - path: web::Path<(String, u64)>, -) -> Result> { - - let index = data.db.open_index(path.0.clone()) - .ok_or(ResponseError::IndexNotFound(path.0.clone()))?; - - let reader = data.db.update_read_txn() - .map_err(|_| ResponseError::CreateTransaction)?; - - let status = index.update_status(&reader, path.1) - .map_err(|e| ResponseError::Internal(e.to_string()))?; - - match status { - Some(status) => Ok(web::Json(status)), - None => Err(ResponseError::UpdateNotFound(path.1))? - } -} - -#[get("/indexes/{index_uid}/updates")] -pub async fn get_all_updates_status( - data: web::Data, - path: web::Path, -) -> Result>> { - - let index = data.db.open_index(path.clone()) - .ok_or(ResponseError::IndexNotFound(path.clone()))?; - - let reader = data.db.update_read_txn() - .map_err(|_| ResponseError::CreateTransaction)?; - - let response = index.all_updates_status(&reader) - .map_err(|err| ResponseError::Internal(err.to_string()))?; - - Ok(web::Json(response)) -} From b08f6737aceac074d8cfc49b5f67a06f22cd7481 Mon Sep 17 00:00:00 2001 From: Quentin de Quelen Date: Thu, 9 Apr 2020 11:11:48 +0200 Subject: [PATCH 07/21] change param tuples by struct add settings endpoint; wip --- meilisearch-http/src/routes/document.rs | 58 +-- meilisearch-http/src/routes/index.rs | 42 +- meilisearch-http/src/routes/mod.rs | 9 +- meilisearch-http/src/routes/search.rs | 7 +- meilisearch-http/src/routes/setting.rs | 528 ++++++++++++------------ meilisearch-http/src/routes/stats.rs | 9 +- 6 files changed, 343 insertions(+), 310 deletions(-) diff --git a/meilisearch-http/src/routes/document.rs b/meilisearch-http/src/routes/document.rs index 825637526..99e40e481 100644 --- a/meilisearch-http/src/routes/document.rs +++ b/meilisearch-http/src/routes/document.rs @@ -7,25 +7,31 @@ use actix_web as aweb; use crate::error::ResponseError; use crate::Data; -use crate::routes::IndexUpdateResponse; +use crate::routes::{IndexUpdateResponse, IndexParam}; type Document = IndexMap; +#[derive(Default, Deserialize)] +pub struct DocumentParam { + index_uid: String, + document_id: String +} + #[get("/indexes/{index_uid}/documents/{document_id}")] pub async fn get_document( data: web::Data, - path: web::Path<(String, String)>, + path: web::Path, ) -> aweb::Result> { - let index = data.db.open_index(&path.0) - .ok_or(ResponseError::IndexNotFound(path.0.clone()))?; - let document_id = meilisearch_core::serde::compute_document_id(path.1.clone()); + let index = data.db.open_index(&path.index_uid) + .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; + let document_id = meilisearch_core::serde::compute_document_id(path.document_id.clone()); let reader = data.db.main_read_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; let response = index.document::(&reader, None, document_id) - .map_err(|_| ResponseError::DocumentNotFound(path.1.clone()))? - .ok_or(ResponseError::DocumentNotFound(path.1.clone()))?; + .map_err(|_| ResponseError::DocumentNotFound(path.document_id.clone()))? + .ok_or(ResponseError::DocumentNotFound(path.document_id.clone()))?; Ok(web::Json(response)) } @@ -33,11 +39,11 @@ pub async fn get_document( #[delete("/indexes/{index_uid}/documents/{document_id}")] pub async fn delete_document( data: web::Data, - path: web::Path<(String, String)>, + path: web::Path, ) -> aweb::Result { - let index = data.db.open_index(&path.0) - .ok_or(ResponseError::IndexNotFound(path.0.clone()))?; - let document_id = meilisearch_core::serde::compute_document_id(path.1.clone()); + let index = data.db.open_index(&path.index_uid) + .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; + let document_id = meilisearch_core::serde::compute_document_id(path.document_id.clone()); let mut update_writer = data.db.update_write_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; @@ -66,12 +72,12 @@ pub struct BrowseQuery { #[get("/indexes/{index_uid}/documents")] pub async fn get_all_documents( data: web::Data, - path: web::Path, + path: web::Path, params: web::Query, ) -> aweb::Result>> { - let index = data.db.open_index(path.clone()) - .ok_or(ResponseError::IndexNotFound(path.clone()))?; + let index = data.db.open_index(&path.index_uid) + .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; let offset = params.offset.unwrap_or(0); let limit = params.limit.unwrap_or(20); @@ -82,7 +88,7 @@ pub async fn get_all_documents( let documents_ids: Result, _> = index .documents_fields_counts .documents_ids(&reader) - .map_err(|_| ResponseError::Internal(path.clone()))? + .map_err(|_| ResponseError::Internal(path.index_uid.clone()))? .skip(offset) .take(limit) .collect(); @@ -120,14 +126,14 @@ pub struct UpdateDocumentsQuery { async fn update_multiple_documents( data: web::Data, - path: web::Path, + path: web::Path, params: web::Query, body: web::Json>, is_partial: bool ) -> aweb::Result { - let index = data.db.open_index(path.clone()) - .ok_or(ResponseError::IndexNotFound(path.clone()))?; + let index = data.db.open_index(path.index_uid.clone()) + .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; let reader = data.db.main_read_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; @@ -182,7 +188,7 @@ async fn update_multiple_documents( #[post("/indexes/{index_uid}/documents")] pub async fn add_documents( data: web::Data, - path: web::Path, + path: web::Path, params: web::Query, body: web::Json> ) -> aweb::Result { @@ -192,7 +198,7 @@ pub async fn add_documents( #[put("/indexes/{index_uid}/documents")] pub async fn update_documents( data: web::Data, - path: web::Path, + path: web::Path, params: web::Query, body: web::Json> ) -> aweb::Result { @@ -202,12 +208,12 @@ pub async fn update_documents( #[post("/indexes/{index_uid}/documents/delete-batch")] pub async fn delete_documents( data: web::Data, - path: web::Path, + path: web::Path, body: web::Json> ) -> aweb::Result { - let index = data.db.open_index(path.clone()) - .ok_or(ResponseError::IndexNotFound(path.clone()))?; + let index = data.db.open_index(path.index_uid.clone()) + .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; let mut writer = data.db.update_write_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; @@ -233,11 +239,11 @@ pub async fn delete_documents( #[delete("/indexes/{index_uid}/documents")] pub async fn clear_all_documents( data: web::Data, - path: web::Path, + path: web::Path, ) -> aweb::Result { - let index = data.db.open_index(path.clone()) - .ok_or(ResponseError::IndexNotFound(path.clone()))?; + let index = data.db.open_index(path.index_uid.clone()) + .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; let mut writer = data.db.update_write_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; diff --git a/meilisearch-http/src/routes/index.rs b/meilisearch-http/src/routes/index.rs index 5b55d652d..ec7f65315 100644 --- a/meilisearch-http/src/routes/index.rs +++ b/meilisearch-http/src/routes/index.rs @@ -8,6 +8,7 @@ use meilisearch_core::UpdateStatus; use crate::error::ResponseError; use crate::Data; +use crate::routes::IndexParam; fn generate_uid() -> String { let mut rng = rand::thread_rng(); @@ -83,11 +84,11 @@ pub async fn list_indexes( #[get("/indexes/{index_uid}")] pub async fn get_index( data: web::Data, - path: web::Path, + path: web::Path, ) -> aweb::Result> { - let index = data.db.open_index(path.clone()) - .ok_or(ResponseError::IndexNotFound(path.clone()))?; + let index = data.db.open_index(path.index_uid.clone()) + .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; let reader = data.db.main_read_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; @@ -112,7 +113,7 @@ pub async fn get_index( Ok(web::Json(IndexResponse { name, - uid: path.to_string(), + uid: path.index_uid.clone(), created_at, updated_at, primary_key, @@ -220,12 +221,12 @@ pub struct UpdateIndexResponse { #[post("/indexes/{index_uid}")] pub async fn update_index( data: web::Data, - path: web::Path, + path: web::Path, body: web::Json ) -> aweb::Result> { - let index = data.db.open_index(path.clone()) - .ok_or(ResponseError::IndexNotFound(path.clone()))?; + let index = data.db.open_index(path.index_uid.clone()) + .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; let mut writer = data.db.main_write_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; @@ -281,7 +282,7 @@ pub async fn update_index( Ok(web::Json(IndexResponse { name, - uid: path.clone(), + uid: path.index_uid.clone(), created_at, updated_at, primary_key, @@ -291,45 +292,50 @@ pub async fn update_index( #[delete("/indexes/{index_uid}")] pub async fn delete_index( data: web::Data, - path: web::Path, + path: web::Path, ) -> aweb::Result { - data.db.delete_index(&path.to_string()) + data.db.delete_index(&path.index_uid) .map_err(|e| ResponseError::Internal(e.to_string()))?; HttpResponse::NoContent().await } +#[derive(Default, Deserialize)] +pub struct UpdateParam { + index_uid: String, + update_id: u64 +} #[get("/indexes/{index_uid}/updates/{update_id}")] pub async fn get_update_status( data: web::Data, - path: web::Path<(String, u64)>, + path: web::Path, ) -> aweb::Result> { - let index = data.db.open_index(path.0.clone()) - .ok_or(ResponseError::IndexNotFound(path.0.clone()))?; + let index = data.db.open_index(path.index_uid.clone()) + .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; let reader = data.db.update_read_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; - let status = index.update_status(&reader, path.1) + let status = index.update_status(&reader, path.update_id) .map_err(|e| ResponseError::Internal(e.to_string()))?; match status { Some(status) => Ok(web::Json(status)), - None => Err(ResponseError::NotFound(format!("Update {} not found", path.1)).into()) + None => Err(ResponseError::NotFound(format!("Update {} not found", path.update_id)).into()) } } #[get("/indexes/{index_uid}/updates")] pub async fn get_all_updates_status( data: web::Data, - path: web::Path, + path: web::Path, ) -> aweb::Result>> { - let index = data.db.open_index(path.clone()) - .ok_or(ResponseError::IndexNotFound(path.clone()))?; + let index = data.db.open_index(path.index_uid.clone()) + .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; let reader = data.db.update_read_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; diff --git a/meilisearch-http/src/routes/mod.rs b/meilisearch-http/src/routes/mod.rs index 165954b60..78fd1a25c 100644 --- a/meilisearch-http/src/routes/mod.rs +++ b/meilisearch-http/src/routes/mod.rs @@ -1,5 +1,5 @@ use actix_web::{get, HttpResponse}; -use serde::Serialize; +use serde::{Serialize, Deserialize}; use log::error; use meilisearch_core::ProcessedUpdateResult; @@ -11,10 +11,15 @@ pub mod index; pub mod key; pub mod search; pub mod stats; -// pub mod setting; +pub mod setting; // pub mod stop_words; // pub mod synonym; +#[derive(Default, Deserialize)] +pub struct IndexParam { + index_uid: String +} + #[derive(Default, Serialize)] #[serde(rename_all = "camelCase")] pub struct IndexUpdateResponse { diff --git a/meilisearch-http/src/routes/search.rs b/meilisearch-http/src/routes/search.rs index 7671f0e57..afdc96fcb 100644 --- a/meilisearch-http/src/routes/search.rs +++ b/meilisearch-http/src/routes/search.rs @@ -12,6 +12,7 @@ use actix_web as aweb; use crate::error::ResponseError; use crate::helpers::meilisearch::{Error, IndexSearchExt, SearchHit, SearchResult}; use crate::Data; +use crate::routes::IndexParam; #[derive(Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] @@ -31,12 +32,12 @@ pub struct SearchQuery { #[get("/indexes/{index_uid}/search")] pub async fn search_with_url_query( data: web::Data, - path: web::Path, + path: web::Path, params: web::Query, ) -> aweb::Result> { - let index = data.db.open_index(path.clone()) - .ok_or(ResponseError::IndexNotFound(path.clone()))?; + let index = data.db.open_index(path.index_uid.clone()) + .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; let reader = data.db.main_read_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; diff --git a/meilisearch-http/src/routes/setting.rs b/meilisearch-http/src/routes/setting.rs index 9cdbcb7a3..5de23a435 100644 --- a/meilisearch-http/src/routes/setting.rs +++ b/meilisearch-http/src/routes/setting.rs @@ -1,47 +1,61 @@ use meilisearch_core::settings::{Settings, SettingsUpdate, UpdateState, DEFAULT_RANKING_RULES}; use std::collections::{BTreeMap, BTreeSet, HashSet}; -use tide::{Request, Response}; +use actix_web::{web, get, post, put, delete, HttpResponse}; +use actix_web as aweb; -use crate::error::{ResponseError, SResult}; -use crate::helpers::tide::RequestExt; -use crate::helpers::tide::ACL::*; -use crate::routes::document::IndexUpdateResponse; +use crate::error::{ResponseError}; use crate::Data; +use crate::routes::{IndexUpdateResponse, IndexParam}; -pub async fn get_all(ctx: Request) -> SResult { - ctx.is_allowed(Private)?; - let index = ctx.index()?; - let db = &ctx.state().db; - let reader = db.main_read_txn()?; +#[get("/indexes/{index_uid}/settings")] +pub async fn get_all( + data: web::Data, + path: web::Path, +) -> aweb::Result { + let index = data.db.open_index(&path.index_uid) + .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; - let stop_words_fst = index.main.stop_words_fst(&reader)?; - let stop_words = stop_words_fst.unwrap_or_default().stream().into_strs()?; + let reader = data.db.main_read_txn() + .map_err(|err| ResponseError::Internal(err.to_string()))?; + + let stop_words_fst = index.main.stop_words_fst(&reader) + .map_err(|err| ResponseError::Internal(err.to_string()))?; + let stop_words = stop_words_fst.unwrap_or_default().stream().into_strs() + .map_err(|err| ResponseError::Internal(err.to_string()))?; let stop_words: BTreeSet = stop_words.into_iter().collect(); - let synonyms_fst = index.main.synonyms_fst(&reader)?.unwrap_or_default(); - let synonyms_list = synonyms_fst.stream().into_strs()?; + let synonyms_fst = index.main.synonyms_fst(&reader) + .map_err(|err| ResponseError::Internal(err.to_string()))? + .unwrap_or_default(); + let synonyms_list = synonyms_fst.stream().into_strs() + .map_err(|err| ResponseError::Internal(err.to_string()))?; let mut synonyms = BTreeMap::new(); let index_synonyms = &index.synonyms; for synonym in synonyms_list { - let alternative_list = index_synonyms.synonyms(&reader, synonym.as_bytes())?; + let alternative_list = index_synonyms.synonyms(&reader, synonym.as_bytes()) + .map_err(|err| ResponseError::Internal(err.to_string()))?; if let Some(list) = alternative_list { - let list = list.stream().into_strs()?; + let list = list.stream().into_strs() + .map_err(|err| ResponseError::Internal(err.to_string()))?; synonyms.insert(synonym, list); } } let ranking_rules = index .main - .ranking_rules(&reader)? + .ranking_rules(&reader) + .map_err(|err| ResponseError::Internal(err.to_string()))? .unwrap_or(DEFAULT_RANKING_RULES.to_vec()) .into_iter() .map(|r| r.to_string()) .collect(); - let distinct_attribute = index.main.distinct_attribute(&reader)?; + let distinct_attribute = index.main.distinct_attribute(&reader) + .map_err(|err| ResponseError::Internal(err.to_string()))?; - let schema = index.main.schema(&reader)?; + let schema = index.main.schema(&reader) + .map_err(|err| ResponseError::Internal(err.to_string()))?; let searchable_attributes = schema.clone().map(|s| { s.indexed_name() @@ -69,306 +83,306 @@ pub async fn get_all(ctx: Request) -> SResult { accept_new_fields: Some(accept_new_fields), }; - Ok(tide::Response::new(200).body_json(&settings).unwrap()) + Ok(HttpResponse::Ok().json(settings)) } -pub async fn update_all(mut ctx: Request) -> SResult { - ctx.is_allowed(Private)?; - let index = ctx.index()?; - let settings: Settings = - ctx.body_json().await.map_err(ResponseError::bad_request)?; - let db = &ctx.state().db; +// pub async fn update_all(mut ctx: Request) -> SResult { +// ctx.is_allowed(Private)?; +// let index = ctx.index()?; +// let settings: Settings = +// ctx.body_json().await.map_err(ResponseError::bad_request)?; +// let db = &ctx.state().db; - let mut writer = db.update_write_txn()?; - let settings = settings.into_update().map_err(ResponseError::bad_request)?; - let update_id = index.settings_update(&mut writer, settings)?; - writer.commit()?; +// let mut writer = db.update_write_txn()?; +// let settings = settings.into_update().map_err(ResponseError::bad_request)?; +// let update_id = index.settings_update(&mut writer, settings)?; +// writer.commit()?; - let response_body = IndexUpdateResponse { update_id }; - Ok(tide::Response::new(202).body_json(&response_body)?) -} +// let response_body = IndexUpdateResponse { update_id }; +// Ok(tide::Response::new(202).body_json(&response_body)?) +// } -pub async fn delete_all(ctx: Request) -> SResult { - ctx.is_allowed(Private)?; - let index = ctx.index()?; - let db = &ctx.state().db; - let mut writer = db.update_write_txn()?; +// pub async fn delete_all(ctx: Request) -> SResult { +// ctx.is_allowed(Private)?; +// let index = ctx.index()?; +// let db = &ctx.state().db; +// let mut writer = db.update_write_txn()?; - let settings = SettingsUpdate { - ranking_rules: UpdateState::Clear, - distinct_attribute: UpdateState::Clear, - primary_key: UpdateState::Clear, - searchable_attributes: UpdateState::Clear, - displayed_attributes: UpdateState::Clear, - stop_words: UpdateState::Clear, - synonyms: UpdateState::Clear, - accept_new_fields: UpdateState::Clear, - }; +// let settings = SettingsUpdate { +// ranking_rules: UpdateState::Clear, +// distinct_attribute: UpdateState::Clear, +// primary_key: UpdateState::Clear, +// searchable_attributes: UpdateState::Clear, +// displayed_attributes: UpdateState::Clear, +// stop_words: UpdateState::Clear, +// synonyms: UpdateState::Clear, +// accept_new_fields: UpdateState::Clear, +// }; - let update_id = index.settings_update(&mut writer, settings)?; +// let update_id = index.settings_update(&mut writer, settings)?; - writer.commit()?; +// writer.commit()?; - let response_body = IndexUpdateResponse { update_id }; - Ok(tide::Response::new(202).body_json(&response_body)?) -} +// let response_body = IndexUpdateResponse { update_id }; +// Ok(tide::Response::new(202).body_json(&response_body)?) +// } -pub async fn get_rules(ctx: Request) -> SResult { - ctx.is_allowed(Private)?; - let index = ctx.index()?; - let db = &ctx.state().db; - let reader = db.main_read_txn()?; +// pub async fn get_rules(ctx: Request) -> SResult { +// ctx.is_allowed(Private)?; +// let index = ctx.index()?; +// let db = &ctx.state().db; +// let reader = db.main_read_txn()?; - let ranking_rules = index - .main - .ranking_rules(&reader)? - .unwrap_or(DEFAULT_RANKING_RULES.to_vec()) - .into_iter() - .map(|r| r.to_string()) - .collect::>(); +// let ranking_rules = index +// .main +// .ranking_rules(&reader)? +// .unwrap_or(DEFAULT_RANKING_RULES.to_vec()) +// .into_iter() +// .map(|r| r.to_string()) +// .collect::>(); - Ok(tide::Response::new(200).body_json(&ranking_rules).unwrap()) -} +// Ok(tide::Response::new(200).body_json(&ranking_rules).unwrap()) +// } -pub async fn update_rules(mut ctx: Request) -> SResult { - ctx.is_allowed(Private)?; - let index = ctx.index()?; - let ranking_rules: Option> = - ctx.body_json().await.map_err(ResponseError::bad_request)?; - let db = &ctx.state().db; +// pub async fn update_rules(mut ctx: Request) -> SResult { +// ctx.is_allowed(Private)?; +// let index = ctx.index()?; +// let ranking_rules: Option> = +// ctx.body_json().await.map_err(ResponseError::bad_request)?; +// let db = &ctx.state().db; - let settings = Settings { - ranking_rules: Some(ranking_rules), - ..Settings::default() - }; +// let settings = Settings { +// ranking_rules: Some(ranking_rules), +// ..Settings::default() +// }; - let mut writer = db.update_write_txn()?; - let settings = settings.into_update().map_err(ResponseError::bad_request)?; - let update_id = index.settings_update(&mut writer, settings)?; - writer.commit()?; +// let mut writer = db.update_write_txn()?; +// let settings = settings.into_update().map_err(ResponseError::bad_request)?; +// let update_id = index.settings_update(&mut writer, settings)?; +// writer.commit()?; - let response_body = IndexUpdateResponse { update_id }; - Ok(tide::Response::new(202).body_json(&response_body)?) -} +// let response_body = IndexUpdateResponse { update_id }; +// Ok(tide::Response::new(202).body_json(&response_body)?) +// } -pub async fn delete_rules(ctx: Request) -> SResult { - ctx.is_allowed(Private)?; - let index = ctx.index()?; - let db = &ctx.state().db; - let mut writer = db.update_write_txn()?; +// pub async fn delete_rules(ctx: Request) -> SResult { +// ctx.is_allowed(Private)?; +// let index = ctx.index()?; +// let db = &ctx.state().db; +// let mut writer = db.update_write_txn()?; - let settings = SettingsUpdate { - ranking_rules: UpdateState::Clear, - ..SettingsUpdate::default() - }; +// let settings = SettingsUpdate { +// ranking_rules: UpdateState::Clear, +// ..SettingsUpdate::default() +// }; - let update_id = index.settings_update(&mut writer, settings)?; +// let update_id = index.settings_update(&mut writer, settings)?; - writer.commit()?; +// writer.commit()?; - let response_body = IndexUpdateResponse { update_id }; - Ok(tide::Response::new(202).body_json(&response_body)?) -} +// let response_body = IndexUpdateResponse { update_id }; +// Ok(tide::Response::new(202).body_json(&response_body)?) +// } -pub async fn get_distinct(ctx: Request) -> SResult { - ctx.is_allowed(Private)?; - let index = ctx.index()?; - let db = &ctx.state().db; - let reader = db.main_read_txn()?; +// pub async fn get_distinct(ctx: Request) -> SResult { +// ctx.is_allowed(Private)?; +// let index = ctx.index()?; +// let db = &ctx.state().db; +// let reader = db.main_read_txn()?; - let distinct_attribute = index.main.distinct_attribute(&reader)?; +// let distinct_attribute = index.main.distinct_attribute(&reader)?; - Ok(tide::Response::new(200) - .body_json(&distinct_attribute) - .unwrap()) -} +// Ok(tide::Response::new(200) +// .body_json(&distinct_attribute) +// .unwrap()) +// } -pub async fn update_distinct(mut ctx: Request) -> SResult { - ctx.is_allowed(Private)?; - let index = ctx.index()?; - let distinct_attribute: Option = - ctx.body_json().await.map_err(ResponseError::bad_request)?; - let db = &ctx.state().db; +// pub async fn update_distinct(mut ctx: Request) -> SResult { +// ctx.is_allowed(Private)?; +// let index = ctx.index()?; +// let distinct_attribute: Option = +// ctx.body_json().await.map_err(ResponseError::bad_request)?; +// let db = &ctx.state().db; - let settings = Settings { - distinct_attribute: Some(distinct_attribute), - ..Settings::default() - }; +// let settings = Settings { +// distinct_attribute: Some(distinct_attribute), +// ..Settings::default() +// }; - let mut writer = db.update_write_txn()?; - let settings = settings.into_update().map_err(ResponseError::bad_request)?; - let update_id = index.settings_update(&mut writer, settings)?; - writer.commit()?; +// let mut writer = db.update_write_txn()?; +// let settings = settings.into_update().map_err(ResponseError::bad_request)?; +// let update_id = index.settings_update(&mut writer, settings)?; +// writer.commit()?; - let response_body = IndexUpdateResponse { update_id }; - Ok(tide::Response::new(202).body_json(&response_body)?) -} +// let response_body = IndexUpdateResponse { update_id }; +// Ok(tide::Response::new(202).body_json(&response_body)?) +// } -pub async fn delete_distinct(ctx: Request) -> SResult { - ctx.is_allowed(Private)?; - let index = ctx.index()?; - let db = &ctx.state().db; - let mut writer = db.update_write_txn()?; +// pub async fn delete_distinct(ctx: Request) -> SResult { +// ctx.is_allowed(Private)?; +// let index = ctx.index()?; +// let db = &ctx.state().db; +// let mut writer = db.update_write_txn()?; - let settings = SettingsUpdate { - distinct_attribute: UpdateState::Clear, - ..SettingsUpdate::default() - }; +// let settings = SettingsUpdate { +// distinct_attribute: UpdateState::Clear, +// ..SettingsUpdate::default() +// }; - let update_id = index.settings_update(&mut writer, settings)?; +// let update_id = index.settings_update(&mut writer, settings)?; - writer.commit()?; +// writer.commit()?; - let response_body = IndexUpdateResponse { update_id }; - Ok(tide::Response::new(202).body_json(&response_body)?) -} +// let response_body = IndexUpdateResponse { update_id }; +// Ok(tide::Response::new(202).body_json(&response_body)?) +// } -pub async fn get_searchable(ctx: Request) -> SResult { - ctx.is_allowed(Private)?; - let index = ctx.index()?; - let db = &ctx.state().db; - let reader = db.main_read_txn()?; +// pub async fn get_searchable(ctx: Request) -> SResult { +// ctx.is_allowed(Private)?; +// let index = ctx.index()?; +// let db = &ctx.state().db; +// let reader = db.main_read_txn()?; - let schema = index.main.schema(&reader)?; +// let schema = index.main.schema(&reader)?; - let searchable_attributes: Option> = - schema.map(|s| s.indexed_name().iter().map(|i| (*i).to_string()).collect()); +// let searchable_attributes: Option> = +// schema.map(|s| s.indexed_name().iter().map(|i| (*i).to_string()).collect()); - Ok(tide::Response::new(200) - .body_json(&searchable_attributes) - .unwrap()) -} +// Ok(tide::Response::new(200) +// .body_json(&searchable_attributes) +// .unwrap()) +// } -pub async fn update_searchable(mut ctx: Request) -> SResult { - ctx.is_allowed(Private)?; - let index = ctx.index()?; - let searchable_attributes: Option> = - ctx.body_json().await.map_err(ResponseError::bad_request)?; - let db = &ctx.state().db; +// pub async fn update_searchable(mut ctx: Request) -> SResult { +// ctx.is_allowed(Private)?; +// let index = ctx.index()?; +// let searchable_attributes: Option> = +// ctx.body_json().await.map_err(ResponseError::bad_request)?; +// let db = &ctx.state().db; - let settings = Settings { - searchable_attributes: Some(searchable_attributes), - ..Settings::default() - }; +// let settings = Settings { +// searchable_attributes: Some(searchable_attributes), +// ..Settings::default() +// }; - let mut writer = db.update_write_txn()?; - let settings = settings.into_update().map_err(ResponseError::bad_request)?; - let update_id = index.settings_update(&mut writer, settings)?; - writer.commit()?; +// let mut writer = db.update_write_txn()?; +// let settings = settings.into_update().map_err(ResponseError::bad_request)?; +// let update_id = index.settings_update(&mut writer, settings)?; +// writer.commit()?; - let response_body = IndexUpdateResponse { update_id }; - Ok(tide::Response::new(202).body_json(&response_body)?) -} +// let response_body = IndexUpdateResponse { update_id }; +// Ok(tide::Response::new(202).body_json(&response_body)?) +// } -pub async fn delete_searchable(ctx: Request) -> SResult { - ctx.is_allowed(Private)?; - let index = ctx.index()?; - let db = &ctx.state().db; +// pub async fn delete_searchable(ctx: Request) -> SResult { +// ctx.is_allowed(Private)?; +// let index = ctx.index()?; +// let db = &ctx.state().db; - let settings = SettingsUpdate { - searchable_attributes: UpdateState::Clear, - ..SettingsUpdate::default() - }; +// let settings = SettingsUpdate { +// searchable_attributes: UpdateState::Clear, +// ..SettingsUpdate::default() +// }; - let mut writer = db.update_write_txn()?; - let update_id = index.settings_update(&mut writer, settings)?; - writer.commit()?; +// let mut writer = db.update_write_txn()?; +// let update_id = index.settings_update(&mut writer, settings)?; +// writer.commit()?; - let response_body = IndexUpdateResponse { update_id }; - Ok(tide::Response::new(202).body_json(&response_body)?) -} +// let response_body = IndexUpdateResponse { update_id }; +// Ok(tide::Response::new(202).body_json(&response_body)?) +// } -pub async fn displayed(ctx: Request) -> SResult { - ctx.is_allowed(Private)?; - let index = ctx.index()?; - let db = &ctx.state().db; - let reader = db.main_read_txn()?; +// pub async fn displayed(ctx: Request) -> SResult { +// ctx.is_allowed(Private)?; +// let index = ctx.index()?; +// let db = &ctx.state().db; +// let reader = db.main_read_txn()?; - let schema = index.main.schema(&reader)?; +// let schema = index.main.schema(&reader)?; - let displayed_attributes: Option> = schema.map(|s| { - s.displayed_name() - .iter() - .map(|i| (*i).to_string()) - .collect() - }); +// let displayed_attributes: Option> = schema.map(|s| { +// s.displayed_name() +// .iter() +// .map(|i| (*i).to_string()) +// .collect() +// }); - Ok(tide::Response::new(200) - .body_json(&displayed_attributes) - .unwrap()) -} +// Ok(tide::Response::new(200) +// .body_json(&displayed_attributes) +// .unwrap()) +// } -pub async fn update_displayed(mut ctx: Request) -> SResult { - ctx.is_allowed(Private)?; - let index = ctx.index()?; - let displayed_attributes: Option> = - ctx.body_json().await.map_err(ResponseError::bad_request)?; - let db = &ctx.state().db; +// pub async fn update_displayed(mut ctx: Request) -> SResult { +// ctx.is_allowed(Private)?; +// let index = ctx.index()?; +// let displayed_attributes: Option> = +// ctx.body_json().await.map_err(ResponseError::bad_request)?; +// let db = &ctx.state().db; - let settings = Settings { - displayed_attributes: Some(displayed_attributes), - ..Settings::default() - }; +// let settings = Settings { +// displayed_attributes: Some(displayed_attributes), +// ..Settings::default() +// }; - let mut writer = db.update_write_txn()?; - let settings = settings.into_update().map_err(ResponseError::bad_request)?; - let update_id = index.settings_update(&mut writer, settings)?; - writer.commit()?; +// let mut writer = db.update_write_txn()?; +// let settings = settings.into_update().map_err(ResponseError::bad_request)?; +// let update_id = index.settings_update(&mut writer, settings)?; +// writer.commit()?; - let response_body = IndexUpdateResponse { update_id }; - Ok(tide::Response::new(202).body_json(&response_body)?) -} +// let response_body = IndexUpdateResponse { update_id }; +// Ok(tide::Response::new(202).body_json(&response_body)?) +// } -pub async fn delete_displayed(ctx: Request) -> SResult { - ctx.is_allowed(Private)?; - let index = ctx.index()?; - let db = &ctx.state().db; +// pub async fn delete_displayed(ctx: Request) -> SResult { +// ctx.is_allowed(Private)?; +// let index = ctx.index()?; +// let db = &ctx.state().db; - let settings = SettingsUpdate { - displayed_attributes: UpdateState::Clear, - ..SettingsUpdate::default() - }; +// let settings = SettingsUpdate { +// displayed_attributes: UpdateState::Clear, +// ..SettingsUpdate::default() +// }; - let mut writer = db.update_write_txn()?; - let update_id = index.settings_update(&mut writer, settings)?; - writer.commit()?; +// let mut writer = db.update_write_txn()?; +// let update_id = index.settings_update(&mut writer, settings)?; +// writer.commit()?; - let response_body = IndexUpdateResponse { update_id }; - Ok(tide::Response::new(202).body_json(&response_body)?) -} +// let response_body = IndexUpdateResponse { update_id }; +// Ok(tide::Response::new(202).body_json(&response_body)?) +// } -pub async fn get_accept_new_fields(ctx: Request) -> SResult { - ctx.is_allowed(Private)?; - let index = ctx.index()?; - let db = &ctx.state().db; - let reader = db.main_read_txn()?; +// pub async fn get_accept_new_fields(ctx: Request) -> SResult { +// ctx.is_allowed(Private)?; +// let index = ctx.index()?; +// let db = &ctx.state().db; +// let reader = db.main_read_txn()?; - let schema = index.main.schema(&reader)?; +// let schema = index.main.schema(&reader)?; - let accept_new_fields = schema.map(|s| s.accept_new_fields()); +// let accept_new_fields = schema.map(|s| s.accept_new_fields()); - Ok(tide::Response::new(200) - .body_json(&accept_new_fields) - .unwrap()) -} +// Ok(tide::Response::new(200) +// .body_json(&accept_new_fields) +// .unwrap()) +// } -pub async fn update_accept_new_fields(mut ctx: Request) -> SResult { - ctx.is_allowed(Private)?; - let index = ctx.index()?; - let accept_new_fields: Option = - ctx.body_json().await.map_err(ResponseError::bad_request)?; - let db = &ctx.state().db; +// pub async fn update_accept_new_fields(mut ctx: Request) -> SResult { +// ctx.is_allowed(Private)?; +// let index = ctx.index()?; +// let accept_new_fields: Option = +// ctx.body_json().await.map_err(ResponseError::bad_request)?; +// let db = &ctx.state().db; - let settings = Settings { - accept_new_fields: Some(accept_new_fields), - ..Settings::default() - }; +// let settings = Settings { +// accept_new_fields: Some(accept_new_fields), +// ..Settings::default() +// }; - let mut writer = db.update_write_txn()?; - let settings = settings.into_update().map_err(ResponseError::bad_request)?; - let update_id = index.settings_update(&mut writer, settings)?; - writer.commit()?; +// let mut writer = db.update_write_txn()?; +// let settings = settings.into_update().map_err(ResponseError::bad_request)?; +// let update_id = index.settings_update(&mut writer, settings)?; +// writer.commit()?; - let response_body = IndexUpdateResponse { update_id }; - Ok(tide::Response::new(202).body_json(&response_body)?) -} +// let response_body = IndexUpdateResponse { update_id }; +// Ok(tide::Response::new(202).body_json(&response_body)?) +// } diff --git a/meilisearch-http/src/routes/stats.rs b/meilisearch-http/src/routes/stats.rs index 47cda692a..b2261a7dc 100644 --- a/meilisearch-http/src/routes/stats.rs +++ b/meilisearch-http/src/routes/stats.rs @@ -11,6 +11,7 @@ use walkdir::WalkDir; use crate::Data; use crate::error::ResponseError; +use crate::routes::IndexParam; #[derive(Serialize)] #[serde(rename_all = "camelCase")] @@ -23,10 +24,10 @@ pub struct IndexStatsResponse { #[get("/indexes/{index_uid}/stats")] pub async fn index_stats( data: web::Data, - path: web::Path, + path: web::Path, ) -> aweb::Result> { - let index = data.db.open_index(path.clone()) - .ok_or(ResponseError::IndexNotFound(path.clone()))?; + let index = data.db.open_index(path.index_uid.clone()) + .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; let reader = data.db.main_read_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; @@ -42,7 +43,7 @@ pub async fn index_stats( .map_err(|err| ResponseError::Internal(err.to_string()))?; let is_indexing = data - .is_indexing(&update_reader, &path) + .is_indexing(&update_reader, &path.index_uid) .map_err(|err| ResponseError::Internal(err.to_string()))? .unwrap_or_default(); From 85833e3a0aefc1d33af5244c83ae052e419cb11a Mon Sep 17 00:00:00 2001 From: Quentin de Quelen Date: Fri, 10 Apr 2020 10:13:08 +0200 Subject: [PATCH 08/21] add setting endpoint --- meilisearch-http/src/main.rs | 17 + meilisearch-http/src/routes/setting.rs | 556 ++++++++++++++----------- 2 files changed, 329 insertions(+), 244 deletions(-) diff --git a/meilisearch-http/src/main.rs b/meilisearch-http/src/main.rs index 0073e029c..430a56ec8 100644 --- a/meilisearch-http/src/main.rs +++ b/meilisearch-http/src/main.rs @@ -72,6 +72,23 @@ async fn main() -> Result<(), MainError> { .service(routes::document::update_documents) .service(routes::document::delete_documents) .service(routes::document::clear_all_documents) + .service(routes::setting::update_all) + .service(routes::setting::get_all) + .service(routes::setting::delete_all) + .service(routes::setting::get_rules) + .service(routes::setting::update_rules) + .service(routes::setting::delete_rules) + .service(routes::setting::get_distinct) + .service(routes::setting::update_distinct) + .service(routes::setting::delete_distinct) + .service(routes::setting::get_searchable) + .service(routes::setting::update_searchable) + .service(routes::setting::delete_searchable) + .service(routes::setting::get_displayed) + .service(routes::setting::update_displayed) + .service(routes::setting::delete_displayed) + .service(routes::setting::get_accept_new_fields) + .service(routes::setting::update_accept_new_fields) .service(routes::key::list) .service(routes::stats::index_stats) .service(routes::stats::get_stats) diff --git a/meilisearch-http/src/routes/setting.rs b/meilisearch-http/src/routes/setting.rs index 5de23a435..603b13902 100644 --- a/meilisearch-http/src/routes/setting.rs +++ b/meilisearch-http/src/routes/setting.rs @@ -1,14 +1,14 @@ use meilisearch_core::settings::{Settings, SettingsUpdate, UpdateState, DEFAULT_RANKING_RULES}; use std::collections::{BTreeMap, BTreeSet, HashSet}; -use actix_web::{web, get, post, put, delete, HttpResponse}; +use actix_web::{web, get, post, delete, HttpResponse}; use actix_web as aweb; use crate::error::{ResponseError}; use crate::Data; use crate::routes::{IndexUpdateResponse, IndexParam}; -#[get("/indexes/{index_uid}/settings")] -pub async fn get_all( +#[post("/indexes/{index_uid}/settings")] +pub async fn update_all( data: web::Data, path: web::Path, ) -> aweb::Result { @@ -86,303 +86,371 @@ pub async fn get_all( Ok(HttpResponse::Ok().json(settings)) } -// pub async fn update_all(mut ctx: Request) -> SResult { -// ctx.is_allowed(Private)?; -// let index = ctx.index()?; -// let settings: Settings = -// ctx.body_json().await.map_err(ResponseError::bad_request)?; -// let db = &ctx.state().db; +#[get("/indexes/{index_uid}/settings")] +pub async fn get_all( + data: web::Data, + path: web::Path, + body: web::Json, +) -> aweb::Result { + let index = data.db.open_index(&path.index_uid) + .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; -// let mut writer = db.update_write_txn()?; -// let settings = settings.into_update().map_err(ResponseError::bad_request)?; -// let update_id = index.settings_update(&mut writer, settings)?; -// writer.commit()?; + let mut writer = data.db.update_write_txn() + .map_err(|err| ResponseError::Internal(err.to_string()))?; + let settings = body.into_inner().into_update() + .map_err(|e| ResponseError::BadRequest(e.to_string()))?; + let update_id = index.settings_update(&mut writer, settings) + .map_err(|err| ResponseError::Internal(err.to_string()))?; + writer.commit() + .map_err(|err| ResponseError::Internal(err.to_string()))?; -// let response_body = IndexUpdateResponse { update_id }; -// Ok(tide::Response::new(202).body_json(&response_body)?) -// } + Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) +} -// pub async fn delete_all(ctx: Request) -> SResult { -// ctx.is_allowed(Private)?; -// let index = ctx.index()?; -// let db = &ctx.state().db; -// let mut writer = db.update_write_txn()?; +#[delete("/indexes/{index_uid}/settings")] +pub async fn delete_all( + data: web::Data, + path: web::Path, +) -> aweb::Result { + let index = data.db.open_index(&path.index_uid) + .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; + let mut writer = data.db.update_write_txn() + .map_err(|err| ResponseError::Internal(err.to_string()))?; -// let settings = SettingsUpdate { -// ranking_rules: UpdateState::Clear, -// distinct_attribute: UpdateState::Clear, -// primary_key: UpdateState::Clear, -// searchable_attributes: UpdateState::Clear, -// displayed_attributes: UpdateState::Clear, -// stop_words: UpdateState::Clear, -// synonyms: UpdateState::Clear, -// accept_new_fields: UpdateState::Clear, -// }; + let settings = SettingsUpdate { + ranking_rules: UpdateState::Clear, + distinct_attribute: UpdateState::Clear, + primary_key: UpdateState::Clear, + searchable_attributes: UpdateState::Clear, + displayed_attributes: UpdateState::Clear, + stop_words: UpdateState::Clear, + synonyms: UpdateState::Clear, + accept_new_fields: UpdateState::Clear, + }; -// let update_id = index.settings_update(&mut writer, settings)?; + let update_id = index.settings_update(&mut writer, settings) + .map_err(|err| ResponseError::Internal(err.to_string()))?; + writer.commit() + .map_err(|err| ResponseError::Internal(err.to_string()))?; -// writer.commit()?; + Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) +} -// let response_body = IndexUpdateResponse { update_id }; -// Ok(tide::Response::new(202).body_json(&response_body)?) -// } +#[get("/indexes/{index_uid}/settings/ranking-rules")] +pub async fn get_rules( + data: web::Data, + path: web::Path, +) -> aweb::Result { + let index = data.db.open_index(&path.index_uid) + .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; + let reader = data.db.main_read_txn() + .map_err(|err| ResponseError::Internal(err.to_string()))?; -// pub async fn get_rules(ctx: Request) -> SResult { -// ctx.is_allowed(Private)?; -// let index = ctx.index()?; -// let db = &ctx.state().db; -// let reader = db.main_read_txn()?; + let ranking_rules = index + .main + .ranking_rules(&reader) + .map_err(|err| ResponseError::Internal(err.to_string()))? + .unwrap_or(DEFAULT_RANKING_RULES.to_vec()) + .into_iter() + .map(|r| r.to_string()) + .collect::>(); -// let ranking_rules = index -// .main -// .ranking_rules(&reader)? -// .unwrap_or(DEFAULT_RANKING_RULES.to_vec()) -// .into_iter() -// .map(|r| r.to_string()) -// .collect::>(); + Ok(HttpResponse::Ok().json(ranking_rules)) +} -// Ok(tide::Response::new(200).body_json(&ranking_rules).unwrap()) -// } +#[post("/indexes/{index_uid}/settings/ranking-rules")] +pub async fn update_rules( + data: web::Data, + path: web::Path, + body: web::Json>>, +) -> aweb::Result { + let index = data.db.open_index(&path.index_uid) + .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; -// pub async fn update_rules(mut ctx: Request) -> SResult { -// ctx.is_allowed(Private)?; -// let index = ctx.index()?; -// let ranking_rules: Option> = -// ctx.body_json().await.map_err(ResponseError::bad_request)?; -// let db = &ctx.state().db; + let settings = Settings { + ranking_rules: Some(body.into_inner()), + ..Settings::default() + }; -// let settings = Settings { -// ranking_rules: Some(ranking_rules), -// ..Settings::default() -// }; + let mut writer = data.db.update_write_txn() + .map_err(|err| ResponseError::Internal(err.to_string()))?; + let settings = settings.into_update() + .map_err(|e| ResponseError::BadRequest(e.to_string()))?; + let update_id = index.settings_update(&mut writer, settings) + .map_err(|err| ResponseError::Internal(err.to_string()))?; + writer.commit() + .map_err(|err| ResponseError::Internal(err.to_string()))?; -// let mut writer = db.update_write_txn()?; -// let settings = settings.into_update().map_err(ResponseError::bad_request)?; -// let update_id = index.settings_update(&mut writer, settings)?; -// writer.commit()?; + Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) +} -// let response_body = IndexUpdateResponse { update_id }; -// Ok(tide::Response::new(202).body_json(&response_body)?) -// } +#[delete("/indexes/{index_uid}/settings/ranking-rules")] +pub async fn delete_rules( + data: web::Data, + path: web::Path, +) -> aweb::Result { + let index = data.db.open_index(&path.index_uid) + .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; + let mut writer = data.db.update_write_txn() + .map_err(|err| ResponseError::Internal(err.to_string()))?; -// pub async fn delete_rules(ctx: Request) -> SResult { -// ctx.is_allowed(Private)?; -// let index = ctx.index()?; -// let db = &ctx.state().db; -// let mut writer = db.update_write_txn()?; + let settings = SettingsUpdate { + ranking_rules: UpdateState::Clear, + ..SettingsUpdate::default() + }; -// let settings = SettingsUpdate { -// ranking_rules: UpdateState::Clear, -// ..SettingsUpdate::default() -// }; + let update_id = index.settings_update(&mut writer, settings) + .map_err(|err| ResponseError::Internal(err.to_string()))?; -// let update_id = index.settings_update(&mut writer, settings)?; + writer.commit() + .map_err(|err| ResponseError::Internal(err.to_string()))?; -// writer.commit()?; + Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) +} -// let response_body = IndexUpdateResponse { update_id }; -// Ok(tide::Response::new(202).body_json(&response_body)?) -// } +#[get("/indexes/{index_uid}/settings/distinct-attribute")] +pub async fn get_distinct( + data: web::Data, + path: web::Path, +) -> aweb::Result { + let index = data.db.open_index(&path.index_uid) + .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; + let reader = data.db.main_read_txn() + .map_err(|err| ResponseError::Internal(err.to_string()))?; + let distinct_attribute = index.main.distinct_attribute(&reader) + .map_err(|err| ResponseError::Internal(err.to_string()))?; -// pub async fn get_distinct(ctx: Request) -> SResult { -// ctx.is_allowed(Private)?; -// let index = ctx.index()?; -// let db = &ctx.state().db; -// let reader = db.main_read_txn()?; + Ok(HttpResponse::Ok().json(distinct_attribute)) +} -// let distinct_attribute = index.main.distinct_attribute(&reader)?; +#[post("/indexes/{index_uid}/settings/distinct-attribute")] +pub async fn update_distinct( + data: web::Data, + path: web::Path, + body: web::Json>, +) -> aweb::Result { + let index = data.db.open_index(&path.index_uid) + .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; -// Ok(tide::Response::new(200) -// .body_json(&distinct_attribute) -// .unwrap()) -// } + let settings = Settings { + distinct_attribute: Some(body.into_inner()), + ..Settings::default() + }; -// pub async fn update_distinct(mut ctx: Request) -> SResult { -// ctx.is_allowed(Private)?; -// let index = ctx.index()?; -// let distinct_attribute: Option = -// ctx.body_json().await.map_err(ResponseError::bad_request)?; -// let db = &ctx.state().db; + let mut writer = data.db.update_write_txn() + .map_err(|err| ResponseError::Internal(err.to_string()))?; + let settings = settings.into_update() + .map_err(|e| ResponseError::BadRequest(e.to_string()))?; + let update_id = index.settings_update(&mut writer, settings) + .map_err(|err| ResponseError::Internal(err.to_string()))?; + writer.commit() + .map_err(|err| ResponseError::Internal(err.to_string()))?; -// let settings = Settings { -// distinct_attribute: Some(distinct_attribute), -// ..Settings::default() -// }; + Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) +} -// let mut writer = db.update_write_txn()?; -// let settings = settings.into_update().map_err(ResponseError::bad_request)?; -// let update_id = index.settings_update(&mut writer, settings)?; -// writer.commit()?; +#[delete("/indexes/{index_uid}/settings/distinct-attribute")] +pub async fn delete_distinct( + data: web::Data, + path: web::Path, +) -> aweb::Result { + let index = data.db.open_index(&path.index_uid) + .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; + let mut writer = data.db.update_write_txn() + .map_err(|err| ResponseError::Internal(err.to_string()))?; -// let response_body = IndexUpdateResponse { update_id }; -// Ok(tide::Response::new(202).body_json(&response_body)?) -// } + let settings = SettingsUpdate { + distinct_attribute: UpdateState::Clear, + ..SettingsUpdate::default() + }; -// pub async fn delete_distinct(ctx: Request) -> SResult { -// ctx.is_allowed(Private)?; -// let index = ctx.index()?; -// let db = &ctx.state().db; -// let mut writer = db.update_write_txn()?; + let update_id = index.settings_update(&mut writer, settings) + .map_err(|err| ResponseError::Internal(err.to_string()))?; -// let settings = SettingsUpdate { -// distinct_attribute: UpdateState::Clear, -// ..SettingsUpdate::default() -// }; + writer.commit() + .map_err(|err| ResponseError::Internal(err.to_string()))?; -// let update_id = index.settings_update(&mut writer, settings)?; + Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) +} -// writer.commit()?; +#[get("/indexes/{index_uid}/settings/searchable-attributes")] +pub async fn get_searchable( + data: web::Data, + path: web::Path, +) -> aweb::Result { + let index = data.db.open_index(&path.index_uid) + .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; + let reader = data.db.main_read_txn() + .map_err(|err| ResponseError::Internal(err.to_string()))?; + let schema = index.main.schema(&reader) + .map_err(|err| ResponseError::Internal(err.to_string()))?; + let searchable_attributes: Option> = + schema.map(|s| s.indexed_name().iter().map(|i| (*i).to_string()).collect()); -// let response_body = IndexUpdateResponse { update_id }; -// Ok(tide::Response::new(202).body_json(&response_body)?) -// } + Ok(HttpResponse::Ok().json(searchable_attributes)) +} -// pub async fn get_searchable(ctx: Request) -> SResult { -// ctx.is_allowed(Private)?; -// let index = ctx.index()?; -// let db = &ctx.state().db; -// let reader = db.main_read_txn()?; +#[post("/indexes/{index_uid}/settings/searchable-attributes")] +pub async fn update_searchable( + data: web::Data, + path: web::Path, + body: web::Json>>, +) -> aweb::Result { + let index = data.db.open_index(&path.index_uid) + .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; -// let schema = index.main.schema(&reader)?; + let settings = Settings { + searchable_attributes: Some(body.into_inner()), + ..Settings::default() + }; -// let searchable_attributes: Option> = -// schema.map(|s| s.indexed_name().iter().map(|i| (*i).to_string()).collect()); + let mut writer = data.db.update_write_txn() + .map_err(|err| ResponseError::Internal(err.to_string()))?; + let settings = settings.into_update() + .map_err(|e| ResponseError::BadRequest(e.to_string()))?; + let update_id = index.settings_update(&mut writer, settings) + .map_err(|err| ResponseError::Internal(err.to_string()))?; + writer.commit() + .map_err(|err| ResponseError::Internal(err.to_string()))?; -// Ok(tide::Response::new(200) -// .body_json(&searchable_attributes) -// .unwrap()) -// } + Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) +} -// pub async fn update_searchable(mut ctx: Request) -> SResult { -// ctx.is_allowed(Private)?; -// let index = ctx.index()?; -// let searchable_attributes: Option> = -// ctx.body_json().await.map_err(ResponseError::bad_request)?; -// let db = &ctx.state().db; +#[delete("/indexes/{index_uid}/settings/searchable-attributes")] +pub async fn delete_searchable( + data: web::Data, + path: web::Path, +) -> aweb::Result { + let index = data.db.open_index(&path.index_uid) + .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; -// let settings = Settings { -// searchable_attributes: Some(searchable_attributes), -// ..Settings::default() -// }; + let settings = SettingsUpdate { + searchable_attributes: UpdateState::Clear, + ..SettingsUpdate::default() + }; -// let mut writer = db.update_write_txn()?; -// let settings = settings.into_update().map_err(ResponseError::bad_request)?; -// let update_id = index.settings_update(&mut writer, settings)?; -// writer.commit()?; + let mut writer = data.db.update_write_txn() + .map_err(|err| ResponseError::Internal(err.to_string()))?; + let update_id = index.settings_update(&mut writer, settings) + .map_err(|err| ResponseError::Internal(err.to_string()))?; + writer.commit() + .map_err(|err| ResponseError::Internal(err.to_string()))?; -// let response_body = IndexUpdateResponse { update_id }; -// Ok(tide::Response::new(202).body_json(&response_body)?) -// } + Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) +} -// pub async fn delete_searchable(ctx: Request) -> SResult { -// ctx.is_allowed(Private)?; -// let index = ctx.index()?; -// let db = &ctx.state().db; +#[get("/indexes/{index_uid}/settings/displayed-attributes")] +pub async fn get_displayed( + data: web::Data, + path: web::Path, +) -> aweb::Result { + let index = data.db.open_index(&path.index_uid) + .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; + let reader = data.db.main_read_txn() + .map_err(|err| ResponseError::Internal(err.to_string()))?; -// let settings = SettingsUpdate { -// searchable_attributes: UpdateState::Clear, -// ..SettingsUpdate::default() -// }; + let schema = index.main.schema(&reader) + .map_err(|err| ResponseError::Internal(err.to_string()))?; -// let mut writer = db.update_write_txn()?; -// let update_id = index.settings_update(&mut writer, settings)?; -// writer.commit()?; + let displayed_attributes: Option> = schema.map(|s| { + s.displayed_name() + .iter() + .map(|i| (*i).to_string()) + .collect() + }); -// let response_body = IndexUpdateResponse { update_id }; -// Ok(tide::Response::new(202).body_json(&response_body)?) -// } + Ok(HttpResponse::Ok().json(displayed_attributes)) +} -// pub async fn displayed(ctx: Request) -> SResult { -// ctx.is_allowed(Private)?; -// let index = ctx.index()?; -// let db = &ctx.state().db; -// let reader = db.main_read_txn()?; +#[post("/indexes/{index_uid}/settings/displayed-attributes")] +pub async fn update_displayed( + data: web::Data, + path: web::Path, + body: web::Json>>, +) -> aweb::Result { + let index = data.db.open_index(&path.index_uid) + .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; -// let schema = index.main.schema(&reader)?; + let settings = Settings { + displayed_attributes: Some(body.into_inner()), + ..Settings::default() + }; -// let displayed_attributes: Option> = schema.map(|s| { -// s.displayed_name() -// .iter() -// .map(|i| (*i).to_string()) -// .collect() -// }); + let mut writer = data.db.update_write_txn() + .map_err(|err| ResponseError::Internal(err.to_string()))?; + let settings = settings.into_update() + .map_err(|e| ResponseError::BadRequest(e.to_string()))?; + let update_id = index.settings_update(&mut writer, settings) + .map_err(|err| ResponseError::Internal(err.to_string()))?; + writer.commit() + .map_err(|err| ResponseError::Internal(err.to_string()))?; -// Ok(tide::Response::new(200) -// .body_json(&displayed_attributes) -// .unwrap()) -// } + Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) +} -// pub async fn update_displayed(mut ctx: Request) -> SResult { -// ctx.is_allowed(Private)?; -// let index = ctx.index()?; -// let displayed_attributes: Option> = -// ctx.body_json().await.map_err(ResponseError::bad_request)?; -// let db = &ctx.state().db; +#[delete("/indexes/{index_uid}/settings/displayed-attributes")] +pub async fn delete_displayed( + data: web::Data, + path: web::Path, +) -> aweb::Result { + let index = data.db.open_index(&path.index_uid) + .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; -// let settings = Settings { -// displayed_attributes: Some(displayed_attributes), -// ..Settings::default() -// }; + let settings = SettingsUpdate { + displayed_attributes: UpdateState::Clear, + ..SettingsUpdate::default() + }; -// let mut writer = db.update_write_txn()?; -// let settings = settings.into_update().map_err(ResponseError::bad_request)?; -// let update_id = index.settings_update(&mut writer, settings)?; -// writer.commit()?; + let mut writer = data.db.update_write_txn() + .map_err(|err| ResponseError::Internal(err.to_string()))?; + let update_id = index.settings_update(&mut writer, settings) + .map_err(|err| ResponseError::Internal(err.to_string()))?; + writer.commit() + .map_err(|err| ResponseError::Internal(err.to_string()))?; -// let response_body = IndexUpdateResponse { update_id }; -// Ok(tide::Response::new(202).body_json(&response_body)?) -// } + Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) +} -// pub async fn delete_displayed(ctx: Request) -> SResult { -// ctx.is_allowed(Private)?; -// let index = ctx.index()?; -// let db = &ctx.state().db; +#[get("/indexes/{index_uid}/settings/accept-new-fields")] +pub async fn get_accept_new_fields( + data: web::Data, + path: web::Path, +) -> aweb::Result { + let index = data.db.open_index(&path.index_uid) + .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; + let reader = data.db.main_read_txn() + .map_err(|err| ResponseError::Internal(err.to_string()))?; -// let settings = SettingsUpdate { -// displayed_attributes: UpdateState::Clear, -// ..SettingsUpdate::default() -// }; + let schema = index.main.schema(&reader) + .map_err(|err| ResponseError::Internal(err.to_string()))?; -// let mut writer = db.update_write_txn()?; -// let update_id = index.settings_update(&mut writer, settings)?; -// writer.commit()?; + let accept_new_fields = schema.map(|s| s.accept_new_fields()); -// let response_body = IndexUpdateResponse { update_id }; -// Ok(tide::Response::new(202).body_json(&response_body)?) -// } + Ok(HttpResponse::Ok().json(accept_new_fields)) +} -// pub async fn get_accept_new_fields(ctx: Request) -> SResult { -// ctx.is_allowed(Private)?; -// let index = ctx.index()?; -// let db = &ctx.state().db; -// let reader = db.main_read_txn()?; +#[post("/indexes/{index_uid}/settings/accept-new-fields")] +pub async fn update_accept_new_fields( + data: web::Data, + path: web::Path, + body: web::Json>, +) -> aweb::Result { + let index = data.db.open_index(&path.index_uid) + .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; -// let schema = index.main.schema(&reader)?; + let settings = Settings { + accept_new_fields: Some(body.into_inner()), + ..Settings::default() + }; -// let accept_new_fields = schema.map(|s| s.accept_new_fields()); + let mut writer = data.db.update_write_txn() + .map_err(|err| ResponseError::Internal(err.to_string()))?; + let settings = settings.into_update() + .map_err(|e| ResponseError::BadRequest(e.to_string()))?; + let update_id = index.settings_update(&mut writer, settings) + .map_err(|err| ResponseError::Internal(err.to_string()))?; + writer.commit() + .map_err(|err| ResponseError::Internal(err.to_string()))?; -// Ok(tide::Response::new(200) -// .body_json(&accept_new_fields) -// .unwrap()) -// } - -// pub async fn update_accept_new_fields(mut ctx: Request) -> SResult { -// ctx.is_allowed(Private)?; -// let index = ctx.index()?; -// let accept_new_fields: Option = -// ctx.body_json().await.map_err(ResponseError::bad_request)?; -// let db = &ctx.state().db; - -// let settings = Settings { -// accept_new_fields: Some(accept_new_fields), -// ..Settings::default() -// }; - -// let mut writer = db.update_write_txn()?; -// let settings = settings.into_update().map_err(ResponseError::bad_request)?; -// let update_id = index.settings_update(&mut writer, settings)?; -// writer.commit()?; - -// let response_body = IndexUpdateResponse { update_id }; -// Ok(tide::Response::new(202).body_json(&response_body)?) -// } + Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) +} From 22fbff98d41c5e31fd692dd029e87a9a8fa6d710 Mon Sep 17 00:00:00 2001 From: Quentin de Quelen Date: Fri, 10 Apr 2020 18:39:52 +0200 Subject: [PATCH 09/21] add stop-word and synonym endpoints --- meilisearch-http/src/main.rs | 6 ++ meilisearch-http/src/routes/mod.rs | 4 +- meilisearch-http/src/routes/stop_words.rs | 86 +++++++++++--------- meilisearch-http/src/routes/synonym.rs | 95 +++++++++++++---------- 4 files changed, 109 insertions(+), 82 deletions(-) diff --git a/meilisearch-http/src/main.rs b/meilisearch-http/src/main.rs index 430a56ec8..edddb37d6 100644 --- a/meilisearch-http/src/main.rs +++ b/meilisearch-http/src/main.rs @@ -89,6 +89,12 @@ async fn main() -> Result<(), MainError> { .service(routes::setting::delete_displayed) .service(routes::setting::get_accept_new_fields) .service(routes::setting::update_accept_new_fields) + .service(routes::stop_words::get) + .service(routes::stop_words::update) + .service(routes::stop_words::delete) + .service(routes::synonym::get) + .service(routes::synonym::update) + .service(routes::synonym::delete) .service(routes::key::list) .service(routes::stats::index_stats) .service(routes::stats::get_stats) diff --git a/meilisearch-http/src/routes/mod.rs b/meilisearch-http/src/routes/mod.rs index 78fd1a25c..c8401e905 100644 --- a/meilisearch-http/src/routes/mod.rs +++ b/meilisearch-http/src/routes/mod.rs @@ -12,8 +12,8 @@ pub mod key; pub mod search; pub mod stats; pub mod setting; -// pub mod stop_words; -// pub mod synonym; +pub mod stop_words; +pub mod synonym; #[derive(Default, Deserialize)] pub struct IndexParam { diff --git a/meilisearch-http/src/routes/stop_words.rs b/meilisearch-http/src/routes/stop_words.rs index f9d0154a7..2234497bf 100644 --- a/meilisearch-http/src/routes/stop_words.rs +++ b/meilisearch-http/src/routes/stop_words.rs @@ -1,63 +1,73 @@ use std::collections::BTreeSet; use meilisearch_core::settings::{SettingsUpdate, UpdateState}; -use tide::{Request, Response}; +use actix_web::{web, get, post, delete, HttpResponse}; +use actix_web as aweb; -use crate::error::{ResponseError, SResult}; -use crate::helpers::tide::RequestExt; -use crate::helpers::tide::ACL::*; -use crate::routes::document::IndexUpdateResponse; +use crate::error::{ResponseError}; use crate::Data; +use crate::routes::{IndexUpdateResponse, IndexParam}; -pub async fn get(ctx: Request) -> SResult { - ctx.is_allowed(Private)?; - let index = ctx.index()?; - let db = &ctx.state().db; - let reader = db.main_read_txn()?; - let stop_words_fst = index.main.stop_words_fst(&reader)?; - let stop_words = stop_words_fst.unwrap_or_default().stream().into_strs()?; +#[get("/indexes/{index_uid}/settings/stop-words")] +pub async fn get( + data: web::Data, + path: web::Path, +) -> aweb::Result { + let index = data.db.open_index(&path.index_uid) + .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; + let reader = data.db.main_read_txn() + .map_err(|err| ResponseError::Internal(err.to_string()))?; + let stop_words_fst = index.main.stop_words_fst(&reader) + .map_err(|err| ResponseError::Internal(err.to_string()))?; + let stop_words = stop_words_fst.unwrap_or_default().stream().into_strs() + .map_err(|err| ResponseError::Internal(err.to_string()))?; - Ok(tide::Response::new(200).body_json(&stop_words).unwrap()) + Ok(HttpResponse::Ok().json(stop_words)) } -pub async fn update(mut ctx: Request) -> SResult { - ctx.is_allowed(Private)?; - let index = ctx.index()?; - - let data: BTreeSet = ctx.body_json().await.map_err(ResponseError::bad_request)?; - - let db = &ctx.state().db; - let mut writer = db.update_write_txn()?; +#[post("/indexes/{index_uid}/settings/stop-words")] +pub async fn update( + data: web::Data, + path: web::Path, + body: web::Json>, +) -> aweb::Result { + let index = data.db.open_index(&path.index_uid) + .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; let settings = SettingsUpdate { - stop_words: UpdateState::Update(data), + stop_words: UpdateState::Update(body.into_inner()), ..SettingsUpdate::default() }; - let update_id = index.settings_update(&mut writer, settings)?; + let mut writer = data.db.update_write_txn() + .map_err(|err| ResponseError::Internal(err.to_string()))?; + let update_id = index.settings_update(&mut writer, settings) + .map_err(|err| ResponseError::Internal(err.to_string()))?; + writer.commit() + .map_err(|err| ResponseError::Internal(err.to_string()))?; - writer.commit()?; - - let response_body = IndexUpdateResponse { update_id }; - Ok(tide::Response::new(202).body_json(&response_body)?) + Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } -pub async fn delete(ctx: Request) -> SResult { - ctx.is_allowed(Private)?; - let index = ctx.index()?; - - let db = &ctx.state().db; - let mut writer = db.update_write_txn()?; +#[delete("/indexes/{index_uid}/settings/stop-words")] +pub async fn delete( + data: web::Data, + path: web::Path, +) -> aweb::Result { + let index = data.db.open_index(&path.index_uid) + .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; let settings = SettingsUpdate { stop_words: UpdateState::Clear, ..SettingsUpdate::default() }; - let update_id = index.settings_update(&mut writer, settings)?; + let mut writer = data.db.update_write_txn() + .map_err(|err| ResponseError::Internal(err.to_string()))?; + let update_id = index.settings_update(&mut writer, settings) + .map_err(|err| ResponseError::Internal(err.to_string()))?; + writer.commit() + .map_err(|err| ResponseError::Internal(err.to_string()))?; - writer.commit()?; - - let response_body = IndexUpdateResponse { update_id }; - Ok(tide::Response::new(202).body_json(&response_body)?) + Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } diff --git a/meilisearch-http/src/routes/synonym.rs b/meilisearch-http/src/routes/synonym.rs index 60fca7e98..587582ee9 100644 --- a/meilisearch-http/src/routes/synonym.rs +++ b/meilisearch-http/src/routes/synonym.rs @@ -2,81 +2,92 @@ use std::collections::BTreeMap; use indexmap::IndexMap; use meilisearch_core::settings::{SettingsUpdate, UpdateState}; -use tide::{Request, Response}; +use actix_web::{web, get, post, delete, HttpResponse}; +use actix_web as aweb; -use crate::error::{ResponseError, SResult}; -use crate::helpers::tide::RequestExt; -use crate::helpers::tide::ACL::*; -use crate::routes::document::IndexUpdateResponse; +use crate::error::{ResponseError}; use crate::Data; +use crate::routes::{IndexUpdateResponse, IndexParam}; -pub async fn get(ctx: Request) -> SResult { - ctx.is_allowed(Private)?; - let index = ctx.index()?; +#[get("/indexes/{index_uid}/settings/synonyms")] +pub async fn get( + data: web::Data, + path: web::Path, +) -> aweb::Result { + let index = data.db.open_index(&path.index_uid) + .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; - let db = &ctx.state().db; - let reader = db.main_read_txn()?; + let reader = data.db.main_read_txn() + .map_err(|err| ResponseError::Internal(err.to_string()))?; - let synonyms_fst = index.main.synonyms_fst(&reader)?.unwrap_or_default(); - let synonyms_list = synonyms_fst.stream().into_strs()?; + let synonyms_fst = index.main.synonyms_fst(&reader) + .map_err(|err| ResponseError::Internal(err.to_string()))? + .unwrap_or_default(); + let synonyms_list = synonyms_fst.stream().into_strs() + .map_err(|err| ResponseError::Internal(err.to_string()))?; let mut synonyms = IndexMap::new(); let index_synonyms = &index.synonyms; for synonym in synonyms_list { - let alternative_list = index_synonyms.synonyms(&reader, synonym.as_bytes())?; + let alternative_list = index_synonyms.synonyms(&reader, synonym.as_bytes()) + .map_err(|err| ResponseError::Internal(err.to_string()))?; if let Some(list) = alternative_list { - let list = list.stream().into_strs()?; + let list = list.stream().into_strs() + .map_err(|err| ResponseError::Internal(err.to_string()))?; synonyms.insert(synonym, list); } } - Ok(tide::Response::new(200).body_json(&synonyms).unwrap()) + Ok(HttpResponse::Ok().json(synonyms)) } -pub async fn update(mut ctx: Request) -> SResult { - ctx.is_allowed(Private)?; - - let data: BTreeMap> = - ctx.body_json().await.map_err(ResponseError::bad_request)?; - - let index = ctx.index()?; - - let db = &ctx.state().db; - let mut writer = db.update_write_txn()?; +#[post("/indexes/{index_uid}/settings/synonyms")] +pub async fn update( + data: web::Data, + path: web::Path, + body: web::Json>>, +) -> aweb::Result { + let index = data.db.open_index(&path.index_uid) + .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; let settings = SettingsUpdate { - synonyms: UpdateState::Update(data), + synonyms: UpdateState::Update(body.into_inner()), ..SettingsUpdate::default() }; - let update_id = index.settings_update(&mut writer, settings)?; + let mut writer = data.db.update_write_txn() + .map_err(|err| ResponseError::Internal(err.to_string()))?; + let update_id = index.settings_update(&mut writer, settings) + .map_err(|err| ResponseError::Internal(err.to_string()))?; + writer.commit() + .map_err(|err| ResponseError::Internal(err.to_string()))?; - writer.commit()?; - - let response_body = IndexUpdateResponse { update_id }; - Ok(tide::Response::new(202).body_json(&response_body)?) + Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } -pub async fn delete(ctx: Request) -> SResult { - ctx.is_allowed(Private)?; - - let index = ctx.index()?; - - let db = &ctx.state().db; - let mut writer = db.update_write_txn()?; +#[delete("/indexes/{index_uid}/settings/synonyms")] +pub async fn delete( + data: web::Data, + path: web::Path, +) -> aweb::Result { + let index = data.db.open_index(&path.index_uid) + .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; let settings = SettingsUpdate { synonyms: UpdateState::Clear, ..SettingsUpdate::default() }; - let update_id = index.settings_update(&mut writer, settings)?; + let mut writer = data.db.update_write_txn() + .map_err(|err| ResponseError::Internal(err.to_string()))?; + let update_id = index.settings_update(&mut writer, settings) + .map_err(|err| ResponseError::Internal(err.to_string()))?; - writer.commit()?; + writer.commit() + .map_err(|err| ResponseError::Internal(err.to_string()))?; - let response_body = IndexUpdateResponse { update_id }; - Ok(tide::Response::new(202).body_json(&response_body)?) + Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } From 6a1f73a30410374b2ed99e0e0a1c1d210892c669 Mon Sep 17 00:00:00 2001 From: Quentin de Quelen Date: Fri, 10 Apr 2020 19:05:05 +0200 Subject: [PATCH 10/21] clippy + fmt --- meilisearch-http/src/data.rs | 2 +- meilisearch-http/src/error.rs | 5 +- meilisearch-http/src/main.rs | 18 +- meilisearch-http/src/routes/document.rs | 134 +++++--- meilisearch-http/src/routes/health.rs | 40 +-- meilisearch-http/src/routes/index.rs | 210 +++++++++---- meilisearch-http/src/routes/key.rs | 9 +- meilisearch-http/src/routes/mod.rs | 12 +- meilisearch-http/src/routes/search.rs | 21 +- meilisearch-http/src/routes/setting.rs | 291 +++++++++++++----- meilisearch-http/src/routes/stats.rs | 77 +++-- meilisearch-http/src/routes/stop_words.rs | 58 ++-- meilisearch-http/src/routes/synonym.rs | 64 ++-- meilisearch-http/tests/documents_add.rs | 14 +- meilisearch-http/tests/index.rs | 8 +- .../tests/settings_accept_new_fields.rs | 3 +- .../tests/settings_ranking_rules.rs | 6 +- 17 files changed, 633 insertions(+), 339 deletions(-) diff --git a/meilisearch-http/src/data.rs b/meilisearch-http/src/data.rs index d6836258a..23f7efb69 100644 --- a/meilisearch-http/src/data.rs +++ b/meilisearch-http/src/data.rs @@ -135,7 +135,7 @@ impl Data { let db = Arc::new(Database::open_or_create(opt.db_path).unwrap()); let mut api_keys = ApiKeys { - master: opt.master_key.clone(), + master: opt.master_key, private: None, public: None, }; diff --git a/meilisearch-http/src/error.rs b/meilisearch-http/src/error.rs index 57a996e84..6c3210e1b 100644 --- a/meilisearch-http/src/error.rs +++ b/meilisearch-http/src/error.rs @@ -1,8 +1,9 @@ use std::fmt; -use serde_json::json; -use actix_web as aweb; + use actix_http::ResponseBuilder; +use actix_web as aweb; use actix_web::http::StatusCode; +use serde_json::json; #[derive(Debug)] pub enum ResponseError { diff --git a/meilisearch-http/src/main.rs b/meilisearch-http/src/main.rs index edddb37d6..e0e19448b 100644 --- a/meilisearch-http/src/main.rs +++ b/meilisearch-http/src/main.rs @@ -1,14 +1,14 @@ use std::{env, thread}; +use actix_web::middleware::Logger; +use actix_web::{web, App, HttpServer}; use log::info; use main_error::MainError; -use structopt::StructOpt; -use actix_web::middleware::Logger; -use actix_web::{web, HttpServer, App}; use meilisearch_http::data::Data; use meilisearch_http::option::Opt; use meilisearch_http::routes; use meilisearch_http::routes::index_update_callback; +use structopt::StructOpt; mod analytics; @@ -30,7 +30,7 @@ async fn main() -> Result<(), MainError> { .into(), ); } - }, + } "development" => { env_logger::from_env(env_logger::Env::default().default_filter_or("info")).init(); } @@ -50,7 +50,7 @@ async fn main() -> Result<(), MainError> { print_launch_resume(&opt, &data); - HttpServer::new(move || + HttpServer::new(move || { App::new() .wrap(Logger::default()) .app_data(web::Data::new(data.clone())) @@ -103,10 +103,10 @@ async fn main() -> Result<(), MainError> { .service(routes::stats::get_sys_info_pretty) .service(routes::health::get_health) .service(routes::health::change_healthyness) - ) - .bind(opt.http_addr)? - .run() - .await?; + }) + .bind(opt.http_addr)? + .run() + .await?; Ok(()) } diff --git a/meilisearch-http/src/routes/document.rs b/meilisearch-http/src/routes/document.rs index 99e40e481..763402fd2 100644 --- a/meilisearch-http/src/routes/document.rs +++ b/meilisearch-http/src/routes/document.rs @@ -1,20 +1,21 @@ use std::collections::BTreeSet; + +use actix_web as aweb; +use actix_web::{delete, get, post, put, web, HttpResponse}; use indexmap::IndexMap; use serde::Deserialize; use serde_json::Value; -use actix_web::{web, get, post, put, delete, HttpResponse}; -use actix_web as aweb; use crate::error::ResponseError; +use crate::routes::{IndexParam, IndexUpdateResponse}; use crate::Data; -use crate::routes::{IndexUpdateResponse, IndexParam}; type Document = IndexMap; #[derive(Default, Deserialize)] pub struct DocumentParam { index_uid: String, - document_id: String + document_id: String, } #[get("/indexes/{index_uid}/documents/{document_id}")] @@ -22,14 +23,19 @@ pub async fn get_document( data: web::Data, path: web::Path, ) -> aweb::Result> { - let index = data.db.open_index(&path.index_uid) + let index = data + .db + .open_index(&path.index_uid) .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; let document_id = meilisearch_core::serde::compute_document_id(path.document_id.clone()); - let reader = data.db.main_read_txn() + let reader = data + .db + .main_read_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; - let response = index.document::(&reader, None, document_id) + let response = index + .document::(&reader, None, document_id) .map_err(|_| ResponseError::DocumentNotFound(path.document_id.clone()))? .ok_or(ResponseError::DocumentNotFound(path.document_id.clone()))?; @@ -41,26 +47,31 @@ pub async fn delete_document( data: web::Data, path: web::Path, ) -> aweb::Result { - let index = data.db.open_index(&path.index_uid) + let index = data + .db + .open_index(&path.index_uid) .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; let document_id = meilisearch_core::serde::compute_document_id(path.document_id.clone()); - let mut update_writer = data.db.update_write_txn() + let mut update_writer = data + .db + .update_write_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; let mut documents_deletion = index.documents_deletion(); documents_deletion.delete_document_by_id(document_id); - let update_id = documents_deletion.finalize(&mut update_writer) + let update_id = documents_deletion + .finalize(&mut update_writer) .map_err(|err| ResponseError::Internal(err.to_string()))?; - update_writer.commit() + update_writer + .commit() .map_err(|err| ResponseError::Internal(err.to_string()))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } - #[derive(Default, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct BrowseQuery { @@ -75,14 +86,17 @@ pub async fn get_all_documents( path: web::Path, params: web::Query, ) -> aweb::Result>> { - - let index = data.db.open_index(&path.index_uid) + let index = data + .db + .open_index(&path.index_uid) .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; let offset = params.offset.unwrap_or(0); let limit = params.limit.unwrap_or(20); - let reader = data.db.main_read_txn() + let reader = data + .db + .main_read_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; let documents_ids: Result, _> = index @@ -93,10 +107,11 @@ pub async fn get_all_documents( .take(limit) .collect(); - let documents_ids = documents_ids - .map_err(|err| ResponseError::Internal(err.to_string()))?; + let documents_ids = documents_ids.map_err(|err| ResponseError::Internal(err.to_string()))?; - let attributes = params.attributes_to_retrieve.clone() + let attributes = params + .attributes_to_retrieve + .clone() .map(|a| a.split(',').map(|a| a.to_string()).collect()); let mut response_body = Vec::::new(); @@ -129,39 +144,48 @@ async fn update_multiple_documents( path: web::Path, params: web::Query, body: web::Json>, - is_partial: bool + is_partial: bool, ) -> aweb::Result { - - let index = data.db.open_index(path.index_uid.clone()) + let index = data + .db + .open_index(path.index_uid.clone()) .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; - let reader = data.db.main_read_txn() + let reader = data + .db + .main_read_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; let mut schema = index .main .schema(&reader) .map_err(|err| ResponseError::Internal(err.to_string()))? - .ok_or(ResponseError::Internal("Impossible to retrieve the schema".to_string()))?; + .ok_or(ResponseError::Internal( + "Impossible to retrieve the schema".to_string(), + ))?; if schema.primary_key().is_none() { let id = match params.primary_key.clone() { Some(id) => id, - None => { - body.first() - .and_then(|docs| find_primary_key(docs)) - .ok_or(ResponseError::BadRequest("Impossible to infer the primary key".to_string()))? - } + None => body.first().and_then(|docs| find_primary_key(docs)).ok_or( + ResponseError::BadRequest("Impossible to infer the primary key".to_string()), + )?, }; - let mut writer = data.db.main_write_txn() + let mut writer = data + .db + .main_write_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; - schema.set_primary_key(&id) + schema + .set_primary_key(&id) .map_err(|e| ResponseError::Internal(e.to_string()))?; - index.main.put_schema(&mut writer, &schema) + index + .main + .put_schema(&mut writer, &schema) .map_err(|e| ResponseError::Internal(e.to_string()))?; - writer.commit() + writer + .commit() .map_err(|err| ResponseError::Internal(err.to_string()))?; } @@ -175,11 +199,15 @@ async fn update_multiple_documents( document_addition.update_document(document); } - let mut update_writer = data.db.update_write_txn() + let mut update_writer = data + .db + .update_write_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; - let update_id = document_addition.finalize(&mut update_writer) + let update_id = document_addition + .finalize(&mut update_writer) .map_err(|e| ResponseError::Internal(e.to_string()))?; - update_writer.commit() + update_writer + .commit() .map_err(|err| ResponseError::Internal(err.to_string()))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) @@ -190,7 +218,7 @@ pub async fn add_documents( data: web::Data, path: web::Path, params: web::Query, - body: web::Json> + body: web::Json>, ) -> aweb::Result { update_multiple_documents(data, path, params, body, false).await } @@ -200,7 +228,7 @@ pub async fn update_documents( data: web::Data, path: web::Path, params: web::Query, - body: web::Json> + body: web::Json>, ) -> aweb::Result { update_multiple_documents(data, path, params, body, true).await } @@ -209,13 +237,16 @@ pub async fn update_documents( pub async fn delete_documents( data: web::Data, path: web::Path, - body: web::Json> + body: web::Json>, ) -> aweb::Result { - - let index = data.db.open_index(path.index_uid.clone()) + let index = data + .db + .open_index(path.index_uid.clone()) .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; - let mut writer = data.db.update_write_txn() + let mut writer = data + .db + .update_write_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; let mut documents_deletion = index.documents_deletion(); @@ -227,10 +258,12 @@ pub async fn delete_documents( } } - let update_id = documents_deletion.finalize(&mut writer) + let update_id = documents_deletion + .finalize(&mut writer) .map_err(|e| ResponseError::Internal(e.to_string()))?; - writer.commit() + writer + .commit() .map_err(|err| ResponseError::Internal(err.to_string()))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) @@ -241,17 +274,22 @@ pub async fn clear_all_documents( data: web::Data, path: web::Path, ) -> aweb::Result { - - let index = data.db.open_index(path.index_uid.clone()) + let index = data + .db + .open_index(path.index_uid.clone()) .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; - let mut writer = data.db.update_write_txn() + let mut writer = data + .db + .update_write_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; - let update_id = index.clear_all(&mut writer) + let update_id = index + .clear_all(&mut writer) .map_err(|e| ResponseError::Internal(e.to_string()))?; - writer.commit() + writer + .commit() .map_err(|err| ResponseError::Internal(err.to_string()))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) diff --git a/meilisearch-http/src/routes/health.rs b/meilisearch-http/src/routes/health.rs index f1b92078d..8b4433303 100644 --- a/meilisearch-http/src/routes/health.rs +++ b/meilisearch-http/src/routes/health.rs @@ -1,17 +1,17 @@ use crate::error::ResponseError; -use actix_web::{web, get, put, HttpResponse}; -use actix_web as aweb; use crate::Data; +use actix_web as aweb; +use actix_web::{get, put, web, HttpResponse}; use heed::types::{Str, Unit}; use serde::Deserialize; const UNHEALTHY_KEY: &str = "_is_unhealthy"; #[get("/health")] -pub async fn get_health( - data: web::Data, -) -> aweb::Result { - let reader = data.db.main_read_txn() +pub async fn get_health(data: web::Data) -> aweb::Result { + let reader = data + .db + .main_read_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; let common_store = data.db.common_store(); @@ -23,29 +23,33 @@ pub async fn get_health( Ok(HttpResponse::Ok().finish()) } -pub async fn set_healthy( - data: web::Data, -) -> aweb::Result { - let mut writer = data.db.main_write_txn() +pub async fn set_healthy(data: web::Data) -> aweb::Result { + let mut writer = data + .db + .main_write_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; let common_store = data.db.common_store(); - common_store.delete::<_, Str>(&mut writer, UNHEALTHY_KEY) + common_store + .delete::<_, Str>(&mut writer, UNHEALTHY_KEY) .map_err(|e| ResponseError::Internal(e.to_string()))?; - writer.commit() + writer + .commit() .map_err(|err| ResponseError::Internal(err.to_string()))?; Ok(HttpResponse::Ok().finish()) } -pub async fn set_unhealthy( - data: web::Data, -) -> aweb::Result { - let mut writer = data.db.main_write_txn() +pub async fn set_unhealthy(data: web::Data) -> aweb::Result { + let mut writer = data + .db + .main_write_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; let common_store = data.db.common_store(); - common_store.put::<_, Str, Unit>(&mut writer, UNHEALTHY_KEY, &()) + common_store + .put::<_, Str, Unit>(&mut writer, UNHEALTHY_KEY, &()) .map_err(|e| ResponseError::Internal(e.to_string()))?; - writer.commit() + writer + .commit() .map_err(|err| ResponseError::Internal(err.to_string()))?; Ok(HttpResponse::Ok().finish()) diff --git a/meilisearch-http/src/routes/index.rs b/meilisearch-http/src/routes/index.rs index ec7f65315..f1153ad74 100644 --- a/meilisearch-http/src/routes/index.rs +++ b/meilisearch-http/src/routes/index.rs @@ -1,14 +1,14 @@ +use actix_web as aweb; +use actix_web::{delete, get, post, web, HttpResponse}; use chrono::{DateTime, Utc}; use log::error; +use meilisearch_core::UpdateStatus; use rand::seq::SliceRandom; use serde::{Deserialize, Serialize}; -use actix_web::{web, get, post, delete, HttpResponse}; -use actix_web as aweb; -use meilisearch_core::UpdateStatus; use crate::error::ResponseError; -use crate::Data; use crate::routes::IndexParam; +use crate::Data; fn generate_uid() -> String { let mut rng = rand::thread_rng(); @@ -30,11 +30,10 @@ pub struct IndexResponse { } #[get("/indexes")] -pub async fn list_indexes( - data: web::Data, -) -> aweb::Result>> { - - let reader = data.db.main_read_txn() +pub async fn list_indexes(data: web::Data) -> aweb::Result>> { + let reader = data + .db + .main_read_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; let mut response_body = Vec::new(); @@ -44,15 +43,27 @@ pub async fn list_indexes( match index { Some(index) => { - let name = index.main.name(&reader) + let name = index + .main + .name(&reader) .map_err(|e| ResponseError::Internal(e.to_string()))? - .ok_or(ResponseError::Internal("Impossible to get the name of an index".to_string()))?; - let created_at = index.main.created_at(&reader) + .ok_or(ResponseError::Internal( + "Impossible to get the name of an index".to_string(), + ))?; + let created_at = index + .main + .created_at(&reader) .map_err(|e| ResponseError::Internal(e.to_string()))? - .ok_or(ResponseError::Internal("Impossible to get the create date of an index".to_string()))?; - let updated_at = index.main.updated_at(&reader) + .ok_or(ResponseError::Internal( + "Impossible to get the create date of an index".to_string(), + ))?; + let updated_at = index + .main + .updated_at(&reader) .map_err(|e| ResponseError::Internal(e.to_string()))? - .ok_or(ResponseError::Internal("Impossible to get the last update date of an index".to_string()))?; + .ok_or(ResponseError::Internal( + "Impossible to get the last update date of an index".to_string(), + ))?; let primary_key = match index.main.schema(&reader) { Ok(Some(schema)) => match schema.primary_key() { @@ -86,22 +97,37 @@ pub async fn get_index( data: web::Data, path: web::Path, ) -> aweb::Result> { - - let index = data.db.open_index(path.index_uid.clone()) + let index = data + .db + .open_index(path.index_uid.clone()) .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; - let reader = data.db.main_read_txn() + let reader = data + .db + .main_read_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; - let name = index.main.name(&reader) + let name = index + .main + .name(&reader) .map_err(|e| ResponseError::Internal(e.to_string()))? - .ok_or(ResponseError::Internal("Impossible to get the name of an index".to_string()))?; - let created_at = index.main.created_at(&reader) + .ok_or(ResponseError::Internal( + "Impossible to get the name of an index".to_string(), + ))?; + let created_at = index + .main + .created_at(&reader) .map_err(|e| ResponseError::Internal(e.to_string()))? - .ok_or(ResponseError::Internal("Impossible to get the create date of an index".to_string()))?; - let updated_at = index.main.updated_at(&reader) + .ok_or(ResponseError::Internal( + "Impossible to get the create date of an index".to_string(), + ))?; + let updated_at = index + .main + .updated_at(&reader) .map_err(|e| ResponseError::Internal(e.to_string()))? - .ok_or(ResponseError::Internal("Impossible to get the last update date of an index".to_string()))?; + .ok_or(ResponseError::Internal( + "Impossible to get the last update date of an index".to_string(), + ))?; let primary_key = match index.main.schema(&reader) { Ok(Some(schema)) => match schema.primary_key() { @@ -131,11 +157,12 @@ pub struct IndexCreateRequest { #[post("/indexes")] pub async fn create_index( data: web::Data, - body: web::Json + body: web::Json, ) -> aweb::Result> { - if let (None, None) = (body.name.clone(), body.uid.clone()) { - return Err(ResponseError::BadRequest("Index creation must have an uid".to_string()).into()); + return Err( + ResponseError::BadRequest("Index creation must have an uid".to_string()).into(), + ); } let uid = match body.uid.clone() { @@ -157,14 +184,20 @@ pub async fn create_index( }, }; - let created_index = data.db.create_index(&uid) + let created_index = data + .db + .create_index(&uid) .map_err(|e| ResponseError::CreateIndex(e.to_string()))?; - let mut writer = data.db.main_write_txn() + let mut writer = data + .db + .main_write_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; let name = body.name.clone().unwrap_or(uid.clone()); - created_index.main.put_name(&mut writer, &name) + created_index + .main + .put_name(&mut writer, &name) .map_err(|e| ResponseError::Internal(e.to_string()))?; let created_at = created_index @@ -180,16 +213,23 @@ pub async fn create_index( .ok_or(ResponseError::Internal("".to_string()))?; if let Some(id) = body.primary_key.clone() { - if let Some(mut schema) = created_index.main.schema(&mut writer) - .map_err(|e| ResponseError::Internal(e.to_string()))? { - schema.set_primary_key(&id) + if let Some(mut schema) = created_index + .main + .schema(&writer) + .map_err(|e| ResponseError::Internal(e.to_string()))? + { + schema + .set_primary_key(&id) .map_err(|e| ResponseError::BadRequest(e.to_string()))?; - created_index.main.put_schema(&mut writer, &schema) + created_index + .main + .put_schema(&mut writer, &schema) .map_err(|e| ResponseError::Internal(e.to_string()))?; } } - writer.commit() + writer + .commit() .map_err(|err| ResponseError::Internal(err.to_string()))?; Ok(web::Json(IndexResponse { @@ -222,55 +262,85 @@ pub struct UpdateIndexResponse { pub async fn update_index( data: web::Data, path: web::Path, - body: web::Json + body: web::Json, ) -> aweb::Result> { - - let index = data.db.open_index(path.index_uid.clone()) + let index = data + .db + .open_index(path.index_uid.clone()) .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; - let mut writer = data.db.main_write_txn() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + let mut writer = data + .db + .main_write_txn() + .map_err(|err| ResponseError::Internal(err.to_string()))?; if let Some(name) = body.name.clone() { - index.main.put_name(&mut writer, &name) + index + .main + .put_name(&mut writer, &name) .map_err(|e| ResponseError::Internal(e.to_string()))?; } if let Some(id) = body.primary_key.clone() { - if let Some(mut schema) = index.main.schema(&mut writer) - .map_err(|e| ResponseError::Internal(e.to_string()))? { + if let Some(mut schema) = index + .main + .schema(&writer) + .map_err(|e| ResponseError::Internal(e.to_string()))? + { match schema.primary_key() { Some(_) => { - return Err(ResponseError::BadRequest("The primary key cannot be updated".to_string()).into()); + return Err(ResponseError::BadRequest( + "The primary key cannot be updated".to_string(), + ) + .into()); } None => { schema .set_primary_key(&id) .map_err(|e| ResponseError::Internal(e.to_string()))?; - index.main.put_schema(&mut writer, &schema) + index + .main + .put_schema(&mut writer, &schema) .map_err(|e| ResponseError::Internal(e.to_string()))?; } } } } - index.main.put_updated_at(&mut writer) + index + .main + .put_updated_at(&mut writer) .map_err(|e| ResponseError::Internal(e.to_string()))?; - writer.commit() + writer + .commit() .map_err(|err| ResponseError::Internal(err.to_string()))?; - let reader = data.db.main_read_txn() + let reader = data + .db + .main_read_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; - let name = index.main.name(&reader) + let name = index + .main + .name(&reader) .map_err(|e| ResponseError::Internal(e.to_string()))? - .ok_or(ResponseError::Internal("Impossible to get the name of an index".to_string()))?; - let created_at = index.main.created_at(&reader) + .ok_or(ResponseError::Internal( + "Impossible to get the name of an index".to_string(), + ))?; + let created_at = index + .main + .created_at(&reader) .map_err(|e| ResponseError::Internal(e.to_string()))? - .ok_or(ResponseError::Internal("Impossible to get the create date of an index".to_string()))?; - let updated_at = index.main.updated_at(&reader) + .ok_or(ResponseError::Internal( + "Impossible to get the create date of an index".to_string(), + ))?; + let updated_at = index + .main + .updated_at(&reader) .map_err(|e| ResponseError::Internal(e.to_string()))? - .ok_or(ResponseError::Internal("Impossible to get the last update date of an index".to_string()))?; + .ok_or(ResponseError::Internal( + "Impossible to get the last update date of an index".to_string(), + ))?; let primary_key = match index.main.schema(&reader) { Ok(Some(schema)) => match schema.primary_key() { @@ -294,8 +364,8 @@ pub async fn delete_index( data: web::Data, path: web::Path, ) -> aweb::Result { - - data.db.delete_index(&path.index_uid) + data.db + .delete_index(&path.index_uid) .map_err(|e| ResponseError::Internal(e.to_string()))?; HttpResponse::NoContent().await @@ -304,7 +374,7 @@ pub async fn delete_index( #[derive(Default, Deserialize)] pub struct UpdateParam { index_uid: String, - update_id: u64 + update_id: u64, } #[get("/indexes/{index_uid}/updates/{update_id}")] @@ -312,19 +382,23 @@ pub async fn get_update_status( data: web::Data, path: web::Path, ) -> aweb::Result> { - - let index = data.db.open_index(path.index_uid.clone()) + let index = data + .db + .open_index(path.index_uid.clone()) .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; - let reader = data.db.update_read_txn() + let reader = data + .db + .update_read_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; - let status = index.update_status(&reader, path.update_id) + let status = index + .update_status(&reader, path.update_id) .map_err(|e| ResponseError::Internal(e.to_string()))?; match status { Some(status) => Ok(web::Json(status)), - None => Err(ResponseError::NotFound(format!("Update {} not found", path.update_id)).into()) + None => Err(ResponseError::NotFound(format!("Update {} not found", path.update_id)).into()), } } @@ -333,14 +407,18 @@ pub async fn get_all_updates_status( data: web::Data, path: web::Path, ) -> aweb::Result>> { - - let index = data.db.open_index(path.index_uid.clone()) + let index = data + .db + .open_index(path.index_uid.clone()) .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; - let reader = data.db.update_read_txn() + let reader = data + .db + .update_read_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; - let response = index.all_updates_status(&reader) + let response = index + .all_updates_status(&reader) .map_err(|err| ResponseError::Internal(err.to_string()))?; Ok(web::Json(response)) diff --git a/meilisearch-http/src/routes/key.rs b/meilisearch-http/src/routes/key.rs index d807840b7..76ab3a116 100644 --- a/meilisearch-http/src/routes/key.rs +++ b/meilisearch-http/src/routes/key.rs @@ -1,8 +1,7 @@ use crate::Data; -use actix_web::{web, get}; +use actix_web::{get, web}; use serde::Serialize; - #[derive(Default, Serialize)] pub struct KeysResponse { private: Option, @@ -10,11 +9,9 @@ pub struct KeysResponse { } #[get("/keys")] -pub async fn list( - data: web::Data, -) -> web::Json { +pub async fn list(data: web::Data) -> web::Json { let api_keys = data.api_keys.clone(); - web::Json(KeysResponse{ + web::Json(KeysResponse { private: api_keys.private, public: api_keys.public, }) diff --git a/meilisearch-http/src/routes/mod.rs b/meilisearch-http/src/routes/mod.rs index c8401e905..66cabd52d 100644 --- a/meilisearch-http/src/routes/mod.rs +++ b/meilisearch-http/src/routes/mod.rs @@ -1,7 +1,7 @@ use actix_web::{get, HttpResponse}; -use serde::{Serialize, Deserialize}; use log::error; use meilisearch_core::ProcessedUpdateResult; +use serde::{Deserialize, Serialize}; use crate::Data; @@ -10,14 +10,14 @@ pub mod health; pub mod index; pub mod key; pub mod search; -pub mod stats; pub mod setting; +pub mod stats; pub mod stop_words; pub mod synonym; #[derive(Default, Deserialize)] pub struct IndexParam { - index_uid: String + index_uid: String, } #[derive(Default, Serialize)] @@ -28,9 +28,7 @@ pub struct IndexUpdateResponse { impl IndexUpdateResponse { pub fn with_id(update_id: u64) -> Self { - Self { - update_id, - } + Self { update_id } } } @@ -48,7 +46,6 @@ pub async fn load_css() -> HttpResponse { .body(include_str!("../../public/bulma.min.css").to_string()) } - pub fn index_update_callback(index_uid: &str, data: &Data, status: ProcessedUpdateResult) { if status.error.is_some() { return; @@ -82,7 +79,6 @@ pub fn index_update_callback(index_uid: &str, data: &Data, status: ProcessedUpda } } - // pub fn load_routes(app: &mut tide::Server) { // app.at("/").get(|_| async { // tide::Response::new(200) diff --git a/meilisearch-http/src/routes/search.rs b/meilisearch-http/src/routes/search.rs index afdc96fcb..cd88694ff 100644 --- a/meilisearch-http/src/routes/search.rs +++ b/meilisearch-http/src/routes/search.rs @@ -4,15 +4,15 @@ use std::time::Duration; use log::warn; use meilisearch_core::Index; +use actix_web as aweb; +use actix_web::{get, post, web}; use rayon::iter::{IntoParallelIterator, ParallelIterator}; use serde::{Deserialize, Serialize}; -use actix_web::{web, get, post}; -use actix_web as aweb; use crate::error::ResponseError; use crate::helpers::meilisearch::{Error, IndexSearchExt, SearchHit, SearchResult}; -use crate::Data; use crate::routes::IndexParam; +use crate::Data; #[derive(Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] @@ -35,18 +35,23 @@ pub async fn search_with_url_query( path: web::Path, params: web::Query, ) -> aweb::Result> { - - let index = data.db.open_index(path.index_uid.clone()) + let index = data + .db + .open_index(path.index_uid.clone()) .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; - let reader = data.db.main_read_txn() + let reader = data + .db + .main_read_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; let schema = index .main .schema(&reader) .map_err(|err| ResponseError::Internal(err.to_string()))? - .ok_or(ResponseError::Internal("Impossible to retrieve the schema".to_string()))?; + .ok_or(ResponseError::Internal( + "Impossible to retrieve the schema".to_string(), + ))?; let mut search_builder = index.new_search(params.q.clone()); @@ -182,7 +187,6 @@ pub async fn search_multi_index( data: web::Data, body: web::Json, ) -> aweb::Result> { - let mut index_list = body.clone().indexes; for index in index_list.clone() { @@ -203,7 +207,6 @@ pub async fn search_multi_index( } } - let par_body = body.clone(); let responses_per_index: Vec<(String, SearchResult)> = index_list .into_par_iter() diff --git a/meilisearch-http/src/routes/setting.rs b/meilisearch-http/src/routes/setting.rs index 603b13902..2883c3997 100644 --- a/meilisearch-http/src/routes/setting.rs +++ b/meilisearch-http/src/routes/setting.rs @@ -1,42 +1,58 @@ +use actix_web as aweb; +use actix_web::{delete, get, post, web, HttpResponse}; use meilisearch_core::settings::{Settings, SettingsUpdate, UpdateState, DEFAULT_RANKING_RULES}; use std::collections::{BTreeMap, BTreeSet, HashSet}; -use actix_web::{web, get, post, delete, HttpResponse}; -use actix_web as aweb; -use crate::error::{ResponseError}; +use crate::error::ResponseError; +use crate::routes::{IndexParam, IndexUpdateResponse}; use crate::Data; -use crate::routes::{IndexUpdateResponse, IndexParam}; #[post("/indexes/{index_uid}/settings")] pub async fn update_all( data: web::Data, path: web::Path, ) -> aweb::Result { - let index = data.db.open_index(&path.index_uid) + let index = data + .db + .open_index(&path.index_uid) .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; - let reader = data.db.main_read_txn() + let reader = data + .db + .main_read_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; - let stop_words_fst = index.main.stop_words_fst(&reader) + let stop_words_fst = index + .main + .stop_words_fst(&reader) .map_err(|err| ResponseError::Internal(err.to_string()))?; - let stop_words = stop_words_fst.unwrap_or_default().stream().into_strs() + let stop_words = stop_words_fst + .unwrap_or_default() + .stream() + .into_strs() .map_err(|err| ResponseError::Internal(err.to_string()))?; let stop_words: BTreeSet = stop_words.into_iter().collect(); - let synonyms_fst = index.main.synonyms_fst(&reader) + let synonyms_fst = index + .main + .synonyms_fst(&reader) .map_err(|err| ResponseError::Internal(err.to_string()))? .unwrap_or_default(); - let synonyms_list = synonyms_fst.stream().into_strs() + let synonyms_list = synonyms_fst + .stream() + .into_strs() .map_err(|err| ResponseError::Internal(err.to_string()))?; let mut synonyms = BTreeMap::new(); let index_synonyms = &index.synonyms; for synonym in synonyms_list { - let alternative_list = index_synonyms.synonyms(&reader, synonym.as_bytes()) + let alternative_list = index_synonyms + .synonyms(&reader, synonym.as_bytes()) .map_err(|err| ResponseError::Internal(err.to_string()))?; if let Some(list) = alternative_list { - let list = list.stream().into_strs() + let list = list + .stream() + .into_strs() .map_err(|err| ResponseError::Internal(err.to_string()))?; synonyms.insert(synonym, list); } @@ -51,10 +67,14 @@ pub async fn update_all( .map(|r| r.to_string()) .collect(); - let distinct_attribute = index.main.distinct_attribute(&reader) + let distinct_attribute = index + .main + .distinct_attribute(&reader) .map_err(|err| ResponseError::Internal(err.to_string()))?; - let schema = index.main.schema(&reader) + let schema = index + .main + .schema(&reader) .map_err(|err| ResponseError::Internal(err.to_string()))?; let searchable_attributes = schema.clone().map(|s| { @@ -92,16 +112,24 @@ pub async fn get_all( path: web::Path, body: web::Json, ) -> aweb::Result { - let index = data.db.open_index(&path.index_uid) + let index = data + .db + .open_index(&path.index_uid) .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; - let mut writer = data.db.update_write_txn() + let mut writer = data + .db + .update_write_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; - let settings = body.into_inner().into_update() + let settings = body + .into_inner() + .into_update() .map_err(|e| ResponseError::BadRequest(e.to_string()))?; - let update_id = index.settings_update(&mut writer, settings) + let update_id = index + .settings_update(&mut writer, settings) .map_err(|err| ResponseError::Internal(err.to_string()))?; - writer.commit() + writer + .commit() .map_err(|err| ResponseError::Internal(err.to_string()))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) @@ -112,9 +140,13 @@ pub async fn delete_all( data: web::Data, path: web::Path, ) -> aweb::Result { - let index = data.db.open_index(&path.index_uid) + let index = data + .db + .open_index(&path.index_uid) .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; - let mut writer = data.db.update_write_txn() + let mut writer = data + .db + .update_write_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; let settings = SettingsUpdate { @@ -128,9 +160,11 @@ pub async fn delete_all( accept_new_fields: UpdateState::Clear, }; - let update_id = index.settings_update(&mut writer, settings) + let update_id = index + .settings_update(&mut writer, settings) .map_err(|err| ResponseError::Internal(err.to_string()))?; - writer.commit() + writer + .commit() .map_err(|err| ResponseError::Internal(err.to_string()))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) @@ -141,10 +175,14 @@ pub async fn get_rules( data: web::Data, path: web::Path, ) -> aweb::Result { - let index = data.db.open_index(&path.index_uid) + let index = data + .db + .open_index(&path.index_uid) .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; - let reader = data.db.main_read_txn() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + let reader = data + .db + .main_read_txn() + .map_err(|err| ResponseError::Internal(err.to_string()))?; let ranking_rules = index .main @@ -164,7 +202,9 @@ pub async fn update_rules( path: web::Path, body: web::Json>>, ) -> aweb::Result { - let index = data.db.open_index(&path.index_uid) + let index = data + .db + .open_index(&path.index_uid) .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; let settings = Settings { @@ -172,13 +212,18 @@ pub async fn update_rules( ..Settings::default() }; - let mut writer = data.db.update_write_txn() + let mut writer = data + .db + .update_write_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; - let settings = settings.into_update() + let settings = settings + .into_update() .map_err(|e| ResponseError::BadRequest(e.to_string()))?; - let update_id = index.settings_update(&mut writer, settings) + let update_id = index + .settings_update(&mut writer, settings) .map_err(|err| ResponseError::Internal(err.to_string()))?; - writer.commit() + writer + .commit() .map_err(|err| ResponseError::Internal(err.to_string()))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) @@ -189,9 +234,13 @@ pub async fn delete_rules( data: web::Data, path: web::Path, ) -> aweb::Result { - let index = data.db.open_index(&path.index_uid) + let index = data + .db + .open_index(&path.index_uid) .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; - let mut writer = data.db.update_write_txn() + let mut writer = data + .db + .update_write_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; let settings = SettingsUpdate { @@ -199,10 +248,12 @@ pub async fn delete_rules( ..SettingsUpdate::default() }; - let update_id = index.settings_update(&mut writer, settings) + let update_id = index + .settings_update(&mut writer, settings) .map_err(|err| ResponseError::Internal(err.to_string()))?; - writer.commit() + writer + .commit() .map_err(|err| ResponseError::Internal(err.to_string()))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) @@ -213,11 +264,17 @@ pub async fn get_distinct( data: web::Data, path: web::Path, ) -> aweb::Result { - let index = data.db.open_index(&path.index_uid) + let index = data + .db + .open_index(&path.index_uid) .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; - let reader = data.db.main_read_txn() + let reader = data + .db + .main_read_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; - let distinct_attribute = index.main.distinct_attribute(&reader) + let distinct_attribute = index + .main + .distinct_attribute(&reader) .map_err(|err| ResponseError::Internal(err.to_string()))?; Ok(HttpResponse::Ok().json(distinct_attribute)) @@ -229,7 +286,9 @@ pub async fn update_distinct( path: web::Path, body: web::Json>, ) -> aweb::Result { - let index = data.db.open_index(&path.index_uid) + let index = data + .db + .open_index(&path.index_uid) .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; let settings = Settings { @@ -237,13 +296,18 @@ pub async fn update_distinct( ..Settings::default() }; - let mut writer = data.db.update_write_txn() + let mut writer = data + .db + .update_write_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; - let settings = settings.into_update() + let settings = settings + .into_update() .map_err(|e| ResponseError::BadRequest(e.to_string()))?; - let update_id = index.settings_update(&mut writer, settings) + let update_id = index + .settings_update(&mut writer, settings) .map_err(|err| ResponseError::Internal(err.to_string()))?; - writer.commit() + writer + .commit() .map_err(|err| ResponseError::Internal(err.to_string()))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) @@ -254,21 +318,27 @@ pub async fn delete_distinct( data: web::Data, path: web::Path, ) -> aweb::Result { - let index = data.db.open_index(&path.index_uid) + let index = data + .db + .open_index(&path.index_uid) .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; - let mut writer = data.db.update_write_txn() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + let mut writer = data + .db + .update_write_txn() + .map_err(|err| ResponseError::Internal(err.to_string()))?; let settings = SettingsUpdate { distinct_attribute: UpdateState::Clear, ..SettingsUpdate::default() }; - let update_id = index.settings_update(&mut writer, settings) - .map_err(|err| ResponseError::Internal(err.to_string()))?; + let update_id = index + .settings_update(&mut writer, settings) + .map_err(|err| ResponseError::Internal(err.to_string()))?; - writer.commit() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + writer + .commit() + .map_err(|err| ResponseError::Internal(err.to_string()))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -278,11 +348,17 @@ pub async fn get_searchable( data: web::Data, path: web::Path, ) -> aweb::Result { - let index = data.db.open_index(&path.index_uid) + let index = data + .db + .open_index(&path.index_uid) .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; - let reader = data.db.main_read_txn() + let reader = data + .db + .main_read_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; - let schema = index.main.schema(&reader) + let schema = index + .main + .schema(&reader) .map_err(|err| ResponseError::Internal(err.to_string()))?; let searchable_attributes: Option> = schema.map(|s| s.indexed_name().iter().map(|i| (*i).to_string()).collect()); @@ -296,7 +372,9 @@ pub async fn update_searchable( path: web::Path, body: web::Json>>, ) -> aweb::Result { - let index = data.db.open_index(&path.index_uid) + let index = data + .db + .open_index(&path.index_uid) .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; let settings = Settings { @@ -304,13 +382,18 @@ pub async fn update_searchable( ..Settings::default() }; - let mut writer = data.db.update_write_txn() + let mut writer = data + .db + .update_write_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; - let settings = settings.into_update() + let settings = settings + .into_update() .map_err(|e| ResponseError::BadRequest(e.to_string()))?; - let update_id = index.settings_update(&mut writer, settings) + let update_id = index + .settings_update(&mut writer, settings) .map_err(|err| ResponseError::Internal(err.to_string()))?; - writer.commit() + writer + .commit() .map_err(|err| ResponseError::Internal(err.to_string()))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) @@ -321,7 +404,9 @@ pub async fn delete_searchable( data: web::Data, path: web::Path, ) -> aweb::Result { - let index = data.db.open_index(&path.index_uid) + let index = data + .db + .open_index(&path.index_uid) .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; let settings = SettingsUpdate { @@ -329,11 +414,15 @@ pub async fn delete_searchable( ..SettingsUpdate::default() }; - let mut writer = data.db.update_write_txn() + let mut writer = data + .db + .update_write_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; - let update_id = index.settings_update(&mut writer, settings) + let update_id = index + .settings_update(&mut writer, settings) .map_err(|err| ResponseError::Internal(err.to_string()))?; - writer.commit() + writer + .commit() .map_err(|err| ResponseError::Internal(err.to_string()))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) @@ -344,12 +433,18 @@ pub async fn get_displayed( data: web::Data, path: web::Path, ) -> aweb::Result { - let index = data.db.open_index(&path.index_uid) + let index = data + .db + .open_index(&path.index_uid) .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; - let reader = data.db.main_read_txn() + let reader = data + .db + .main_read_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; - let schema = index.main.schema(&reader) + let schema = index + .main + .schema(&reader) .map_err(|err| ResponseError::Internal(err.to_string()))?; let displayed_attributes: Option> = schema.map(|s| { @@ -368,7 +463,9 @@ pub async fn update_displayed( path: web::Path, body: web::Json>>, ) -> aweb::Result { - let index = data.db.open_index(&path.index_uid) + let index = data + .db + .open_index(&path.index_uid) .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; let settings = Settings { @@ -376,13 +473,18 @@ pub async fn update_displayed( ..Settings::default() }; - let mut writer = data.db.update_write_txn() + let mut writer = data + .db + .update_write_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; - let settings = settings.into_update() + let settings = settings + .into_update() .map_err(|e| ResponseError::BadRequest(e.to_string()))?; - let update_id = index.settings_update(&mut writer, settings) + let update_id = index + .settings_update(&mut writer, settings) .map_err(|err| ResponseError::Internal(err.to_string()))?; - writer.commit() + writer + .commit() .map_err(|err| ResponseError::Internal(err.to_string()))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) @@ -393,7 +495,9 @@ pub async fn delete_displayed( data: web::Data, path: web::Path, ) -> aweb::Result { - let index = data.db.open_index(&path.index_uid) + let index = data + .db + .open_index(&path.index_uid) .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; let settings = SettingsUpdate { @@ -401,12 +505,16 @@ pub async fn delete_displayed( ..SettingsUpdate::default() }; - let mut writer = data.db.update_write_txn() - .map_err(|err| ResponseError::Internal(err.to_string()))?; - let update_id = index.settings_update(&mut writer, settings) - .map_err(|err| ResponseError::Internal(err.to_string()))?; - writer.commit() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + let mut writer = data + .db + .update_write_txn() + .map_err(|err| ResponseError::Internal(err.to_string()))?; + let update_id = index + .settings_update(&mut writer, settings) + .map_err(|err| ResponseError::Internal(err.to_string()))?; + writer + .commit() + .map_err(|err| ResponseError::Internal(err.to_string()))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -416,12 +524,18 @@ pub async fn get_accept_new_fields( data: web::Data, path: web::Path, ) -> aweb::Result { - let index = data.db.open_index(&path.index_uid) + let index = data + .db + .open_index(&path.index_uid) .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; - let reader = data.db.main_read_txn() + let reader = data + .db + .main_read_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; - let schema = index.main.schema(&reader) + let schema = index + .main + .schema(&reader) .map_err(|err| ResponseError::Internal(err.to_string()))?; let accept_new_fields = schema.map(|s| s.accept_new_fields()); @@ -435,7 +549,9 @@ pub async fn update_accept_new_fields( path: web::Path, body: web::Json>, ) -> aweb::Result { - let index = data.db.open_index(&path.index_uid) + let index = data + .db + .open_index(&path.index_uid) .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; let settings = Settings { @@ -443,13 +559,18 @@ pub async fn update_accept_new_fields( ..Settings::default() }; - let mut writer = data.db.update_write_txn() + let mut writer = data + .db + .update_write_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; - let settings = settings.into_update() + let settings = settings + .into_update() .map_err(|e| ResponseError::BadRequest(e.to_string()))?; - let update_id = index.settings_update(&mut writer, settings) + let update_id = index + .settings_update(&mut writer, settings) .map_err(|err| ResponseError::Internal(err.to_string()))?; - writer.commit() + writer + .commit() .map_err(|err| ResponseError::Internal(err.to_string()))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) diff --git a/meilisearch-http/src/routes/stats.rs b/meilisearch-http/src/routes/stats.rs index b2261a7dc..ee804457e 100644 --- a/meilisearch-http/src/routes/stats.rs +++ b/meilisearch-http/src/routes/stats.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use actix_web as aweb; -use actix_web::{web, get}; +use actix_web::{get, web}; use chrono::{DateTime, Utc}; use log::error; use pretty_bytes::converter::convert; @@ -9,9 +9,9 @@ use serde::Serialize; use sysinfo::{NetworkExt, ProcessExt, ProcessorExt, System, SystemExt}; use walkdir::WalkDir; -use crate::Data; use crate::error::ResponseError; use crate::routes::IndexParam; +use crate::Data; #[derive(Serialize)] #[serde(rename_all = "camelCase")] @@ -26,20 +26,30 @@ pub async fn index_stats( data: web::Data, path: web::Path, ) -> aweb::Result> { - let index = data.db.open_index(path.index_uid.clone()) + let index = data + .db + .open_index(path.index_uid.clone()) .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; - let reader = data.db.main_read_txn() + let reader = data + .db + .main_read_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; - let number_of_documents = index.main.number_of_documents(&reader) + let number_of_documents = index + .main + .number_of_documents(&reader) .map_err(|err| ResponseError::Internal(err.to_string()))?; - let fields_frequency = index.main.fields_frequency(&reader) + let fields_frequency = index + .main + .fields_frequency(&reader) .map_err(|err| ResponseError::Internal(err.to_string()))? .unwrap_or_default(); - let update_reader = data.db.update_read_txn() + let update_reader = data + .db + .update_read_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; let is_indexing = data @@ -63,15 +73,16 @@ pub struct StatsResult { } #[get("/stats")] -pub async fn get_stats( - data: web::Data, -) -> aweb::Result> { - +pub async fn get_stats(data: web::Data) -> aweb::Result> { let mut index_list = HashMap::new(); - let reader = data.db.main_read_txn() + let reader = data + .db + .main_read_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; - let update_reader = data.db.update_read_txn() + let update_reader = data + .db + .update_read_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; let indexes_set = data.db.indexes_uids(); @@ -79,10 +90,14 @@ pub async fn get_stats( let index = data.db.open_index(&index_uid); match index { Some(index) => { - let number_of_documents = index.main.number_of_documents(&reader) + let number_of_documents = index + .main + .number_of_documents(&reader) .map_err(|err| ResponseError::Internal(err.to_string()))?; - let fields_frequency = index.main.fields_frequency(&reader) + let fields_frequency = index + .main + .fields_frequency(&reader) .map_err(|err| ResponseError::Internal(err.to_string()))? .unwrap_or_default(); @@ -112,7 +127,8 @@ pub async fn get_stats( .filter(|metadata| metadata.is_file()) .fold(0, |acc, m| acc + m.len()); - let last_update = data.last_update(&reader) + let last_update = data + .last_update(&reader) .map_err(|err| ResponseError::Internal(err.to_string()))?; Ok(web::Json(StatsResult { @@ -200,9 +216,7 @@ impl SysInfo { } #[get("/sys-info")] -pub async fn get_sys_info( - data: web::Data, -) -> web::Json { +pub async fn get_sys_info(data: web::Data) -> web::Json { let mut sys = System::new(); let mut info = SysInfo::new(); @@ -216,11 +230,13 @@ pub async fn get_sys_info( info.global.used_memory = sys.get_used_memory(); info.global.total_swap = sys.get_total_swap(); info.global.used_swap = sys.get_used_swap(); - info.global.input_data = sys.get_networks() + info.global.input_data = sys + .get_networks() .into_iter() .map(|(_, n)| n.get_received()) .sum::(); - info.global.output_data = sys.get_networks() + info.global.output_data = sys + .get_networks() .into_iter() .map(|(_, n)| n.get_transmitted()) .sum::(); @@ -294,11 +310,8 @@ impl SysInfoPretty { } } - #[get("/sys-info/pretty")] -pub async fn get_sys_info_pretty( - data: web::Data, -) -> web::Json { +pub async fn get_sys_info_pretty(data: web::Data) -> web::Json { let mut sys = System::new(); let mut info = SysInfoPretty::new(); @@ -316,8 +329,18 @@ pub async fn get_sys_info_pretty( info.global.used_memory = convert(sys.get_used_memory() as f64 * 1024.0); info.global.total_swap = convert(sys.get_total_swap() as f64 * 1024.0); info.global.used_swap = convert(sys.get_used_swap() as f64 * 1024.0); - info.global.input_data = convert(sys.get_networks().into_iter().map(|(_, n)| n.get_received()).sum::() as f64); - info.global.output_data = convert(sys.get_networks().into_iter().map(|(_, n)| n.get_transmitted()).sum::() as f64); + info.global.input_data = convert( + sys.get_networks() + .into_iter() + .map(|(_, n)| n.get_received()) + .sum::() as f64, + ); + info.global.output_data = convert( + sys.get_networks() + .into_iter() + .map(|(_, n)| n.get_transmitted()) + .sum::() as f64, + ); if let Some(process) = sys.get_process(data.server_pid) { info.process.memory = convert(process.memory() as f64 * 1024.0); diff --git a/meilisearch-http/src/routes/stop_words.rs b/meilisearch-http/src/routes/stop_words.rs index 2234497bf..905f90150 100644 --- a/meilisearch-http/src/routes/stop_words.rs +++ b/meilisearch-http/src/routes/stop_words.rs @@ -1,25 +1,31 @@ use std::collections::BTreeSet; -use meilisearch_core::settings::{SettingsUpdate, UpdateState}; -use actix_web::{web, get, post, delete, HttpResponse}; use actix_web as aweb; +use actix_web::{delete, get, post, web, HttpResponse}; +use meilisearch_core::settings::{SettingsUpdate, UpdateState}; -use crate::error::{ResponseError}; +use crate::error::ResponseError; +use crate::routes::{IndexParam, IndexUpdateResponse}; use crate::Data; -use crate::routes::{IndexUpdateResponse, IndexParam}; #[get("/indexes/{index_uid}/settings/stop-words")] -pub async fn get( - data: web::Data, - path: web::Path, -) -> aweb::Result { - let index = data.db.open_index(&path.index_uid) +pub async fn get(data: web::Data, path: web::Path) -> aweb::Result { + let index = data + .db + .open_index(&path.index_uid) .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; - let reader = data.db.main_read_txn() + let reader = data + .db + .main_read_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; - let stop_words_fst = index.main.stop_words_fst(&reader) + let stop_words_fst = index + .main + .stop_words_fst(&reader) .map_err(|err| ResponseError::Internal(err.to_string()))?; - let stop_words = stop_words_fst.unwrap_or_default().stream().into_strs() + let stop_words = stop_words_fst + .unwrap_or_default() + .stream() + .into_strs() .map_err(|err| ResponseError::Internal(err.to_string()))?; Ok(HttpResponse::Ok().json(stop_words)) @@ -31,7 +37,9 @@ pub async fn update( path: web::Path, body: web::Json>, ) -> aweb::Result { - let index = data.db.open_index(&path.index_uid) + let index = data + .db + .open_index(&path.index_uid) .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; let settings = SettingsUpdate { @@ -39,11 +47,15 @@ pub async fn update( ..SettingsUpdate::default() }; - let mut writer = data.db.update_write_txn() + let mut writer = data + .db + .update_write_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; - let update_id = index.settings_update(&mut writer, settings) + let update_id = index + .settings_update(&mut writer, settings) .map_err(|err| ResponseError::Internal(err.to_string()))?; - writer.commit() + writer + .commit() .map_err(|err| ResponseError::Internal(err.to_string()))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) @@ -54,7 +66,9 @@ pub async fn delete( data: web::Data, path: web::Path, ) -> aweb::Result { - let index = data.db.open_index(&path.index_uid) + let index = data + .db + .open_index(&path.index_uid) .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; let settings = SettingsUpdate { @@ -62,11 +76,15 @@ pub async fn delete( ..SettingsUpdate::default() }; - let mut writer = data.db.update_write_txn() + let mut writer = data + .db + .update_write_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; - let update_id = index.settings_update(&mut writer, settings) + let update_id = index + .settings_update(&mut writer, settings) .map_err(|err| ResponseError::Internal(err.to_string()))?; - writer.commit() + writer + .commit() .map_err(|err| ResponseError::Internal(err.to_string()))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) diff --git a/meilisearch-http/src/routes/synonym.rs b/meilisearch-http/src/routes/synonym.rs index 587582ee9..7892988a6 100644 --- a/meilisearch-http/src/routes/synonym.rs +++ b/meilisearch-http/src/routes/synonym.rs @@ -1,29 +1,34 @@ use std::collections::BTreeMap; +use actix_web as aweb; +use actix_web::{delete, get, post, web, HttpResponse}; use indexmap::IndexMap; use meilisearch_core::settings::{SettingsUpdate, UpdateState}; -use actix_web::{web, get, post, delete, HttpResponse}; -use actix_web as aweb; -use crate::error::{ResponseError}; +use crate::error::ResponseError; +use crate::routes::{IndexParam, IndexUpdateResponse}; use crate::Data; -use crate::routes::{IndexUpdateResponse, IndexParam}; #[get("/indexes/{index_uid}/settings/synonyms")] -pub async fn get( - data: web::Data, - path: web::Path, -) -> aweb::Result { - let index = data.db.open_index(&path.index_uid) +pub async fn get(data: web::Data, path: web::Path) -> aweb::Result { + let index = data + .db + .open_index(&path.index_uid) .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; - let reader = data.db.main_read_txn() + let reader = data + .db + .main_read_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; - let synonyms_fst = index.main.synonyms_fst(&reader) + let synonyms_fst = index + .main + .synonyms_fst(&reader) .map_err(|err| ResponseError::Internal(err.to_string()))? .unwrap_or_default(); - let synonyms_list = synonyms_fst.stream().into_strs() + let synonyms_list = synonyms_fst + .stream() + .into_strs() .map_err(|err| ResponseError::Internal(err.to_string()))?; let mut synonyms = IndexMap::new(); @@ -31,11 +36,14 @@ pub async fn get( let index_synonyms = &index.synonyms; for synonym in synonyms_list { - let alternative_list = index_synonyms.synonyms(&reader, synonym.as_bytes()) + let alternative_list = index_synonyms + .synonyms(&reader, synonym.as_bytes()) .map_err(|err| ResponseError::Internal(err.to_string()))?; if let Some(list) = alternative_list { - let list = list.stream().into_strs() + let list = list + .stream() + .into_strs() .map_err(|err| ResponseError::Internal(err.to_string()))?; synonyms.insert(synonym, list); } @@ -50,7 +58,9 @@ pub async fn update( path: web::Path, body: web::Json>>, ) -> aweb::Result { - let index = data.db.open_index(&path.index_uid) + let index = data + .db + .open_index(&path.index_uid) .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; let settings = SettingsUpdate { @@ -58,11 +68,15 @@ pub async fn update( ..SettingsUpdate::default() }; - let mut writer = data.db.update_write_txn() + let mut writer = data + .db + .update_write_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; - let update_id = index.settings_update(&mut writer, settings) + let update_id = index + .settings_update(&mut writer, settings) .map_err(|err| ResponseError::Internal(err.to_string()))?; - writer.commit() + writer + .commit() .map_err(|err| ResponseError::Internal(err.to_string()))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) @@ -73,7 +87,9 @@ pub async fn delete( data: web::Data, path: web::Path, ) -> aweb::Result { - let index = data.db.open_index(&path.index_uid) + let index = data + .db + .open_index(&path.index_uid) .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; let settings = SettingsUpdate { @@ -81,12 +97,16 @@ pub async fn delete( ..SettingsUpdate::default() }; - let mut writer = data.db.update_write_txn() + let mut writer = data + .db + .update_write_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; - let update_id = index.settings_update(&mut writer, settings) + let update_id = index + .settings_update(&mut writer, settings) .map_err(|err| ResponseError::Internal(err.to_string()))?; - writer.commit() + writer + .commit() .map_err(|err| ResponseError::Internal(err.to_string()))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) diff --git a/meilisearch-http/tests/documents_add.rs b/meilisearch-http/tests/documents_add.rs index a223c4757..ebb66e7a4 100644 --- a/meilisearch-http/tests/documents_add.rs +++ b/meilisearch-http/tests/documents_add.rs @@ -91,11 +91,11 @@ fn check_add_documents_with_nested_null() { // 2 - Add a document that contains a null in a nested object - let body = json!([{ - "id": 0, - "foo": { + let body = json!([{ + "id": 0, + "foo": { "bar": null - } + } }]); let url = "/indexes/tasks/documents"; @@ -126,9 +126,9 @@ fn check_add_documents_with_nested_sequence() { // 2 - Add a document that contains a seq in a nested object - let body = json!([{ - "id": 0, - "foo": { + let body = json!([{ + "id": 0, + "foo": { "bar": [123,456], "fez": [{ "id": 255, diff --git a/meilisearch-http/tests/index.rs b/meilisearch-http/tests/index.rs index e0b44b9b5..f825e336b 100644 --- a/meilisearch-http/tests/index.rs +++ b/meilisearch-http/tests/index.rs @@ -660,7 +660,7 @@ fn check_add_documents_without_primary_key() { } #[test] -fn check_first_update_should_bring_up_processed_status_after_first_docs_addition(){ +fn check_first_update_should_bring_up_processed_status_after_first_docs_addition() { let mut server = common::Server::with_uid("movies"); let body = json!({ @@ -678,11 +678,11 @@ fn check_first_update_should_bring_up_processed_status_after_first_docs_addition // 2. Index the documents from movies.json, present inside of assets directory server.add_or_replace_multiple_documents(body); - + // 3. Fetch the status of the indexing done above. let (response, status_code) = server.get_all_updates_status(); - + // 4. Verify the fetch is successful and indexing status is 'processed' assert_eq!(status_code, 200); - assert_eq!(response[0]["status"], "processed"); + assert_eq!(response[0]["status"], "processed"); } diff --git a/meilisearch-http/tests/settings_accept_new_fields.rs b/meilisearch-http/tests/settings_accept_new_fields.rs index 807aa1cd2..7293badd4 100644 --- a/meilisearch-http/tests/settings_accept_new_fields.rs +++ b/meilisearch-http/tests/settings_accept_new_fields.rs @@ -289,10 +289,9 @@ fn index_new_fields_false_then_true() { assert_json_eq!(response, expected); } - // Fix issue https://github.com/meilisearch/MeiliSearch/issues/518 #[test] -fn accept_new_fields_does_not_take_into_account_the_primary_key () { +fn accept_new_fields_does_not_take_into_account_the_primary_key() { let mut server = common::Server::with_uid("movies"); // 1 - Create an index with no primary-key diff --git a/meilisearch-http/tests/settings_ranking_rules.rs b/meilisearch-http/tests/settings_ranking_rules.rs index f203216ff..9c5a82f59 100644 --- a/meilisearch-http/tests/settings_ranking_rules.rs +++ b/meilisearch-http/tests/settings_ranking_rules.rs @@ -148,10 +148,7 @@ fn write_custom_ranking_and_index_documents() { // 1 - Add ranking rules with one custom ranking on a string - let body = json!([ - "asc(title)", - "typo" - ]); + let body = json!(["asc(title)", "typo"]); server.update_ranking_rules(body); @@ -184,5 +181,4 @@ fn write_custom_ranking_and_index_documents() { assert_eq!(status_code, 200); assert_json_eq!(response, expected, ordered: false); - } From 5fed155f1564a89ecbc996c8b4ec7a2af667cb81 Mon Sep 17 00:00:00 2001 From: Quentin de Quelen Date: Fri, 10 Apr 2020 19:40:43 +0200 Subject: [PATCH 11/21] add middleware --- Cargo.lock | 13 +++++++++++++ meilisearch-http/Cargo.toml | 1 + meilisearch-http/src/main.rs | 13 ++++++++++--- 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b224ef1e5..fe2b67b7d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -34,6 +34,18 @@ dependencies = [ "trust-dns-resolver", ] +[[package]] +name = "actix-cors" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6206917d5c0fdd79d81cec9ef02d3e802df4abf276d96241e1f595d971e002" +dependencies = [ + "actix-service", + "actix-web", + "derive_more", + "futures", +] + [[package]] name = "actix-files" version = "0.2.1" @@ -1489,6 +1501,7 @@ dependencies = [ name = "meilisearch-http" version = "0.10.1" dependencies = [ + "actix-cors", "actix-files", "actix-http", "actix-rt", diff --git a/meilisearch-http/Cargo.toml b/meilisearch-http/Cargo.toml index cd506c092..ce0bf934b 100644 --- a/meilisearch-http/Cargo.toml +++ b/meilisearch-http/Cargo.toml @@ -45,6 +45,7 @@ actix-rt = "1" actix-web = "2" actix-http = "1" actix-files = "0.2.1" +actix-cors = "0.2.0" tokio = { version = "0.2.0", features = ["macros"] } [dev-dependencies] diff --git a/meilisearch-http/src/main.rs b/meilisearch-http/src/main.rs index e0e19448b..0e43e15f1 100644 --- a/meilisearch-http/src/main.rs +++ b/meilisearch-http/src/main.rs @@ -1,7 +1,7 @@ use std::{env, thread}; -use actix_web::middleware::Logger; -use actix_web::{web, App, HttpServer}; +use actix_cors::Cors; +use actix_web::{web, App, HttpServer, middleware}; use log::info; use main_error::MainError; use meilisearch_http::data::Data; @@ -52,7 +52,14 @@ async fn main() -> Result<(), MainError> { HttpServer::new(move || { App::new() - .wrap(Logger::default()) + .wrap( + Cors::new() + .send_wildcard() + .allowed_header("x-meili-api-key") + .finish() + ) + .wrap(middleware::Logger::default()) + .wrap(middleware::Compress::default()) .app_data(web::Data::new(data.clone())) .service(routes::load_html) .service(routes::load_css) From 38d41252e62ff24df65507749683e70910100cfe Mon Sep 17 00:00:00 2001 From: Quentin de Quelen Date: Tue, 14 Apr 2020 18:00:35 +0200 Subject: [PATCH 12/21] add authentication middleware --- Cargo.lock | 1 + meilisearch-http/Cargo.toml | 1 + meilisearch-http/src/error.rs | 5 +- meilisearch-http/src/main.rs | 21 ++-- meilisearch-http/src/routes/mod.rs | 183 ++++++++++++++--------------- 5 files changed, 105 insertions(+), 106 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fe2b67b7d..9b2f212af 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1505,6 +1505,7 @@ dependencies = [ "actix-files", "actix-http", "actix-rt", + "actix-service", "actix-web", "assert-json-diff", "chrono", diff --git a/meilisearch-http/Cargo.toml b/meilisearch-http/Cargo.toml index ce0bf934b..7d3ae7695 100644 --- a/meilisearch-http/Cargo.toml +++ b/meilisearch-http/Cargo.toml @@ -46,6 +46,7 @@ actix-web = "2" actix-http = "1" actix-files = "0.2.1" actix-cors = "0.2.0" +actix-service = "1.0.5" tokio = { version = "0.2.0", features = ["macros"] } [dev-dependencies] diff --git a/meilisearch-http/src/error.rs b/meilisearch-http/src/error.rs index 6c3210e1b..469140f55 100644 --- a/meilisearch-http/src/error.rs +++ b/meilisearch-http/src/error.rs @@ -9,6 +9,7 @@ use serde_json::json; pub enum ResponseError { Internal(String), BadRequest(String), + MissingAuthorizationHeader, InvalidToken(String), NotFound(String), IndexNotFound(String), @@ -27,6 +28,7 @@ impl fmt::Display for ResponseError { match self { Self::Internal(err) => write!(f, "Internal server error: {}", err), Self::BadRequest(err) => write!(f, "Bad request: {}", err), + Self::MissingAuthorizationHeader => write!(f, "You must have an authorization token"), Self::InvalidToken(err) => write!(f, "Invalid API key: {}", err), Self::NotFound(err) => write!(f, "{} not found", err), Self::IndexNotFound(index_uid) => write!(f, "Index {} not found", index_uid), @@ -53,7 +55,8 @@ impl aweb::error::ResponseError for ResponseError { match *self { Self::Internal(_) => StatusCode::INTERNAL_SERVER_ERROR, Self::BadRequest(_) => StatusCode::BAD_REQUEST, - Self::InvalidToken(_) => StatusCode::FORBIDDEN, + Self::MissingAuthorizationHeader => StatusCode::FORBIDDEN, + Self::InvalidToken(_) => StatusCode::UNAUTHORIZED, Self::NotFound(_) => StatusCode::NOT_FOUND, Self::IndexNotFound(_) => StatusCode::NOT_FOUND, Self::DocumentNotFound(_) => StatusCode::NOT_FOUND, diff --git a/meilisearch-http/src/main.rs b/meilisearch-http/src/main.rs index 0e43e15f1..6ea356b63 100644 --- a/meilisearch-http/src/main.rs +++ b/meilisearch-http/src/main.rs @@ -1,7 +1,7 @@ use std::{env, thread}; use actix_cors::Cors; -use actix_web::{web, App, HttpServer, middleware}; +use actix_web::{middleware, web, App, HttpServer}; use log::info; use main_error::MainError; use meilisearch_http::data::Data; @@ -54,15 +54,21 @@ async fn main() -> Result<(), MainError> { App::new() .wrap( Cors::new() - .send_wildcard() - .allowed_header("x-meili-api-key") - .finish() + .send_wildcard() + .allowed_header("x-meili-api-key") + .finish(), ) .wrap(middleware::Logger::default()) .wrap(middleware::Compress::default()) .app_data(web::Data::new(data.clone())) + .wrap(routes::Authentication::Public) .service(routes::load_html) .service(routes::load_css) + .service(routes::search::search_with_url_query) + .service(routes::search::search_multi_index) + .service(routes::document::get_document) + .service(routes::document::get_all_documents) + .wrap(routes::Authentication::Private) .service(routes::index::list_indexes) .service(routes::index::get_index) .service(routes::index::create_index) @@ -70,11 +76,7 @@ async fn main() -> Result<(), MainError> { .service(routes::index::delete_index) .service(routes::index::get_update_status) .service(routes::index::get_all_updates_status) - .service(routes::search::search_with_url_query) - .service(routes::search::search_multi_index) - .service(routes::document::get_document) .service(routes::document::delete_document) - .service(routes::document::get_all_documents) .service(routes::document::add_documents) .service(routes::document::update_documents) .service(routes::document::delete_documents) @@ -102,7 +104,6 @@ async fn main() -> Result<(), MainError> { .service(routes::synonym::get) .service(routes::synonym::update) .service(routes::synonym::delete) - .service(routes::key::list) .service(routes::stats::index_stats) .service(routes::stats::get_stats) .service(routes::stats::get_version) @@ -110,6 +111,8 @@ async fn main() -> Result<(), MainError> { .service(routes::stats::get_sys_info_pretty) .service(routes::health::get_health) .service(routes::health::change_healthyness) + .wrap(routes::Authentication::Admin) + .service(routes::key::list) }) .bind(opt.http_addr)? .run() diff --git a/meilisearch-http/src/routes/mod.rs b/meilisearch-http/src/routes/mod.rs index 66cabd52d..5d92e185f 100644 --- a/meilisearch-http/src/routes/mod.rs +++ b/meilisearch-http/src/routes/mod.rs @@ -1,8 +1,17 @@ +use std::cell::RefCell; +use std::pin::Pin; +use std::rc::Rc; +use std::task::{Context, Poll}; + +use actix_service::{Service, Transform}; +use actix_web::{dev::ServiceRequest, dev::ServiceResponse, Error}; use actix_web::{get, HttpResponse}; +use futures::future::{err, ok, Future, Ready}; use log::error; use meilisearch_core::ProcessedUpdateResult; use serde::{Deserialize, Serialize}; +use crate::error::ResponseError; use crate::Data; pub mod document; @@ -79,109 +88,91 @@ pub fn index_update_callback(index_uid: &str, data: &Data, status: ProcessedUpda } } -// pub fn load_routes(app: &mut tide::Server) { -// app.at("/").get(|_| async { -// tide::Response::new(200) -// .body_string() -// .set_mime(mime::TEXT_HTML_UTF_8) -// }); -// app.at("/bulma.min.css").get(|_| async { -// tide::Response::new(200) -// .body_string(include_str!("../../public/bulma.min.css").to_string()) -// .set_mime(mime::TEXT_CSS_UTF_8) -// }); +#[derive(Clone)] +pub enum Authentication { + Public, + Private, + Admin, +} -// app.at("/indexes") -// .get(|ctx| into_response(index::list_indexes(ctx))) -// .post(|ctx| into_response(index::create_index(ctx))); +impl Transform for Authentication +where + S: Service, Error = Error>, + S::Future: 'static, + B: 'static, +{ + type Request = ServiceRequest; + type Response = ServiceResponse; + type Error = Error; + type InitError = (); + type Transform = LoggingMiddleware; + type Future = Ready>; -// app.at("/indexes/search") -// .post(|ctx| into_response(search::search_multi_index(ctx))); + fn new_transform(&self, service: S) -> Self::Future { + ok(LoggingMiddleware { + acl: (*self).clone(), + service: Rc::new(RefCell::new(service)), + }) + } +} -// app.at("/indexes/:index") -// .get(|ctx| into_response(index::get_index(ctx))) -// .put(|ctx| into_response(index::update_index(ctx))) -// .delete(|ctx| into_response(index::delete_index(ctx))); +pub struct LoggingMiddleware { + acl: Authentication, + service: Rc>, +} -// app.at("/indexes/:index/search") -// .get(|ctx| into_response(search::search_with_url_query(ctx))); +impl Service for LoggingMiddleware +where + S: Service, Error = Error> + 'static, + S::Future: 'static, + B: 'static, +{ + type Request = ServiceRequest; + type Response = ServiceResponse; + type Error = Error; + type Future = Pin>>>; -// app.at("/indexes/:index/updates") -// .get(|ctx| into_response(index::get_all_updates_status(ctx))); + fn poll_ready(&mut self, cx: &mut Context) -> Poll> { + self.service.poll_ready(cx) + } -// app.at("/indexes/:index/updates/:update_id") -// .get(|ctx| into_response(index::get_update_status(ctx))); + fn call(&mut self, req: ServiceRequest) -> Self::Future { + let mut svc = self.service.clone(); + let data = req.app_data::().unwrap(); -// app.at("/indexes/:index/documents") -// .get(|ctx| into_response(document::get_all_documents(ctx))) -// .post(|ctx| into_response(document::add_or_replace_multiple_documents(ctx))) -// .put(|ctx| into_response(document::add_or_update_multiple_documents(ctx))) -// .delete(|ctx| into_response(document::clear_all_documents(ctx))); + if data.api_keys.master.is_none() { + return Box::pin(svc.call(req)); + } -// app.at("/indexes/:index/documents/:document_id") -// .get(|ctx| into_response(document::get_document(ctx))) -// .delete(|ctx| into_response(document::delete_document(ctx))); + let auth_header = match req.headers().get("X-Meili-API-Key") { + Some(auth) => match auth.to_str() { + Ok(auth) => auth, + Err(_) => return Box::pin(err(ResponseError::MissingAuthorizationHeader.into())), + }, + None => { + return Box::pin(err(ResponseError::MissingAuthorizationHeader.into())); + } + }; -// app.at("/indexes/:index/documents/delete-batch") -// .post(|ctx| into_response(document::delete_multiple_documents(ctx))); + let authenticated = match self.acl { + Authentication::Admin => data.api_keys.master.as_deref() == Some(auth_header), + Authentication::Private => { + data.api_keys.master.as_deref() == Some(auth_header) + || data.api_keys.private.as_deref() == Some(auth_header) + } + Authentication::Public => { + data.api_keys.master.as_deref() == Some(auth_header) + || data.api_keys.private.as_deref() == Some(auth_header) + || data.api_keys.public.as_deref() == Some(auth_header) + } + }; -// app.at("/indexes/:index/settings") -// .get(|ctx| into_response(setting::get_all(ctx))) -// .post(|ctx| into_response(setting::update_all(ctx))) -// .delete(|ctx| into_response(setting::delete_all(ctx))); - -// app.at("/indexes/:index/settings/ranking-rules") -// .get(|ctx| into_response(setting::get_rules(ctx))) -// .post(|ctx| into_response(setting::update_rules(ctx))) -// .delete(|ctx| into_response(setting::delete_rules(ctx))); - -// app.at("/indexes/:index/settings/distinct-attribute") -// .get(|ctx| into_response(setting::get_distinct(ctx))) -// .post(|ctx| into_response(setting::update_distinct(ctx))) -// .delete(|ctx| into_response(setting::delete_distinct(ctx))); - -// app.at("/indexes/:index/settings/searchable-attributes") -// .get(|ctx| into_response(setting::get_searchable(ctx))) -// .post(|ctx| into_response(setting::update_searchable(ctx))) -// .delete(|ctx| into_response(setting::delete_searchable(ctx))); - -// app.at("/indexes/:index/settings/displayed-attributes") -// .get(|ctx| into_response(setting::displayed(ctx))) -// .post(|ctx| into_response(setting::update_displayed(ctx))) -// .delete(|ctx| into_response(setting::delete_displayed(ctx))); - -// app.at("/indexes/:index/settings/accept-new-fields") -// .get(|ctx| into_response(setting::get_accept_new_fields(ctx))) -// .post(|ctx| into_response(setting::update_accept_new_fields(ctx))); - -// app.at("/indexes/:index/settings/synonyms") -// .get(|ctx| into_response(synonym::get(ctx))) -// .post(|ctx| into_response(synonym::update(ctx))) -// .delete(|ctx| into_response(synonym::delete(ctx))); - -// app.at("/indexes/:index/settings/stop-words") -// .get(|ctx| into_response(stop_words::get(ctx))) -// .post(|ctx| into_response(stop_words::update(ctx))) -// .delete(|ctx| into_response(stop_words::delete(ctx))); - -// app.at("/indexes/:index/stats") -// .get(|ctx| into_response(stats::index_stats(ctx))); - -// app.at("/keys").get(|ctx| into_response(key::list(ctx))); - -// app.at("/health") -// .get(|ctx| into_response(health::get_health(ctx))) -// .put(|ctx| into_response(health::change_healthyness(ctx))); - -// app.at("/stats") -// .get(|ctx| into_response(stats::get_stats(ctx))); - -// app.at("/version") -// .get(|ctx| into_response(stats::get_version(ctx))); - -// app.at("/sys-info") -// .get(|ctx| into_response(stats::get_sys_info(ctx))); - -// app.at("/sys-info/pretty") -// .get(|ctx| into_response(stats::get_sys_info_pretty(ctx))); -// } + if authenticated { + Box::pin(svc.call(req)) + } else { + Box::pin(err( + ResponseError::InvalidToken(auth_header.to_string()).into() + )) + } + } +} From 5e2861ff55428d447c7f95155cd6e5c20ae554c1 Mon Sep 17 00:00:00 2001 From: Quentin de Quelen Date: Wed, 15 Apr 2020 10:51:15 +0200 Subject: [PATCH 13/21] prepare architecture for tests --- meilisearch-http/src/data.rs | 2 +- .../src/helpers/authentication.rs | 100 +++++++++++++ meilisearch-http/src/helpers/mod.rs | 4 +- meilisearch-http/src/helpers/tide.rs | 83 ----------- meilisearch-http/src/lib.rs | 106 ++++++++++++++ meilisearch-http/src/main.rs | 60 +------- meilisearch-http/src/routes/mod.rs | 135 ------------------ 7 files changed, 213 insertions(+), 277 deletions(-) create mode 100644 meilisearch-http/src/helpers/authentication.rs delete mode 100644 meilisearch-http/src/helpers/tide.rs diff --git a/meilisearch-http/src/data.rs b/meilisearch-http/src/data.rs index 23f7efb69..97a3526a7 100644 --- a/meilisearch-http/src/data.rs +++ b/meilisearch-http/src/data.rs @@ -9,8 +9,8 @@ use meilisearch_core::{Database, Error as MError, MResult, MainT, UpdateT}; use sha2::Digest; use sysinfo::Pid; +use crate::index_update_callback; use crate::option::Opt; -use crate::routes::index_update_callback; const LAST_UPDATE_KEY: &str = "last-update"; diff --git a/meilisearch-http/src/helpers/authentication.rs b/meilisearch-http/src/helpers/authentication.rs new file mode 100644 index 000000000..de62e22f3 --- /dev/null +++ b/meilisearch-http/src/helpers/authentication.rs @@ -0,0 +1,100 @@ +use std::cell::RefCell; +use std::pin::Pin; +use std::rc::Rc; +use std::task::{Context, Poll}; + +use actix_service::{Service, Transform}; +use actix_web::{dev::ServiceRequest, dev::ServiceResponse, Error}; +use futures::future::{err, ok, Future, Ready}; + +use crate::error::ResponseError; +use crate::Data; + +#[derive(Clone)] +pub enum Authentication { + Public, + Private, + Admin, +} + +impl Transform for Authentication +where + S: Service, Error = Error>, + S::Future: 'static, + B: 'static, +{ + type Request = ServiceRequest; + type Response = ServiceResponse; + type Error = Error; + type InitError = (); + type Transform = LoggingMiddleware; + type Future = Ready>; + + fn new_transform(&self, service: S) -> Self::Future { + ok(LoggingMiddleware { + acl: (*self).clone(), + service: Rc::new(RefCell::new(service)), + }) + } +} + +pub struct LoggingMiddleware { + acl: Authentication, + service: Rc>, +} + +impl Service for LoggingMiddleware +where + S: Service, Error = Error> + 'static, + S::Future: 'static, + B: 'static, +{ + type Request = ServiceRequest; + type Response = ServiceResponse; + type Error = Error; + type Future = Pin>>>; + + fn poll_ready(&mut self, cx: &mut Context) -> Poll> { + self.service.poll_ready(cx) + } + + fn call(&mut self, req: ServiceRequest) -> Self::Future { + let mut svc = self.service.clone(); + let data = req.app_data::().unwrap(); + + if data.api_keys.master.is_none() { + return Box::pin(svc.call(req)); + } + + let auth_header = match req.headers().get("X-Meili-API-Key") { + Some(auth) => match auth.to_str() { + Ok(auth) => auth, + Err(_) => return Box::pin(err(ResponseError::MissingAuthorizationHeader.into())), + }, + None => { + return Box::pin(err(ResponseError::MissingAuthorizationHeader.into())); + } + }; + + let authenticated = match self.acl { + Authentication::Admin => data.api_keys.master.as_deref() == Some(auth_header), + Authentication::Private => { + data.api_keys.master.as_deref() == Some(auth_header) + || data.api_keys.private.as_deref() == Some(auth_header) + } + Authentication::Public => { + data.api_keys.master.as_deref() == Some(auth_header) + || data.api_keys.private.as_deref() == Some(auth_header) + || data.api_keys.public.as_deref() == Some(auth_header) + } + }; + + if authenticated { + Box::pin(svc.call(req)) + } else { + Box::pin(err( + ResponseError::InvalidToken(auth_header.to_string()).into() + )) + } + } +} diff --git a/meilisearch-http/src/helpers/mod.rs b/meilisearch-http/src/helpers/mod.rs index eb40f27e6..996333141 100644 --- a/meilisearch-http/src/helpers/mod.rs +++ b/meilisearch-http/src/helpers/mod.rs @@ -1,2 +1,4 @@ +pub mod authentication; pub mod meilisearch; -// pub mod tide; + +pub use authentication::Authentication; diff --git a/meilisearch-http/src/helpers/tide.rs b/meilisearch-http/src/helpers/tide.rs deleted file mode 100644 index f474372f0..000000000 --- a/meilisearch-http/src/helpers/tide.rs +++ /dev/null @@ -1,83 +0,0 @@ -use crate::error::{ResponseError, SResult}; -use crate::Data; -use meilisearch_core::Index; -use tide::Request; - -pub enum ACL { - Admin, - Private, - Public, -} - -pub trait RequestExt { - fn is_allowed(&self, acl: ACL) -> SResult<()>; - fn url_param(&self, name: &str) -> SResult; - fn index(&self) -> SResult; - fn document_id(&self) -> SResult; -} - -impl RequestExt for Request { - fn is_allowed(&self, acl: ACL) -> SResult<()> { - let user_api_key = self.header("X-Meili-API-Key"); - - if self.state().api_keys.master.is_none() { - return Ok(()) - } - - match acl { - ACL::Admin => { - if user_api_key == self.state().api_keys.master.as_deref() { - return Ok(()); - } - } - ACL::Private => { - if user_api_key == self.state().api_keys.master.as_deref() { - return Ok(()); - } - if user_api_key == self.state().api_keys.private.as_deref() { - return Ok(()); - } - } - ACL::Public => { - if user_api_key == self.state().api_keys.master.as_deref() { - return Ok(()); - } - if user_api_key == self.state().api_keys.private.as_deref() { - return Ok(()); - } - if user_api_key == self.state().api_keys.public.as_deref() { - return Ok(()); - } - } - } - - Err(ResponseError::InvalidToken( - user_api_key.unwrap_or("Need a token").to_owned(), - )) - } - - fn url_param(&self, name: &str) -> SResult { - let param = self - .param::(name) - .map_err(|e| ResponseError::bad_parameter(name, e))?; - Ok(param) - } - - fn index(&self) -> SResult { - let index_uid = self.url_param("index")?; - let index = self - .state() - .db - .open_index(&index_uid) - .ok_or(ResponseError::index_not_found(index_uid))?; - Ok(index) - } - - fn document_id(&self) -> SResult { - let name = self - .param::("document_id") - .map_err(|_| ResponseError::bad_parameter("documentId", "primaryKey"))?; - - Ok(name) - } -} diff --git a/meilisearch-http/src/lib.rs b/meilisearch-http/src/lib.rs index db7fe97c3..77201fd98 100644 --- a/meilisearch-http/src/lib.rs +++ b/meilisearch-http/src/lib.rs @@ -8,3 +8,109 @@ pub mod option; pub mod routes; pub use self::data::Data; +use actix_http::Error; +use actix_service::ServiceFactory; +use actix_web::{dev, web, App}; +use log::error; +use meilisearch_core::ProcessedUpdateResult; + +pub fn create_app( + data: &Data, +) -> App< + impl ServiceFactory< + Config = (), + Request = dev::ServiceRequest, + Response = dev::ServiceResponse, + Error = Error, + InitError = (), + >, + actix_http::body::Body, +> { + App::new() + .app_data(web::Data::new(data.clone())) + .wrap(helpers::Authentication::Public) + .service(routes::load_html) + .service(routes::load_css) + .service(routes::search::search_with_url_query) + .service(routes::search::search_multi_index) + .service(routes::document::get_document) + .service(routes::document::get_all_documents) + .wrap(helpers::Authentication::Private) + .service(routes::index::list_indexes) + .service(routes::index::get_index) + .service(routes::index::create_index) + .service(routes::index::update_index) + .service(routes::index::delete_index) + .service(routes::index::get_update_status) + .service(routes::index::get_all_updates_status) + .service(routes::document::delete_document) + .service(routes::document::add_documents) + .service(routes::document::update_documents) + .service(routes::document::delete_documents) + .service(routes::document::clear_all_documents) + .service(routes::setting::update_all) + .service(routes::setting::get_all) + .service(routes::setting::delete_all) + .service(routes::setting::get_rules) + .service(routes::setting::update_rules) + .service(routes::setting::delete_rules) + .service(routes::setting::get_distinct) + .service(routes::setting::update_distinct) + .service(routes::setting::delete_distinct) + .service(routes::setting::get_searchable) + .service(routes::setting::update_searchable) + .service(routes::setting::delete_searchable) + .service(routes::setting::get_displayed) + .service(routes::setting::update_displayed) + .service(routes::setting::delete_displayed) + .service(routes::setting::get_accept_new_fields) + .service(routes::setting::update_accept_new_fields) + .service(routes::stop_words::get) + .service(routes::stop_words::update) + .service(routes::stop_words::delete) + .service(routes::synonym::get) + .service(routes::synonym::update) + .service(routes::synonym::delete) + .service(routes::stats::index_stats) + .service(routes::stats::get_stats) + .service(routes::stats::get_version) + .service(routes::stats::get_sys_info) + .service(routes::stats::get_sys_info_pretty) + .service(routes::health::get_health) + .service(routes::health::change_healthyness) + .wrap(helpers::Authentication::Admin) + .service(routes::key::list) +} + +pub fn index_update_callback(index_uid: &str, data: &Data, status: ProcessedUpdateResult) { + if status.error.is_some() { + return; + } + + if let Some(index) = data.db.open_index(&index_uid) { + let db = &data.db; + let mut writer = match db.main_write_txn() { + Ok(writer) => writer, + Err(e) => { + error!("Impossible to get write_txn; {}", e); + return; + } + }; + + if let Err(e) = data.compute_stats(&mut writer, &index_uid) { + error!("Impossible to compute stats; {}", e) + } + + if let Err(e) = data.set_last_update(&mut writer) { + error!("Impossible to update last_update; {}", e) + } + + if let Err(e) = index.main.put_updated_at(&mut writer) { + error!("Impossible to update updated_at; {}", e) + } + + if let Err(e) = writer.commit() { + error!("Impossible to get write_txn; {}", e); + } + } +} diff --git a/meilisearch-http/src/main.rs b/meilisearch-http/src/main.rs index 6ea356b63..f802dab33 100644 --- a/meilisearch-http/src/main.rs +++ b/meilisearch-http/src/main.rs @@ -1,13 +1,12 @@ use std::{env, thread}; use actix_cors::Cors; -use actix_web::{middleware, web, App, HttpServer}; +use actix_web::{middleware, HttpServer}; use log::info; use main_error::MainError; use meilisearch_http::data::Data; use meilisearch_http::option::Opt; -use meilisearch_http::routes; -use meilisearch_http::routes::index_update_callback; +use meilisearch_http::{create_app, index_update_callback}; use structopt::StructOpt; mod analytics; @@ -51,7 +50,7 @@ async fn main() -> Result<(), MainError> { print_launch_resume(&opt, &data); HttpServer::new(move || { - App::new() + create_app(&data) .wrap( Cors::new() .send_wildcard() @@ -60,59 +59,6 @@ async fn main() -> Result<(), MainError> { ) .wrap(middleware::Logger::default()) .wrap(middleware::Compress::default()) - .app_data(web::Data::new(data.clone())) - .wrap(routes::Authentication::Public) - .service(routes::load_html) - .service(routes::load_css) - .service(routes::search::search_with_url_query) - .service(routes::search::search_multi_index) - .service(routes::document::get_document) - .service(routes::document::get_all_documents) - .wrap(routes::Authentication::Private) - .service(routes::index::list_indexes) - .service(routes::index::get_index) - .service(routes::index::create_index) - .service(routes::index::update_index) - .service(routes::index::delete_index) - .service(routes::index::get_update_status) - .service(routes::index::get_all_updates_status) - .service(routes::document::delete_document) - .service(routes::document::add_documents) - .service(routes::document::update_documents) - .service(routes::document::delete_documents) - .service(routes::document::clear_all_documents) - .service(routes::setting::update_all) - .service(routes::setting::get_all) - .service(routes::setting::delete_all) - .service(routes::setting::get_rules) - .service(routes::setting::update_rules) - .service(routes::setting::delete_rules) - .service(routes::setting::get_distinct) - .service(routes::setting::update_distinct) - .service(routes::setting::delete_distinct) - .service(routes::setting::get_searchable) - .service(routes::setting::update_searchable) - .service(routes::setting::delete_searchable) - .service(routes::setting::get_displayed) - .service(routes::setting::update_displayed) - .service(routes::setting::delete_displayed) - .service(routes::setting::get_accept_new_fields) - .service(routes::setting::update_accept_new_fields) - .service(routes::stop_words::get) - .service(routes::stop_words::update) - .service(routes::stop_words::delete) - .service(routes::synonym::get) - .service(routes::synonym::update) - .service(routes::synonym::delete) - .service(routes::stats::index_stats) - .service(routes::stats::get_stats) - .service(routes::stats::get_version) - .service(routes::stats::get_sys_info) - .service(routes::stats::get_sys_info_pretty) - .service(routes::health::get_health) - .service(routes::health::change_healthyness) - .wrap(routes::Authentication::Admin) - .service(routes::key::list) }) .bind(opt.http_addr)? .run() diff --git a/meilisearch-http/src/routes/mod.rs b/meilisearch-http/src/routes/mod.rs index 5d92e185f..33b71c684 100644 --- a/meilisearch-http/src/routes/mod.rs +++ b/meilisearch-http/src/routes/mod.rs @@ -1,19 +1,6 @@ -use std::cell::RefCell; -use std::pin::Pin; -use std::rc::Rc; -use std::task::{Context, Poll}; - -use actix_service::{Service, Transform}; -use actix_web::{dev::ServiceRequest, dev::ServiceResponse, Error}; use actix_web::{get, HttpResponse}; -use futures::future::{err, ok, Future, Ready}; -use log::error; -use meilisearch_core::ProcessedUpdateResult; use serde::{Deserialize, Serialize}; -use crate::error::ResponseError; -use crate::Data; - pub mod document; pub mod health; pub mod index; @@ -54,125 +41,3 @@ pub async fn load_css() -> HttpResponse { .content_type("text/css; charset=utf-8") .body(include_str!("../../public/bulma.min.css").to_string()) } - -pub fn index_update_callback(index_uid: &str, data: &Data, status: ProcessedUpdateResult) { - if status.error.is_some() { - return; - } - - if let Some(index) = data.db.open_index(&index_uid) { - let db = &data.db; - let mut writer = match db.main_write_txn() { - Ok(writer) => writer, - Err(e) => { - error!("Impossible to get write_txn; {}", e); - return; - } - }; - - if let Err(e) = data.compute_stats(&mut writer, &index_uid) { - error!("Impossible to compute stats; {}", e) - } - - if let Err(e) = data.set_last_update(&mut writer) { - error!("Impossible to update last_update; {}", e) - } - - if let Err(e) = index.main.put_updated_at(&mut writer) { - error!("Impossible to update updated_at; {}", e) - } - - if let Err(e) = writer.commit() { - error!("Impossible to get write_txn; {}", e); - } - } -} - -#[derive(Clone)] -pub enum Authentication { - Public, - Private, - Admin, -} - -impl Transform for Authentication -where - S: Service, Error = Error>, - S::Future: 'static, - B: 'static, -{ - type Request = ServiceRequest; - type Response = ServiceResponse; - type Error = Error; - type InitError = (); - type Transform = LoggingMiddleware; - type Future = Ready>; - - fn new_transform(&self, service: S) -> Self::Future { - ok(LoggingMiddleware { - acl: (*self).clone(), - service: Rc::new(RefCell::new(service)), - }) - } -} - -pub struct LoggingMiddleware { - acl: Authentication, - service: Rc>, -} - -impl Service for LoggingMiddleware -where - S: Service, Error = Error> + 'static, - S::Future: 'static, - B: 'static, -{ - type Request = ServiceRequest; - type Response = ServiceResponse; - type Error = Error; - type Future = Pin>>>; - - fn poll_ready(&mut self, cx: &mut Context) -> Poll> { - self.service.poll_ready(cx) - } - - fn call(&mut self, req: ServiceRequest) -> Self::Future { - let mut svc = self.service.clone(); - let data = req.app_data::().unwrap(); - - if data.api_keys.master.is_none() { - return Box::pin(svc.call(req)); - } - - let auth_header = match req.headers().get("X-Meili-API-Key") { - Some(auth) => match auth.to_str() { - Ok(auth) => auth, - Err(_) => return Box::pin(err(ResponseError::MissingAuthorizationHeader.into())), - }, - None => { - return Box::pin(err(ResponseError::MissingAuthorizationHeader.into())); - } - }; - - let authenticated = match self.acl { - Authentication::Admin => data.api_keys.master.as_deref() == Some(auth_header), - Authentication::Private => { - data.api_keys.master.as_deref() == Some(auth_header) - || data.api_keys.private.as_deref() == Some(auth_header) - } - Authentication::Public => { - data.api_keys.master.as_deref() == Some(auth_header) - || data.api_keys.private.as_deref() == Some(auth_header) - || data.api_keys.public.as_deref() == Some(auth_header) - } - }; - - if authenticated { - Box::pin(svc.call(req)) - } else { - Box::pin(err( - ResponseError::InvalidToken(auth_header.to_string()).into() - )) - } - } -} From 27b3b53bc5e4a25492829afd553f2f980712324b Mon Sep 17 00:00:00 2001 From: Quentin de Quelen Date: Thu, 16 Apr 2020 11:09:47 +0200 Subject: [PATCH 14/21] update tests & fix the broken code --- meilisearch-http/Cargo.toml | 2 +- meilisearch-http/src/error.rs | 4 +- meilisearch-http/src/lib.rs | 1 + meilisearch-http/src/routes/document.rs | 14 +- meilisearch-http/src/routes/index.rs | 33 +- meilisearch-http/src/routes/setting.rs | 58 ++-- meilisearch-http/tests/common.rs | 323 +++++++++--------- meilisearch-http/tests/documents_add.rs | 50 +-- meilisearch-http/tests/documents_delete.rs | 24 +- meilisearch-http/tests/health.rs | 14 +- meilisearch-http/tests/index.rs | 156 ++++----- meilisearch-http/tests/search.rs | 124 +++---- meilisearch-http/tests/search_settings.rs | 70 ++-- meilisearch-http/tests/settings.rs | 56 +-- .../tests/settings_accept_new_fields.rs | 96 +++--- .../tests/settings_ranking_rules.rs | 56 +-- meilisearch-http/tests/settings_stop_words.rs | 28 +- 17 files changed, 554 insertions(+), 555 deletions(-) diff --git a/meilisearch-http/Cargo.toml b/meilisearch-http/Cargo.toml index 7d3ae7695..bd6a9aa5e 100644 --- a/meilisearch-http/Cargo.toml +++ b/meilisearch-http/Cargo.toml @@ -47,7 +47,7 @@ actix-http = "1" actix-files = "0.2.1" actix-cors = "0.2.0" actix-service = "1.0.5" -tokio = { version = "0.2.0", features = ["macros"] } +tokio = { version = "0.2.18", features = ["macros", "time"] } [dev-dependencies] http-service = "0.4.0" diff --git a/meilisearch-http/src/error.rs b/meilisearch-http/src/error.rs index 469140f55..ef2a910d5 100644 --- a/meilisearch-http/src/error.rs +++ b/meilisearch-http/src/error.rs @@ -26,8 +26,8 @@ pub enum ResponseError { impl fmt::Display for ResponseError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::Internal(err) => write!(f, "Internal server error: {}", err), - Self::BadRequest(err) => write!(f, "Bad request: {}", err), + Self::Internal(err) => write!(f, "{}", err), + Self::BadRequest(err) => write!(f, "{}", err), Self::MissingAuthorizationHeader => write!(f, "You must have an authorization token"), Self::InvalidToken(err) => write!(f, "Invalid API key: {}", err), Self::NotFound(err) => write!(f, "{} not found", err), diff --git a/meilisearch-http/src/lib.rs b/meilisearch-http/src/lib.rs index 77201fd98..13a239a6c 100644 --- a/meilisearch-http/src/lib.rs +++ b/meilisearch-http/src/lib.rs @@ -28,6 +28,7 @@ pub fn create_app( > { App::new() .app_data(web::Data::new(data.clone())) + .app_data(web::JsonConfig::default().limit(1024 * 1024 * 10)) // Json Limit of 10Mb .wrap(helpers::Authentication::Public) .service(routes::load_html) .service(routes::load_css) diff --git a/meilisearch-http/src/routes/document.rs b/meilisearch-http/src/routes/document.rs index 763402fd2..c7dc20bc0 100644 --- a/meilisearch-http/src/routes/document.rs +++ b/meilisearch-http/src/routes/document.rs @@ -22,7 +22,7 @@ pub struct DocumentParam { pub async fn get_document( data: web::Data, path: web::Path, -) -> aweb::Result> { +) -> aweb::Result { let index = data .db .open_index(&path.index_uid) @@ -39,7 +39,7 @@ pub async fn get_document( .map_err(|_| ResponseError::DocumentNotFound(path.document_id.clone()))? .ok_or(ResponseError::DocumentNotFound(path.document_id.clone()))?; - Ok(web::Json(response)) + Ok(HttpResponse::Ok().json(response)) } #[delete("/indexes/{index_uid}/documents/{document_id}")] @@ -85,7 +85,7 @@ pub async fn get_all_documents( data: web::Data, path: web::Path, params: web::Query, -) -> aweb::Result>> { +) -> aweb::Result { let index = data .db .open_index(&path.index_uid) @@ -114,14 +114,14 @@ pub async fn get_all_documents( .clone() .map(|a| a.split(',').map(|a| a.to_string()).collect()); - let mut response_body = Vec::::new(); + let mut response = Vec::::new(); for document_id in documents_ids { if let Ok(Some(document)) = index.document(&reader, attributes.clone(), document_id) { - response_body.push(document); + response.push(document); } } - Ok(web::Json(response_body)) + Ok(HttpResponse::Ok().json(response)) } fn find_primary_key(document: &IndexMap) -> Option { @@ -168,7 +168,7 @@ async fn update_multiple_documents( let id = match params.primary_key.clone() { Some(id) => id, None => body.first().and_then(|docs| find_primary_key(docs)).ok_or( - ResponseError::BadRequest("Impossible to infer the primary key".to_string()), + ResponseError::BadRequest("Could not infer a primary key".to_string()), )?, }; diff --git a/meilisearch-http/src/routes/index.rs b/meilisearch-http/src/routes/index.rs index f1153ad74..8e6dac8ca 100644 --- a/meilisearch-http/src/routes/index.rs +++ b/meilisearch-http/src/routes/index.rs @@ -1,8 +1,7 @@ use actix_web as aweb; -use actix_web::{delete, get, post, web, HttpResponse}; +use actix_web::{delete, get, post, put, web, HttpResponse}; use chrono::{DateTime, Utc}; use log::error; -use meilisearch_core::UpdateStatus; use rand::seq::SliceRandom; use serde::{Deserialize, Serialize}; @@ -30,13 +29,13 @@ pub struct IndexResponse { } #[get("/indexes")] -pub async fn list_indexes(data: web::Data) -> aweb::Result>> { +pub async fn list_indexes(data: web::Data) -> aweb::Result { let reader = data .db .main_read_txn() .map_err(|err| ResponseError::Internal(err.to_string()))?; - let mut response_body = Vec::new(); + let mut response = Vec::new(); for index_uid in data.db.indexes_uids() { let index = data.db.open_index(&index_uid); @@ -80,7 +79,7 @@ pub async fn list_indexes(data: web::Data) -> aweb::Result error!( "Index {} is referenced in the indexes list but cannot be found", @@ -89,14 +88,14 @@ pub async fn list_indexes(data: web::Data) -> aweb::Result, path: web::Path, -) -> aweb::Result> { +) -> aweb::Result { let index = data .db .open_index(path.index_uid.clone()) @@ -137,7 +136,7 @@ pub async fn get_index( _ => None, }; - Ok(web::Json(IndexResponse { + Ok(HttpResponse::Ok().json(IndexResponse { name, uid: path.index_uid.clone(), created_at, @@ -158,7 +157,7 @@ pub struct IndexCreateRequest { pub async fn create_index( data: web::Data, body: web::Json, -) -> aweb::Result> { +) -> aweb::Result { if let (None, None) = (body.name.clone(), body.uid.clone()) { return Err( ResponseError::BadRequest("Index creation must have an uid".to_string()).into(), @@ -232,7 +231,7 @@ pub async fn create_index( .commit() .map_err(|err| ResponseError::Internal(err.to_string()))?; - Ok(web::Json(IndexResponse { + Ok(HttpResponse::Created().json(IndexResponse { name, uid, created_at, @@ -258,12 +257,12 @@ pub struct UpdateIndexResponse { primary_key: Option, } -#[post("/indexes/{index_uid}")] +#[put("/indexes/{index_uid}")] pub async fn update_index( data: web::Data, path: web::Path, body: web::Json, -) -> aweb::Result> { +) -> aweb::Result { let index = data .db .open_index(path.index_uid.clone()) @@ -350,7 +349,7 @@ pub async fn update_index( _ => None, }; - Ok(web::Json(IndexResponse { + Ok(HttpResponse::Ok().json(IndexResponse { name, uid: path.index_uid.clone(), created_at, @@ -381,7 +380,7 @@ pub struct UpdateParam { pub async fn get_update_status( data: web::Data, path: web::Path, -) -> aweb::Result> { +) -> aweb::Result { let index = data .db .open_index(path.index_uid.clone()) @@ -397,7 +396,7 @@ pub async fn get_update_status( .map_err(|e| ResponseError::Internal(e.to_string()))?; match status { - Some(status) => Ok(web::Json(status)), + Some(status) => Ok(HttpResponse::Ok().json(status)), None => Err(ResponseError::NotFound(format!("Update {} not found", path.update_id)).into()), } } @@ -406,7 +405,7 @@ pub async fn get_update_status( pub async fn get_all_updates_status( data: web::Data, path: web::Path, -) -> aweb::Result>> { +) -> aweb::Result { let index = data .db .open_index(path.index_uid.clone()) @@ -421,5 +420,5 @@ pub async fn get_all_updates_status( .all_updates_status(&reader) .map_err(|err| ResponseError::Internal(err.to_string()))?; - Ok(web::Json(response)) + Ok(HttpResponse::Ok().json(response)) } diff --git a/meilisearch-http/src/routes/setting.rs b/meilisearch-http/src/routes/setting.rs index 2883c3997..ca37ee1ac 100644 --- a/meilisearch-http/src/routes/setting.rs +++ b/meilisearch-http/src/routes/setting.rs @@ -11,6 +11,35 @@ use crate::Data; pub async fn update_all( data: web::Data, path: web::Path, + body: web::Json, +) -> aweb::Result { + let index = data + .db + .open_index(&path.index_uid) + .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; + + let mut writer = data + .db + .update_write_txn() + .map_err(|err| ResponseError::Internal(err.to_string()))?; + let settings = body + .into_inner() + .into_update() + .map_err(|e| ResponseError::BadRequest(e.to_string()))?; + let update_id = index + .settings_update(&mut writer, settings) + .map_err(|err| ResponseError::Internal(err.to_string()))?; + writer + .commit() + .map_err(|err| ResponseError::Internal(err.to_string()))?; + + Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) +} + +#[get("/indexes/{index_uid}/settings")] +pub async fn get_all( + data: web::Data, + path: web::Path, ) -> aweb::Result { let index = data .db @@ -106,35 +135,6 @@ pub async fn update_all( Ok(HttpResponse::Ok().json(settings)) } -#[get("/indexes/{index_uid}/settings")] -pub async fn get_all( - data: web::Data, - path: web::Path, - body: web::Json, -) -> aweb::Result { - let index = data - .db - .open_index(&path.index_uid) - .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; - - let mut writer = data - .db - .update_write_txn() - .map_err(|err| ResponseError::Internal(err.to_string()))?; - let settings = body - .into_inner() - .into_update() - .map_err(|e| ResponseError::BadRequest(e.to_string()))?; - let update_id = index - .settings_update(&mut writer, settings) - .map_err(|err| ResponseError::Internal(err.to_string()))?; - writer - .commit() - .map_err(|err| ResponseError::Internal(err.to_string()))?; - - Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) -} - #[delete("/indexes/{index_uid}/settings")] pub async fn delete_all( data: web::Data, diff --git a/meilisearch-http/tests/common.rs b/meilisearch-http/tests/common.rs index 42a2ce556..8c4e5d147 100644 --- a/meilisearch-http/tests/common.rs +++ b/meilisearch-http/tests/common.rs @@ -1,23 +1,17 @@ #![allow(dead_code)] -use http::StatusCode; -use serde_json::Value; +use serde_json::{json, Value}; use std::time::Duration; -use async_std::io::prelude::*; -use async_std::task::{block_on, sleep}; -use http_service::Body; -use http_service_mock::{make_server, TestBackend}; +use actix_web::{http::StatusCode, test}; use meilisearch_http::data::Data; use meilisearch_http::option::Opt; -use meilisearch_http::routes; -use serde_json::json; use tempdir::TempDir; -use tide::server::Service; +use tokio::time::delay_for; pub struct Server { uid: String, - mock: TestBackend>, + data: Data, } impl Server { @@ -33,20 +27,16 @@ impl Server { }; let data = Data::new(opt.clone()); - let mut app = tide::with_state(data); - routes::load_routes(&mut app); - let http_server = app.into_http_service(); - let mock = make_server(http_server).unwrap(); Server { uid: uid.to_string(), - mock, + data: data, } } - pub fn wait_update_id(&mut self, update_id: u64) { + pub async fn wait_update_id(&mut self, update_id: u64) { loop { - let (response, status_code) = self.get_update_status(update_id); + let (response, status_code) = self.get_update_status(update_id).await; assert_eq!(status_code, 200); if response["status"] == "processed" || response["status"] == "error" { @@ -54,350 +44,365 @@ impl Server { return; } - block_on(sleep(Duration::from_secs(1))); + delay_for(Duration::from_secs(1)).await; } } // Global Http request GET/POST/DELETE async or sync - pub fn get_request(&mut self, url: &str) -> (Value, StatusCode) { + pub async fn get_request(&mut self, url: &str) -> (Value, StatusCode) { eprintln!("get_request: {}", url); - let req = http::Request::get(url).body(Body::empty()).unwrap(); - let res = self.mock.simulate(req).unwrap(); + + let mut app = test::init_service(meilisearch_http::create_app(&self.data)).await; + + let req = test::TestRequest::get().uri(url).to_request(); + let res = test::call_service(&mut app, req).await; let status_code = res.status().clone(); - let mut buf = Vec::new(); - block_on(res.into_body().read_to_end(&mut buf)).unwrap(); - let response = serde_json::from_slice(&buf).unwrap_or_default(); + let body = test::read_body(res).await; + let response = serde_json::from_slice(&body).unwrap_or_default(); (response, status_code) } - pub fn post_request(&mut self, url: &str, body: Value) -> (Value, StatusCode) { + pub async fn post_request(&mut self, url: &str, body: Value) -> (Value, StatusCode) { eprintln!("post_request: {}", url); - let body_bytes = body.to_string().into_bytes(); - let req = http::Request::post(url) - .body(Body::from(body_bytes)) - .unwrap(); - let res = self.mock.simulate(req).unwrap(); + let mut app = test::init_service(meilisearch_http::create_app(&self.data)).await; + + let req = test::TestRequest::post() + .uri(url) + .set_json(&body) + .to_request(); + let res = test::call_service(&mut app, req).await; let status_code = res.status().clone(); - let mut buf = Vec::new(); - block_on(res.into_body().read_to_end(&mut buf)).unwrap(); - let response = serde_json::from_slice(&buf).unwrap_or_default(); + let body = test::read_body(res).await; + let response = serde_json::from_slice(&body).unwrap_or_default(); (response, status_code) } - pub fn post_request_async(&mut self, url: &str, body: Value) -> (Value, StatusCode) { + pub async fn post_request_async(&mut self, url: &str, body: Value) -> (Value, StatusCode) { eprintln!("post_request_async: {}", url); - let (response, status_code) = self.post_request(url, body); + + let (response, status_code) = self.post_request(url, body).await; assert_eq!(status_code, 202); assert!(response["updateId"].as_u64().is_some()); - self.wait_update_id(response["updateId"].as_u64().unwrap()); + self.wait_update_id(response["updateId"].as_u64().unwrap()) + .await; (response, status_code) } - pub fn put_request(&mut self, url: &str, body: Value) -> (Value, StatusCode) { + pub async fn put_request(&mut self, url: &str, body: Value) -> (Value, StatusCode) { eprintln!("put_request: {}", url); - let body_bytes = body.to_string().into_bytes(); - let req = http::Request::put(url) - .body(Body::from(body_bytes)) - .unwrap(); - let res = self.mock.simulate(req).unwrap(); + let mut app = test::init_service(meilisearch_http::create_app(&self.data)).await; + + let req = test::TestRequest::put() + .uri(url) + .set_json(&body) + .to_request(); + let res = test::call_service(&mut app, req).await; let status_code = res.status().clone(); - let mut buf = Vec::new(); - block_on(res.into_body().read_to_end(&mut buf)).unwrap(); - let response = serde_json::from_slice(&buf).unwrap_or_default(); + let body = test::read_body(res).await; + let response = serde_json::from_slice(&body).unwrap_or_default(); (response, status_code) } - pub fn put_request_async(&mut self, url: &str, body: Value) -> (Value, StatusCode) { + pub async fn put_request_async(&mut self, url: &str, body: Value) -> (Value, StatusCode) { eprintln!("put_request_async: {}", url); - let (response, status_code) = self.put_request(url, body); + + let (response, status_code) = self.put_request(url, body).await; assert!(response["updateId"].as_u64().is_some()); assert_eq!(status_code, 202); - self.wait_update_id(response["updateId"].as_u64().unwrap()); + self.wait_update_id(response["updateId"].as_u64().unwrap()) + .await; (response, status_code) } - pub fn delete_request(&mut self, url: &str) -> (Value, StatusCode) { + pub async fn delete_request(&mut self, url: &str) -> (Value, StatusCode) { eprintln!("delete_request: {}", url); - let req = http::Request::delete(url).body(Body::empty()).unwrap(); - let res = self.mock.simulate(req).unwrap(); + + let mut app = test::init_service(meilisearch_http::create_app(&self.data)).await; + + let req = test::TestRequest::delete().uri(url).to_request(); + let res = test::call_service(&mut app, req).await; let status_code = res.status().clone(); - let mut buf = Vec::new(); - block_on(res.into_body().read_to_end(&mut buf)).unwrap(); - let response = serde_json::from_slice(&buf).unwrap_or_default(); + let body = test::read_body(res).await; + let response = serde_json::from_slice(&body).unwrap_or_default(); (response, status_code) } - pub fn delete_request_async(&mut self, url: &str) -> (Value, StatusCode) { + pub async fn delete_request_async(&mut self, url: &str) -> (Value, StatusCode) { eprintln!("delete_request_async: {}", url); - let (response, status_code) = self.delete_request(url); + + let (response, status_code) = self.delete_request(url).await; assert!(response["updateId"].as_u64().is_some()); assert_eq!(status_code, 202); - self.wait_update_id(response["updateId"].as_u64().unwrap()); + self.wait_update_id(response["updateId"].as_u64().unwrap()) + .await; (response, status_code) } // All Routes - pub fn list_indexes(&mut self) -> (Value, StatusCode) { - self.get_request("/indexes") + pub async fn list_indexes(&mut self) -> (Value, StatusCode) { + self.get_request("/indexes").await } - pub fn create_index(&mut self, body: Value) -> (Value, StatusCode) { - self.post_request("/indexes", body) + pub async fn create_index(&mut self, body: Value) -> (Value, StatusCode) { + self.post_request("/indexes", body).await } - pub fn search_multi_index(&mut self, query: &str) -> (Value, StatusCode) { + pub async fn search_multi_index(&mut self, query: &str) -> (Value, StatusCode) { let url = format!("/indexes/search?{}", query); - self.get_request(&url) + self.get_request(&url).await } - pub fn get_index(&mut self) -> (Value, StatusCode) { + pub async fn get_index(&mut self) -> (Value, StatusCode) { let url = format!("/indexes/{}", self.uid); - self.get_request(&url) + self.get_request(&url).await } - pub fn update_index(&mut self, body: Value) -> (Value, StatusCode) { + pub async fn update_index(&mut self, body: Value) -> (Value, StatusCode) { let url = format!("/indexes/{}", self.uid); - self.put_request(&url, body) + self.put_request(&url, body).await } - pub fn delete_index(&mut self) -> (Value, StatusCode) { + pub async fn delete_index(&mut self) -> (Value, StatusCode) { let url = format!("/indexes/{}", self.uid); - self.delete_request(&url) + self.delete_request(&url).await } - pub fn search(&mut self, query: &str) -> (Value, StatusCode) { + pub async fn search(&mut self, query: &str) -> (Value, StatusCode) { let url = format!("/indexes/{}/search?{}", self.uid, query); - self.get_request(&url) + self.get_request(&url).await } - pub fn get_all_updates_status(&mut self) -> (Value, StatusCode) { + pub async fn get_all_updates_status(&mut self) -> (Value, StatusCode) { let url = format!("/indexes/{}/updates", self.uid); - self.get_request(&url) + self.get_request(&url).await } - pub fn get_update_status(&mut self, update_id: u64) -> (Value, StatusCode) { + pub async fn get_update_status(&mut self, update_id: u64) -> (Value, StatusCode) { let url = format!("/indexes/{}/updates/{}", self.uid, update_id); - self.get_request(&url) + self.get_request(&url).await } - pub fn get_all_documents(&mut self) -> (Value, StatusCode) { + pub async fn get_all_documents(&mut self) -> (Value, StatusCode) { let url = format!("/indexes/{}/documents", self.uid); - self.get_request(&url) + self.get_request(&url).await } - pub fn add_or_replace_multiple_documents(&mut self, body: Value) { + pub async fn add_or_replace_multiple_documents(&mut self, body: Value) { let url = format!("/indexes/{}/documents", self.uid); - self.post_request_async(&url, body); + self.post_request_async(&url, body).await; } - pub fn add_or_replace_multiple_documents_sync(&mut self, body: Value) -> (Value, StatusCode) { + pub async fn add_or_replace_multiple_documents_sync( + &mut self, + body: Value, + ) -> (Value, StatusCode) { let url = format!("/indexes/{}/documents", self.uid); - self.post_request(&url, body) + self.post_request(&url, body).await } - pub fn add_or_update_multiple_documents(&mut self, body: Value) { + pub async fn add_or_update_multiple_documents(&mut self, body: Value) { let url = format!("/indexes/{}/documents", self.uid); - self.put_request_async(&url, body); + self.put_request_async(&url, body).await; } - pub fn clear_all_documents(&mut self) { + pub async fn clear_all_documents(&mut self) { let url = format!("/indexes/{}/documents", self.uid); - self.delete_request_async(&url); + self.delete_request_async(&url).await; } - pub fn get_document(&mut self, document_id: impl ToString) -> (Value, StatusCode) { + pub async fn get_document(&mut self, document_id: impl ToString) -> (Value, StatusCode) { let url = format!( "/indexes/{}/documents/{}", self.uid, document_id.to_string() ); - self.get_request(&url) + self.get_request(&url).await } - pub fn delete_document(&mut self, document_id: impl ToString) -> (Value, StatusCode) { + pub async fn delete_document(&mut self, document_id: impl ToString) -> (Value, StatusCode) { let url = format!( "/indexes/{}/documents/{}", self.uid, document_id.to_string() ); - self.delete_request_async(&url) + self.delete_request_async(&url).await } - pub fn delete_multiple_documents(&mut self, body: Value) { + pub async fn delete_multiple_documents(&mut self, body: Value) { let url = format!("/indexes/{}/documents/delete-batch", self.uid); - self.post_request_async(&url, body); + self.post_request_async(&url, body).await; } - pub fn get_all_settings(&mut self) -> (Value, StatusCode) { + pub async fn get_all_settings(&mut self) -> (Value, StatusCode) { let url = format!("/indexes/{}/settings", self.uid); - self.get_request(&url) + self.get_request(&url).await } - pub fn update_all_settings(&mut self, body: Value) { + pub async fn update_all_settings(&mut self, body: Value) { let url = format!("/indexes/{}/settings", self.uid); - self.post_request_async(&url, body); + self.post_request_async(&url, body).await; } - pub fn delete_all_settings(&mut self) -> (Value, StatusCode) { + pub async fn delete_all_settings(&mut self) -> (Value, StatusCode) { let url = format!("/indexes/{}/settings", self.uid); - self.delete_request_async(&url) + self.delete_request_async(&url).await } - pub fn get_ranking_rules(&mut self) -> (Value, StatusCode) { + pub async fn get_ranking_rules(&mut self) -> (Value, StatusCode) { let url = format!("/indexes/{}/settings/ranking-rules", self.uid); - self.get_request(&url) + self.get_request(&url).await } - pub fn update_ranking_rules(&mut self, body: Value) { + pub async fn update_ranking_rules(&mut self, body: Value) { let url = format!("/indexes/{}/settings/ranking-rules", self.uid); - self.post_request_async(&url, body); + self.post_request_async(&url, body).await; } - pub fn update_ranking_rules_sync(&mut self, body: Value) -> (Value, StatusCode) { + pub async fn update_ranking_rules_sync(&mut self, body: Value) -> (Value, StatusCode) { let url = format!("/indexes/{}/settings/ranking-rules", self.uid); - self.post_request(&url, body) + self.post_request(&url, body).await } - pub fn delete_ranking_rules(&mut self) -> (Value, StatusCode) { + pub async fn delete_ranking_rules(&mut self) -> (Value, StatusCode) { let url = format!("/indexes/{}/settings/ranking-rules", self.uid); - self.delete_request_async(&url) + self.delete_request_async(&url).await } - pub fn get_distinct_attribute(&mut self) -> (Value, StatusCode) { + pub async fn get_distinct_attribute(&mut self) -> (Value, StatusCode) { let url = format!("/indexes/{}/settings/distinct-attribute", self.uid); - self.get_request(&url) + self.get_request(&url).await } - pub fn update_distinct_attribute(&mut self, body: Value) { + pub async fn update_distinct_attribute(&mut self, body: Value) { let url = format!("/indexes/{}/settings/distinct-attribute", self.uid); - self.post_request_async(&url, body); + self.post_request_async(&url, body).await; } - pub fn delete_distinct_attribute(&mut self) -> (Value, StatusCode) { + pub async fn delete_distinct_attribute(&mut self) -> (Value, StatusCode) { let url = format!("/indexes/{}/settings/distinct-attribute", self.uid); - self.delete_request_async(&url) + self.delete_request_async(&url).await } - pub fn get_primary_key(&mut self) -> (Value, StatusCode) { + pub async fn get_primary_key(&mut self) -> (Value, StatusCode) { let url = format!("/indexes/{}/settings/primary_key", self.uid); - self.get_request(&url) + self.get_request(&url).await } - pub fn get_searchable_attributes(&mut self) -> (Value, StatusCode) { + pub async fn get_searchable_attributes(&mut self) -> (Value, StatusCode) { let url = format!("/indexes/{}/settings/searchable-attributes", self.uid); - self.get_request(&url) + self.get_request(&url).await } - pub fn update_searchable_attributes(&mut self, body: Value) { + pub async fn update_searchable_attributes(&mut self, body: Value) { let url = format!("/indexes/{}/settings/searchable-attributes", self.uid); - self.post_request_async(&url, body); + self.post_request_async(&url, body).await; } - pub fn delete_searchable_attributes(&mut self) -> (Value, StatusCode) { + pub async fn delete_searchable_attributes(&mut self) -> (Value, StatusCode) { let url = format!("/indexes/{}/settings/searchable-attributes", self.uid); - self.delete_request_async(&url) + self.delete_request_async(&url).await } - pub fn get_displayed_attributes(&mut self) -> (Value, StatusCode) { + pub async fn get_displayed_attributes(&mut self) -> (Value, StatusCode) { let url = format!("/indexes/{}/settings/displayed-attributes", self.uid); - self.get_request(&url) + self.get_request(&url).await } - pub fn update_displayed_attributes(&mut self, body: Value) { + pub async fn update_displayed_attributes(&mut self, body: Value) { let url = format!("/indexes/{}/settings/displayed-attributes", self.uid); - self.post_request_async(&url, body); + self.post_request_async(&url, body).await; } - pub fn delete_displayed_attributes(&mut self) -> (Value, StatusCode) { + pub async fn delete_displayed_attributes(&mut self) -> (Value, StatusCode) { let url = format!("/indexes/{}/settings/displayed-attributes", self.uid); - self.delete_request_async(&url) + self.delete_request_async(&url).await } - pub fn get_accept_new_fields(&mut self) -> (Value, StatusCode) { + pub async fn get_accept_new_fields(&mut self) -> (Value, StatusCode) { let url = format!("/indexes/{}/settings/accept-new-fields", self.uid); - self.get_request(&url) + self.get_request(&url).await } - pub fn update_accept_new_fields(&mut self, body: Value) { + pub async fn update_accept_new_fields(&mut self, body: Value) { let url = format!("/indexes/{}/settings/accept-new-fields", self.uid); - self.post_request_async(&url, body); + self.post_request_async(&url, body).await; } - pub fn get_synonyms(&mut self) -> (Value, StatusCode) { + pub async fn get_synonyms(&mut self) -> (Value, StatusCode) { let url = format!("/indexes/{}/settings/synonyms", self.uid); - self.get_request(&url) + self.get_request(&url).await } - pub fn update_synonyms(&mut self, body: Value) { + pub async fn update_synonyms(&mut self, body: Value) { let url = format!("/indexes/{}/settings/synonyms", self.uid); - self.post_request_async(&url, body); + self.post_request_async(&url, body).await; } - pub fn delete_synonyms(&mut self) -> (Value, StatusCode) { + pub async fn delete_synonyms(&mut self) -> (Value, StatusCode) { let url = format!("/indexes/{}/settings/synonyms", self.uid); - self.delete_request_async(&url) + self.delete_request_async(&url).await } - pub fn get_stop_words(&mut self) -> (Value, StatusCode) { + pub async fn get_stop_words(&mut self) -> (Value, StatusCode) { let url = format!("/indexes/{}/settings/stop-words", self.uid); - self.get_request(&url) + self.get_request(&url).await } - pub fn update_stop_words(&mut self, body: Value) { + pub async fn update_stop_words(&mut self, body: Value) { let url = format!("/indexes/{}/settings/stop-words", self.uid); - self.post_request_async(&url, body); + self.post_request_async(&url, body).await; } - pub fn delete_stop_words(&mut self) -> (Value, StatusCode) { + pub async fn delete_stop_words(&mut self) -> (Value, StatusCode) { let url = format!("/indexes/{}/settings/stop-words", self.uid); - self.delete_request_async(&url) + self.delete_request_async(&url).await } - pub fn get_index_stats(&mut self) -> (Value, StatusCode) { + pub async fn get_index_stats(&mut self) -> (Value, StatusCode) { let url = format!("/indexes/{}/stats", self.uid); - self.get_request(&url) + self.get_request(&url).await } - pub fn list_keys(&mut self) -> (Value, StatusCode) { - self.get_request("/keys") + pub async fn list_keys(&mut self) -> (Value, StatusCode) { + self.get_request("/keys").await } - pub fn get_health(&mut self) -> (Value, StatusCode) { - self.get_request("/health") + pub async fn get_health(&mut self) -> (Value, StatusCode) { + self.get_request("/health").await } - pub fn update_health(&mut self, body: Value) -> (Value, StatusCode) { - self.put_request("/health", body) + pub async fn update_health(&mut self, body: Value) -> (Value, StatusCode) { + self.put_request("/health", body).await } - pub fn get_version(&mut self) -> (Value, StatusCode) { - self.get_request("/version") + pub async fn get_version(&mut self) -> (Value, StatusCode) { + self.get_request("/version").await } - pub fn get_sys_info(&mut self) -> (Value, StatusCode) { - self.get_request("/sys-info") + pub async fn get_sys_info(&mut self) -> (Value, StatusCode) { + self.get_request("/sys-info").await } - pub fn get_sys_info_pretty(&mut self) -> (Value, StatusCode) { - self.get_request("/sys-info/pretty") + pub async fn get_sys_info_pretty(&mut self) -> (Value, StatusCode) { + self.get_request("/sys-info/pretty").await } // Populate routes - pub fn populate_movies(&mut self) { + pub async fn populate_movies(&mut self) { let body = json!({ "uid": "movies", "primaryKey": "id", }); - self.create_index(body); + self.create_index(body).await; let body = json!({ "rankingRules": [ @@ -436,12 +441,12 @@ impl Server { "acceptNewFields": false, }); - self.update_all_settings(body); + self.update_all_settings(body).await; let dataset = include_bytes!("assets/movies.json"); let body: Value = serde_json::from_slice(dataset).unwrap(); - self.add_or_replace_multiple_documents(body); + self.add_or_replace_multiple_documents(body).await; } } diff --git a/meilisearch-http/tests/documents_add.rs b/meilisearch-http/tests/documents_add.rs index ebb66e7a4..760c116a8 100644 --- a/meilisearch-http/tests/documents_add.rs +++ b/meilisearch-http/tests/documents_add.rs @@ -3,8 +3,8 @@ use serde_json::json; mod common; // Test issue https://github.com/meilisearch/MeiliSearch/issues/519 -#[test] -fn check_add_documents_with_primary_key_param() { +#[actix_rt::test] +async fn check_add_documents_with_primary_key_param() { let mut server = common::Server::with_uid("movies"); // 1 - Create the index with no primary_key @@ -12,7 +12,7 @@ fn check_add_documents_with_primary_key_param() { let body = json!({ "uid": "movies", }); - let (response, status_code) = server.create_index(body); + let (response, status_code) = server.create_index(body).await; assert_eq!(status_code, 201); assert_eq!(response["primaryKey"], json!(null)); @@ -24,28 +24,28 @@ fn check_add_documents_with_primary_key_param() { }]); let url = "/indexes/movies/documents?primaryKey=title"; - let (response, status_code) = server.post_request(&url, body); + let (response, status_code) = server.post_request(&url, body).await; eprintln!("{:#?}", response); assert_eq!(status_code, 202); let update_id = response["updateId"].as_u64().unwrap(); - server.wait_update_id(update_id); + server.wait_update_id(update_id).await; // 3 - Check update success - let (response, status_code) = server.get_update_status(update_id); + let (response, status_code) = server.get_update_status(update_id).await; assert_eq!(status_code, 200); assert_eq!(response["status"], "processed"); } // Test issue https://github.com/meilisearch/MeiliSearch/issues/568 -#[test] -fn check_add_documents_with_nested_boolean() { +#[actix_rt::test] +async fn check_add_documents_with_nested_boolean() { let mut server = common::Server::with_uid("tasks"); // 1 - Create the index with no primary_key let body = json!({ "uid": "tasks" }); - let (response, status_code) = server.create_index(body); + let (response, status_code) = server.create_index(body).await; assert_eq!(status_code, 201); assert_eq!(response["primaryKey"], json!(null)); @@ -64,28 +64,28 @@ fn check_add_documents_with_nested_boolean() { }]); let url = "/indexes/tasks/documents"; - let (response, status_code) = server.post_request(&url, body); + let (response, status_code) = server.post_request(&url, body).await; eprintln!("{:#?}", response); assert_eq!(status_code, 202); let update_id = response["updateId"].as_u64().unwrap(); - server.wait_update_id(update_id); + server.wait_update_id(update_id).await; // 3 - Check update success - let (response, status_code) = server.get_update_status(update_id); + let (response, status_code) = server.get_update_status(update_id).await; assert_eq!(status_code, 200); assert_eq!(response["status"], "processed"); } // Test issue https://github.com/meilisearch/MeiliSearch/issues/571 -#[test] -fn check_add_documents_with_nested_null() { +#[actix_rt::test] +async fn check_add_documents_with_nested_null() { let mut server = common::Server::with_uid("tasks"); // 1 - Create the index with no primary_key let body = json!({ "uid": "tasks" }); - let (response, status_code) = server.create_index(body); + let (response, status_code) = server.create_index(body).await; assert_eq!(status_code, 201); assert_eq!(response["primaryKey"], json!(null)); @@ -99,28 +99,28 @@ fn check_add_documents_with_nested_null() { }]); let url = "/indexes/tasks/documents"; - let (response, status_code) = server.post_request(&url, body); + let (response, status_code) = server.post_request(&url, body).await; eprintln!("{:#?}", response); assert_eq!(status_code, 202); let update_id = response["updateId"].as_u64().unwrap(); - server.wait_update_id(update_id); + server.wait_update_id(update_id).await; // 3 - Check update success - let (response, status_code) = server.get_update_status(update_id); + let (response, status_code) = server.get_update_status(update_id).await; assert_eq!(status_code, 200); assert_eq!(response["status"], "processed"); } // Test issue https://github.com/meilisearch/MeiliSearch/issues/574 -#[test] -fn check_add_documents_with_nested_sequence() { +#[actix_rt::test] +async fn check_add_documents_with_nested_sequence() { let mut server = common::Server::with_uid("tasks"); // 1 - Create the index with no primary_key let body = json!({ "uid": "tasks" }); - let (response, status_code) = server.create_index(body); + let (response, status_code) = server.create_index(body).await; assert_eq!(status_code, 201); assert_eq!(response["primaryKey"], json!(null)); @@ -158,20 +158,20 @@ fn check_add_documents_with_nested_sequence() { }]); let url = "/indexes/tasks/documents"; - let (response, status_code) = server.post_request(&url, body.clone()); + let (response, status_code) = server.post_request(&url, body.clone()).await; eprintln!("{:#?}", response); assert_eq!(status_code, 202); let update_id = response["updateId"].as_u64().unwrap(); - server.wait_update_id(update_id); + server.wait_update_id(update_id).await; // 3 - Check update success - let (response, status_code) = server.get_update_status(update_id); + let (response, status_code) = server.get_update_status(update_id).await; assert_eq!(status_code, 200); assert_eq!(response["status"], "processed"); let url = "/indexes/tasks/search?q=leesz"; - let (response, status_code) = server.get_request(&url); + let (response, status_code) = server.get_request(&url).await; assert_eq!(status_code, 200); assert_eq!(response["hits"], body); } diff --git a/meilisearch-http/tests/documents_delete.rs b/meilisearch-http/tests/documents_delete.rs index 0773702ca..7edc9ac63 100644 --- a/meilisearch-http/tests/documents_delete.rs +++ b/meilisearch-http/tests/documents_delete.rs @@ -1,31 +1,31 @@ mod common; -#[test] -fn delete() { +#[actix_rt::test] +async fn delete() { let mut server = common::Server::with_uid("movies"); - server.populate_movies(); + server.populate_movies().await; - let (_response, status_code) = server.get_document(419704); + let (_response, status_code) = server.get_document(419704).await; assert_eq!(status_code, 200); - server.delete_document(419704); + server.delete_document(419704).await; - let (_response, status_code) = server.get_document(419704); + let (_response, status_code) = server.get_document(419704).await; assert_eq!(status_code, 404); } // Resolve teh issue https://github.com/meilisearch/MeiliSearch/issues/493 -#[test] -fn delete_batch() { +#[actix_rt::test] +async fn delete_batch() { let mut server = common::Server::with_uid("movies"); - server.populate_movies(); + server.populate_movies().await; - let (_response, status_code) = server.get_document(419704); + let (_response, status_code) = server.get_document(419704).await; assert_eq!(status_code, 200); let body = serde_json::json!([419704, 512200, 181812]); - server.delete_multiple_documents(body); + server.delete_multiple_documents(body).await; - let (_response, status_code) = server.get_document(419704); + let (_response, status_code) = server.get_document(419704).await; assert_eq!(status_code, 404); } diff --git a/meilisearch-http/tests/health.rs b/meilisearch-http/tests/health.rs index f41852f56..295a41a25 100644 --- a/meilisearch-http/tests/health.rs +++ b/meilisearch-http/tests/health.rs @@ -3,36 +3,36 @@ use std::convert::Into; mod common; -#[test] -fn test_healthyness() { +#[actix_rt::test] +async fn test_healthyness() { let mut server = common::Server::with_uid("movies"); // Check that the server is healthy - let (_response, status_code) = server.get_health(); + let (_response, status_code) = server.get_health().await; assert_eq!(status_code, 200); // Set the serve Unhealthy let body = json!({ "health": false, }); - let (_response, status_code) = server.update_health(body); + let (_response, status_code) = server.update_health(body).await; assert_eq!(status_code, 200); // Check that the server is unhealthy - let (_response, status_code) = server.get_health(); + let (_response, status_code) = server.get_health().await; assert_eq!(status_code, 503); // Set the server healthy let body = json!({ "health": true, }); - let (_response, status_code) = server.update_health(body); + let (_response, status_code) = server.update_health(body).await; assert_eq!(status_code, 200); // Check if the server is healthy - let (_response, status_code) = server.get_health(); + let (_response, status_code) = server.get_health().await; assert_eq!(status_code, 200); } diff --git a/meilisearch-http/tests/index.rs b/meilisearch-http/tests/index.rs index f825e336b..6ea81f606 100644 --- a/meilisearch-http/tests/index.rs +++ b/meilisearch-http/tests/index.rs @@ -4,8 +4,8 @@ use serde_json::Value; mod common; -#[test] -fn create_index_with_name() { +#[actix_rt::test] +async fn create_index_with_name() { let mut server = common::Server::with_uid("movies"); // 1 - Create a new index @@ -14,7 +14,7 @@ fn create_index_with_name() { "name": "movies", }); - let (res1_value, status_code) = server.create_index(body); + let (res1_value, status_code) = server.create_index(body).await; assert_eq!(status_code, 201); assert_eq!(res1_value.as_object().unwrap().len(), 5); @@ -29,7 +29,7 @@ fn create_index_with_name() { // 2 - Check the list of indexes - let (res2_value, status_code) = server.list_indexes(); + let (res2_value, status_code) = server.list_indexes().await; assert_eq!(status_code, 200); assert_eq!(res2_value.as_array().unwrap().len(), 1); @@ -44,8 +44,8 @@ fn create_index_with_name() { assert_eq!(r2_updated_at.len(), r1_updated_at.len()); } -#[test] -fn create_index_with_uid() { +#[actix_rt::test] +async fn create_index_with_uid() { let mut server = common::Server::with_uid("movies"); // 1 - Create a new index @@ -54,7 +54,7 @@ fn create_index_with_uid() { "uid": "movies", }); - let (res1_value, status_code) = server.create_index(body); + let (res1_value, status_code) = server.create_index(body).await; assert_eq!(status_code, 201); assert_eq!(res1_value.as_object().unwrap().len(), 5); @@ -69,7 +69,7 @@ fn create_index_with_uid() { // 2 - Check the list of indexes - let (res2_value, status_code) = server.list_indexes(); + let (res2_value, status_code) = server.list_indexes().await; assert_eq!(status_code, 200); assert_eq!(res2_value.as_array().unwrap().len(), 1); @@ -84,8 +84,8 @@ fn create_index_with_uid() { assert_eq!(r2_updated_at.len(), r1_updated_at.len()); } -#[test] -fn create_index_with_name_and_uid() { +#[actix_rt::test] +async fn create_index_with_name_and_uid() { let mut server = common::Server::with_uid("movies"); // 1 - Create a new index @@ -94,7 +94,7 @@ fn create_index_with_name_and_uid() { "name": "Films", "uid": "fr_movies", }); - let (res1_value, status_code) = server.create_index(body); + let (res1_value, status_code) = server.create_index(body).await; assert_eq!(status_code, 201); assert_eq!(res1_value.as_object().unwrap().len(), 5); @@ -109,7 +109,7 @@ fn create_index_with_name_and_uid() { // 2 - Check the list of indexes - let (res2_value, status_code) = server.list_indexes(); + let (res2_value, status_code) = server.list_indexes().await; assert_eq!(status_code, 200); assert_eq!(res2_value.as_array().unwrap().len(), 1); @@ -124,8 +124,8 @@ fn create_index_with_name_and_uid() { assert_eq!(r2_updated_at.len(), r1_updated_at.len()); } -#[test] -fn rename_index() { +#[actix_rt::test] +async fn rename_index() { let mut server = common::Server::with_uid("movies"); // 1 - Create a new index @@ -135,7 +135,7 @@ fn rename_index() { "uid": "movies", }); - let (res1_value, status_code) = server.create_index(body); + let (res1_value, status_code) = server.create_index(body).await; assert_eq!(status_code, 201); assert_eq!(res1_value.as_object().unwrap().len(), 5); @@ -154,7 +154,7 @@ fn rename_index() { "name": "TV Shows", }); - let (res2_value, status_code) = server.update_index(body); + let (res2_value, status_code) = server.update_index(body).await; assert_eq!(status_code, 200); assert_eq!(res2_value.as_object().unwrap().len(), 5); @@ -169,7 +169,7 @@ fn rename_index() { // 3 - Check the list of indexes - let (res3_value, status_code) = server.list_indexes(); + let (res3_value, status_code) = server.list_indexes().await; assert_eq!(status_code, 200); assert_eq!(res3_value.as_array().unwrap().len(), 1); @@ -184,8 +184,8 @@ fn rename_index() { assert_eq!(r3_updated_at.len(), r2_updated_at.len()); } -#[test] -fn delete_index_and_recreate_it() { +#[actix_rt::test] +async fn delete_index_and_recreate_it() { let mut server = common::Server::with_uid("movies"); // 1 - Create a new index @@ -195,7 +195,7 @@ fn delete_index_and_recreate_it() { "uid": "movies", }); - let (res1_value, status_code) = server.create_index(body); + let (res1_value, status_code) = server.create_index(body).await; assert_eq!(status_code, 201); assert_eq!(res1_value.as_object().unwrap().len(), 5); @@ -210,7 +210,7 @@ fn delete_index_and_recreate_it() { // 2 - Check the list of indexes - let (res2_value, status_code) = server.list_indexes(); + let (res2_value, status_code) = server.list_indexes().await; assert_eq!(status_code, 200); assert_eq!(res2_value.as_array().unwrap().len(), 1); @@ -226,13 +226,13 @@ fn delete_index_and_recreate_it() { // 3- Delete an index - let (_res2_value, status_code) = server.delete_index(); + let (_res2_value, status_code) = server.delete_index().await; assert_eq!(status_code, 204); // 4 - Check the list of indexes - let (res2_value, status_code) = server.list_indexes(); + let (res2_value, status_code) = server.list_indexes().await; assert_eq!(status_code, 200); assert_eq!(res2_value.as_array().unwrap().len(), 0); @@ -243,7 +243,7 @@ fn delete_index_and_recreate_it() { "name": "movies", }); - let (res1_value, status_code) = server.create_index(body); + let (res1_value, status_code) = server.create_index(body).await; assert_eq!(status_code, 201); assert_eq!(res1_value.as_object().unwrap().len(), 5); @@ -258,7 +258,7 @@ fn delete_index_and_recreate_it() { // 6 - Check the list of indexes - let (res2_value, status_code) = server.list_indexes(); + let (res2_value, status_code) = server.list_indexes().await; assert_eq!(status_code, 200); assert_eq!(res2_value.as_array().unwrap().len(), 1); assert_eq!(res2_value[0].as_object().unwrap().len(), 5); @@ -272,8 +272,8 @@ fn delete_index_and_recreate_it() { assert_eq!(r2_updated_at.len(), r1_updated_at.len()); } -#[test] -fn check_multiples_indexes() { +#[actix_rt::test] +async fn check_multiples_indexes() { let mut server = common::Server::with_uid("movies"); // 1 - Create a new index @@ -282,7 +282,7 @@ fn check_multiples_indexes() { "name": "movies", }); - let (res1_value, status_code) = server.create_index(body); + let (res1_value, status_code) = server.create_index(body).await; assert_eq!(status_code, 201); assert_eq!(res1_value.as_object().unwrap().len(), 5); @@ -297,7 +297,7 @@ fn check_multiples_indexes() { // 2 - Check the list of indexes - let (res2_value, status_code) = server.list_indexes(); + let (res2_value, status_code) = server.list_indexes().await; assert_eq!(status_code, 200); assert_eq!(res2_value.as_array().unwrap().len(), 1); @@ -317,7 +317,7 @@ fn check_multiples_indexes() { "name": "films", }); - let (res3_value, status_code) = server.create_index(body); + let (res3_value, status_code) = server.create_index(body).await; assert_eq!(status_code, 201); assert_eq!(res3_value.as_object().unwrap().len(), 5); @@ -332,7 +332,7 @@ fn check_multiples_indexes() { // 4 - Check the list of indexes - let (res4_value, status_code) = server.list_indexes(); + let (res4_value, status_code) = server.list_indexes().await; assert_eq!(status_code, 200); assert_eq!(res4_value.as_array().unwrap().len(), 2); @@ -370,15 +370,15 @@ fn check_multiples_indexes() { } } -#[test] -fn create_index_failed() { +#[actix_rt::test] +async fn create_index_failed() { let mut server = common::Server::with_uid("movies"); // 2 - Push index creation with empty json body let body = json!({}); - let (res_value, status_code) = server.create_index(body); + let (res_value, status_code) = server.create_index(body).await; assert_eq!(status_code, 400); let message = res_value["message"].as_str().unwrap(); @@ -392,12 +392,9 @@ fn create_index_failed() { "active": true }); - let (res_value, status_code) = server.create_index(body); + let (_res_value, status_code) = server.create_index(body).await; assert_eq!(status_code, 400); - let message = res_value["message"].as_str().unwrap(); - assert_eq!(res_value.as_object().unwrap().len(), 1); - assert_eq!(message, "invalid data"); // 3 - Create a index with wrong data type @@ -406,17 +403,14 @@ fn create_index_failed() { "uid": 0 }); - let (res_value, status_code) = server.create_index(body); + let (_res_value, status_code) = server.create_index(body).await; assert_eq!(status_code, 400); - let message = res_value["message"].as_str().unwrap(); - assert_eq!(res_value.as_object().unwrap().len(), 1); - assert_eq!(message, "invalid data"); } // Resolve issue https://github.com/meilisearch/MeiliSearch/issues/492 -#[test] -fn create_index_with_primary_key_and_index() { +#[actix_rt::test] +async fn create_index_with_primary_key_and_index() { let mut server = common::Server::with_uid("movies"); // 1 - Create the index @@ -426,7 +420,7 @@ fn create_index_with_primary_key_and_index() { "primaryKey": "id", }); - let (_response, status_code) = server.create_index(body); + let (_response, status_code) = server.create_index(body).await; assert_eq!(status_code, 201); // 2 - Add content @@ -436,11 +430,11 @@ fn create_index_with_primary_key_and_index() { "text": "The mask" }]); - server.add_or_replace_multiple_documents(body.clone()); + server.add_or_replace_multiple_documents(body.clone()).await; // 3 - Retreive document - let (response, _status_code) = server.get_document(123); + let (response, _status_code) = server.get_document(123).await; let expect = json!({ "id": 123, @@ -454,8 +448,8 @@ fn create_index_with_primary_key_and_index() { // Test when the given index uid is not valid // Should have a 400 status code // Should have the right error message -#[test] -fn create_index_with_invalid_uid() { +#[actix_rt::test] +async fn create_index_with_invalid_uid() { let mut server = common::Server::with_uid(""); // 1 - Create the index with invalid uid @@ -464,7 +458,7 @@ fn create_index_with_invalid_uid() { "uid": "the movies" }); - let (response, status_code) = server.create_index(body); + let (response, status_code) = server.create_index(body).await; assert_eq!(status_code, 400); let message = response["message"].as_str().unwrap(); @@ -477,7 +471,7 @@ fn create_index_with_invalid_uid() { "uid": "%$#" }); - let (response, status_code) = server.create_index(body); + let (response, status_code) = server.create_index(body).await; assert_eq!(status_code, 400); let message = response["message"].as_str().unwrap(); @@ -490,7 +484,7 @@ fn create_index_with_invalid_uid() { "uid": "the~movies" }); - let (response, status_code) = server.create_index(body); + let (response, status_code) = server.create_index(body).await; assert_eq!(status_code, 400); let message = response["message"].as_str().unwrap(); @@ -503,7 +497,7 @@ fn create_index_with_invalid_uid() { "uid": "🎉" }); - let (response, status_code) = server.create_index(body); + let (response, status_code) = server.create_index(body).await; assert_eq!(status_code, 400); let message = response["message"].as_str().unwrap(); @@ -512,8 +506,8 @@ fn create_index_with_invalid_uid() { } // Test that it's possible to add primary_key if it's not already set on index creation -#[test] -fn create_index_and_add_indentifier_after() { +#[actix_rt::test] +async fn create_index_and_add_indentifier_after() { let mut server = common::Server::with_uid("movies"); // 1 - Create the index with no primary_key @@ -521,7 +515,7 @@ fn create_index_and_add_indentifier_after() { let body = json!({ "uid": "movies", }); - let (response, status_code) = server.create_index(body); + let (response, status_code) = server.create_index(body).await; assert_eq!(status_code, 201); assert_eq!(response["primaryKey"], json!(null)); @@ -531,21 +525,21 @@ fn create_index_and_add_indentifier_after() { "primaryKey": "id", }); - let (response, status_code) = server.update_index(body); + let (response, status_code) = server.update_index(body).await; assert_eq!(status_code, 200); eprintln!("response: {:#?}", response); assert_eq!(response["primaryKey"].as_str().unwrap(), "id"); // 3 - Get index to verify if the primary_key is good - let (response, status_code) = server.get_index(); + let (response, status_code) = server.get_index().await; assert_eq!(status_code, 200); assert_eq!(response["primaryKey"].as_str().unwrap(), "id"); } // Test that it's impossible to change the primary_key -#[test] -fn create_index_and_update_indentifier_after() { +#[actix_rt::test] +async fn create_index_and_update_indentifier_after() { let mut server = common::Server::with_uid("movies"); // 1 - Create the index with no primary_key @@ -554,7 +548,7 @@ fn create_index_and_update_indentifier_after() { "uid": "movies", "primaryKey": "id", }); - let (response, status_code) = server.create_index(body); + let (response, status_code) = server.create_index(body).await; assert_eq!(status_code, 201); assert_eq!(response["primaryKey"].as_str().unwrap(), "id"); @@ -564,19 +558,19 @@ fn create_index_and_update_indentifier_after() { "primaryKey": "skuid", }); - let (_response, status_code) = server.update_index(body); + let (_response, status_code) = server.update_index(body).await; assert_eq!(status_code, 400); // 3 - Get index to verify if the primary_key still the first one - let (response, status_code) = server.get_index(); + let (response, status_code) = server.get_index().await; assert_eq!(status_code, 200); assert_eq!(response["primaryKey"].as_str().unwrap(), "id"); } // Test that schema inference work well -#[test] -fn create_index_without_primary_key_and_add_document() { +#[actix_rt::test] +async fn create_index_without_primary_key_and_add_document() { let mut server = common::Server::with_uid("movies"); // 1 - Create the index with no primary_key @@ -584,7 +578,7 @@ fn create_index_without_primary_key_and_add_document() { let body = json!({ "uid": "movies", }); - let (response, status_code) = server.create_index(body); + let (response, status_code) = server.create_index(body).await; assert_eq!(status_code, 201); assert_eq!(response["primaryKey"], json!(null)); @@ -595,18 +589,18 @@ fn create_index_without_primary_key_and_add_document() { "title": "I'm a legend", }]); - server.add_or_update_multiple_documents(body); + server.add_or_update_multiple_documents(body).await; // 3 - Get index to verify if the primary_key is good - let (response, status_code) = server.get_index(); + let (response, status_code) = server.get_index().await; assert_eq!(status_code, 200); assert_eq!(response["primaryKey"].as_str().unwrap(), "id"); } // Test search with no primary_key -#[test] -fn create_index_without_primary_key_and_search() { +#[actix_rt::test] +async fn create_index_without_primary_key_and_search() { let mut server = common::Server::with_uid("movies"); // 1 - Create the index with no primary_key @@ -614,7 +608,7 @@ fn create_index_without_primary_key_and_search() { let body = json!({ "uid": "movies", }); - let (response, status_code) = server.create_index(body); + let (response, status_code) = server.create_index(body).await; assert_eq!(status_code, 201); assert_eq!(response["primaryKey"], json!(null)); @@ -622,15 +616,15 @@ fn create_index_without_primary_key_and_search() { let query = "q=captain&limit=3"; - let (response, status_code) = server.search(&query); + let (response, status_code) = server.search(&query).await; assert_eq!(status_code, 200); assert_eq!(response["hits"].as_array().unwrap().len(), 0); } // Test the error message when we push an document update and impossibility to find primary key // Test issue https://github.com/meilisearch/MeiliSearch/issues/517 -#[test] -fn check_add_documents_without_primary_key() { +#[actix_rt::test] +async fn check_add_documents_without_primary_key() { let mut server = common::Server::with_uid("movies"); // 1 - Create the index with no primary_key @@ -638,7 +632,7 @@ fn check_add_documents_without_primary_key() { let body = json!({ "uid": "movies", }); - let (response, status_code) = server.create_index(body); + let (response, status_code) = server.create_index(body).await; assert_eq!(status_code, 201); assert_eq!(response["primaryKey"], json!(null)); @@ -649,7 +643,7 @@ fn check_add_documents_without_primary_key() { "comment": "comment test" }]); - let (response, status_code) = server.add_or_replace_multiple_documents_sync(body); + let (response, status_code) = server.add_or_replace_multiple_documents_sync(body).await; let expected = json!({ "message": "Could not infer a primary key" @@ -659,8 +653,8 @@ fn check_add_documents_without_primary_key() { assert_json_eq!(response, expected, ordered: false); } -#[test] -fn check_first_update_should_bring_up_processed_status_after_first_docs_addition() { +#[actix_rt::test] +async fn check_first_update_should_bring_up_processed_status_after_first_docs_addition() { let mut server = common::Server::with_uid("movies"); let body = json!({ @@ -668,7 +662,7 @@ fn check_first_update_should_bring_up_processed_status_after_first_docs_addition }); // 1. Create Index - let (response, status_code) = server.create_index(body); + let (response, status_code) = server.create_index(body).await; assert_eq!(status_code, 201); assert_eq!(response["primaryKey"], json!(null)); @@ -677,10 +671,10 @@ fn check_first_update_should_bring_up_processed_status_after_first_docs_addition let body: Value = serde_json::from_slice(dataset).unwrap(); // 2. Index the documents from movies.json, present inside of assets directory - server.add_or_replace_multiple_documents(body); + server.add_or_replace_multiple_documents(body).await; // 3. Fetch the status of the indexing done above. - let (response, status_code) = server.get_all_updates_status(); + let (response, status_code) = server.get_all_updates_status().await; // 4. Verify the fetch is successful and indexing status is 'processed' assert_eq!(status_code, 200); diff --git a/meilisearch-http/tests/search.rs b/meilisearch-http/tests/search.rs index faa5d9e9b..7f3409b20 100644 --- a/meilisearch-http/tests/search.rs +++ b/meilisearch-http/tests/search.rs @@ -16,8 +16,8 @@ static GLOBAL_SERVER: Lazy> = Lazy::new(|| { // Search // q: Captain // limit: 3 -#[test] -fn search_with_limit() { +#[actix_rt::test] +async fn search_with_limit() { let query = "q=captain&limit=3"; let expected = json!([ @@ -74,7 +74,7 @@ fn search_with_limit() { } ]); - let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query); + let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query).await; assert_json_eq!(expected, response["hits"].clone(), ordered: false); } @@ -82,8 +82,8 @@ fn search_with_limit() { // q: Captain // limit: 3 // offset: 1 -#[test] -fn search_with_offset() { +#[actix_rt::test] +async fn search_with_offset() { let query = "q=captain&limit=3&offset=1"; let expected = json!([ @@ -141,7 +141,7 @@ fn search_with_offset() { } ]); - let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query); + let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query).await; assert_json_eq!(expected, response["hits"].clone(), ordered: false); } @@ -149,8 +149,8 @@ fn search_with_offset() { // q: Captain // limit: 1 // attributeToHighlight: * -#[test] -fn search_with_attribute_to_highlight_wildcard() { +#[actix_rt::test] +async fn search_with_attribute_to_highlight_wildcard() { let query = "q=captain&limit=1&attributesToHighlight=*"; let expected = json!([ @@ -190,7 +190,7 @@ fn search_with_attribute_to_highlight_wildcard() { } ]); - let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query); + let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query).await; assert_json_eq!(expected, response["hits"].clone(), ordered: false); } @@ -198,8 +198,8 @@ fn search_with_attribute_to_highlight_wildcard() { // q: Captain // limit: 1 // attributeToHighlight: title -#[test] -fn search_with_attribute_to_highlight_1() { +#[actix_rt::test] +async fn search_with_attribute_to_highlight_1() { let query = "q=captain&limit=1&attributesToHighlight=title"; let expected = json!([ @@ -239,7 +239,7 @@ fn search_with_attribute_to_highlight_1() { } ]); - let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query); + let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query).await; assert_json_eq!(expected, response["hits"].clone(), ordered: false); } @@ -247,8 +247,8 @@ fn search_with_attribute_to_highlight_1() { // q: Captain // limit: 1 // attributeToHighlight: title,tagline -#[test] -fn search_with_attribute_to_highlight_title_tagline() { +#[actix_rt::test] +async fn search_with_attribute_to_highlight_title_tagline() { let query = "q=captain&limit=1&attributesToHighlight=title,tagline"; let expected = json!([ @@ -288,7 +288,7 @@ fn search_with_attribute_to_highlight_title_tagline() { } ]); - let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query); + let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query).await; assert_json_eq!(expected, response["hits"].clone(), ordered: false); } @@ -296,8 +296,8 @@ fn search_with_attribute_to_highlight_title_tagline() { // q: Captain // limit: 1 // attributeToHighlight: title,overview -#[test] -fn search_with_attribute_to_highlight_title_overview() { +#[actix_rt::test] +async fn search_with_attribute_to_highlight_title_overview() { let query = "q=captain&limit=1&attributesToHighlight=title,overview"; let expected = json!([ @@ -337,7 +337,7 @@ fn search_with_attribute_to_highlight_title_overview() { } ]); - let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query); + let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query).await; assert_json_eq!(expected, response["hits"].clone(), ordered: false); } @@ -345,8 +345,8 @@ fn search_with_attribute_to_highlight_title_overview() { // q: Captain // limit: 1 // matches: true -#[test] -fn search_with_matches() { +#[actix_rt::test] +async fn search_with_matches() { let query = "q=captain&limit=1&matches=true"; let expected = json!([ @@ -383,7 +383,7 @@ fn search_with_matches() { } ]); - let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query); + let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query).await; assert_json_eq!(expected, response["hits"].clone(), ordered: false); } @@ -392,8 +392,8 @@ fn search_with_matches() { // limit: 1 // attributesToCrop: overview // cropLength: 20 -#[test] -fn search_witch_crop() { +#[actix_rt::test] +async fn search_witch_crop() { let query = "q=captain&limit=1&attributesToCrop=overview&cropLength=20"; let expected = json!([ @@ -433,7 +433,7 @@ fn search_witch_crop() { } ]); - let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query); + let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query).await; assert_json_eq!(expected, response["hits"].clone(), ordered: false); } @@ -441,8 +441,8 @@ fn search_witch_crop() { // q: Captain // limit: 1 // attributesToRetrieve: [title,tagline,overview,poster_path] -#[test] -fn search_with_attributes_to_retrieve() { +#[actix_rt::test] +async fn search_with_attributes_to_retrieve() { let query = "q=captain&limit=1&attributesToRetrieve=title,tagline,overview,poster_path"; let expected = json!([ @@ -454,7 +454,7 @@ fn search_with_attributes_to_retrieve() { } ]); - let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query); + let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query).await; assert_json_eq!(expected, response["hits"].clone(), ordered: false); } @@ -462,8 +462,8 @@ fn search_with_attributes_to_retrieve() { // q: Captain // limit: 1 // attributesToRetrieve: * -#[test] -fn search_with_attributes_to_retrieve_wildcard() { +#[actix_rt::test] +async fn search_with_attributes_to_retrieve_wildcard() { let query = "q=captain&limit=1&attributesToRetrieve=*"; let expected = json!([ @@ -486,7 +486,7 @@ fn search_with_attributes_to_retrieve_wildcard() { } ]); - let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query); + let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query).await; assert_json_eq!(expected, response["hits"].clone(), ordered: false); } @@ -494,8 +494,8 @@ fn search_with_attributes_to_retrieve_wildcard() { // q: Captain // limit: 3 // filters: director:Anthony%20Russo -#[test] -fn search_with_filter() { +#[actix_rt::test] +async fn search_with_filter() { let query = "q=captain&filters=director%20%3D%20%22Anthony%20Russo%22&limit=3"; let expected = json!([ { @@ -744,8 +744,8 @@ fn search_with_filter() { // limit: 1 // attributesToHighlight: [title,overview] // matches: true -#[test] -fn search_with_attributes_to_highlight_and_matches() { +#[actix_rt::test] +async fn search_with_attributes_to_highlight_and_matches() { let query = "q=captain&limit=1&attributesToHighlight=title,overview&matches=true"; let expected = json!( [ @@ -799,7 +799,7 @@ fn search_with_attributes_to_highlight_and_matches() { } ]); - let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query); + let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query).await; assert_json_eq!(expected, response["hits"].clone(), ordered: false); } @@ -810,8 +810,8 @@ fn search_with_attributes_to_highlight_and_matches() { // matches: true // cropLength: 20 // attributesToCrop: overview -#[test] -fn search_with_attributes_to_highlight_and_matches_and_crop() { +#[actix_rt::test] +async fn search_with_attributes_to_highlight_and_matches_and_crop() { let query = "q=captain&limit=1&attributesToCrop=overview&cropLength=20&attributesToHighlight=title,overview&matches=true"; let expected = json!([ @@ -865,7 +865,7 @@ fn search_with_attributes_to_highlight_and_matches_and_crop() { } ]); - let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query); + let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query).await; assert_json_eq!(expected, response["hits"].clone(), ordered: false); } @@ -874,8 +874,8 @@ fn search_with_attributes_to_highlight_and_matches_and_crop() { // limit: 1 // attributesToRetrieve: [title,producer,director] // attributesToHighlight: [title] -#[test] -fn search_with_differents_attributes() { +#[actix_rt::test] +async fn search_with_differents_attributes() { let query = "q=captain&limit=1&attributesToRetrieve=title,producer,director&attributesToHighlight=title"; let expected = json!([ @@ -889,7 +889,7 @@ fn search_with_differents_attributes() { } ]); - let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query); + let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query).await; assert_json_eq!(expected, response["hits"].clone(), ordered: false); } @@ -899,8 +899,8 @@ fn search_with_differents_attributes() { // attributesToRetrieve: [title,producer,director] // attributesToCrop: [overview] // cropLength: 10 -#[test] -fn search_with_differents_attributes_2() { +#[actix_rt::test] +async fn search_with_differents_attributes_2() { let query = "q=captain&limit=1&attributesToRetrieve=title,producer,director&attributesToCrop=overview&cropLength=10"; let expected = json!([ @@ -914,7 +914,7 @@ fn search_with_differents_attributes_2() { } ]); - let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query); + let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query).await; assert_json_eq!(expected, response["hits"].clone(), ordered: false); } @@ -923,8 +923,8 @@ fn search_with_differents_attributes_2() { // limit: 1 // attributesToRetrieve: [title,producer,director] // attributesToCrop: [overview:10] -#[test] -fn search_with_differents_attributes_3() { +#[actix_rt::test] +async fn search_with_differents_attributes_3() { let query = "q=captain&limit=1&attributesToRetrieve=title,producer,director&attributesToCrop=overview:10"; let expected = json!([ @@ -938,7 +938,7 @@ fn search_with_differents_attributes_3() { } ]); - let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query); + let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query).await; assert_json_eq!(expected, response["hits"].clone(), ordered: false); } @@ -947,8 +947,8 @@ fn search_with_differents_attributes_3() { // limit: 1 // attributesToRetrieve: [title,producer,director] // attributesToCrop: [overview:10,title:0] -#[test] -fn search_with_differents_attributes_4() { +#[actix_rt::test] +async fn search_with_differents_attributes_4() { let query = "q=captain&limit=1&attributesToRetrieve=title,producer,director&attributesToCrop=overview:10,title:0"; let expected = json!([ @@ -963,7 +963,7 @@ fn search_with_differents_attributes_4() { } ]); - let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query); + let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query).await; assert_json_eq!(expected, response["hits"].clone(), ordered: false); } @@ -972,8 +972,8 @@ fn search_with_differents_attributes_4() { // limit: 1 // attributesToRetrieve: [title,producer,director] // attributesToCrop: [*,overview:10] -#[test] -fn search_with_differents_attributes_5() { +#[actix_rt::test] +async fn search_with_differents_attributes_5() { let query = "q=captain&limit=1&attributesToRetrieve=title,producer,director&attributesToCrop=*,overview:10"; let expected = json!([ @@ -990,7 +990,7 @@ fn search_with_differents_attributes_5() { } ]); - let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query); + let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query).await; assert_json_eq!(expected, response["hits"].clone(), ordered: false); } @@ -1000,8 +1000,8 @@ fn search_with_differents_attributes_5() { // attributesToRetrieve: [title,producer,director] // attributesToCrop: [*,overview:10] // attributesToHighlight: [title] -#[test] -fn search_with_differents_attributes_6() { +#[actix_rt::test] +async fn search_with_differents_attributes_6() { let query = "q=captain&limit=1&attributesToRetrieve=title,producer,director&attributesToCrop=*,overview:10&attributesToHighlight=title"; let expected = json!([ @@ -1018,7 +1018,7 @@ fn search_with_differents_attributes_6() { } ]); - let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query); + let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query).await; assert_json_eq!(expected, response["hits"].clone(), ordered: false); } @@ -1028,8 +1028,8 @@ fn search_with_differents_attributes_6() { // attributesToRetrieve: [title,producer,director] // attributesToCrop: [*,overview:10] // attributesToHighlight: [*] -#[test] -fn search_with_differents_attributes_7() { +#[actix_rt::test] +async fn search_with_differents_attributes_7() { let query = "q=captain&limit=1&attributesToRetrieve=title,producer,director&attributesToCrop=*,overview:10&attributesToHighlight=*"; let expected = json!([ @@ -1046,7 +1046,7 @@ fn search_with_differents_attributes_7() { } ]); - let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query); + let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query).await; assert_json_eq!(expected, response["hits"].clone(), ordered: false); } @@ -1056,8 +1056,8 @@ fn search_with_differents_attributes_7() { // attributesToRetrieve: [title,producer,director] // attributesToCrop: [*,overview:10] // attributesToHighlight: [*,tagline] -#[test] -fn search_with_differents_attributes_8() { +#[actix_rt::test] +async fn search_with_differents_attributes_8() { let query = "q=captain&limit=1&attributesToRetrieve=title,producer,director&attributesToCrop=*,overview:10&attributesToHighlight=*,tagline"; let expected = json!([ @@ -1075,6 +1075,6 @@ fn search_with_differents_attributes_8() { } ]); - let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query); + let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query).await; assert_json_eq!(expected, response["hits"].clone(), ordered: false); } diff --git a/meilisearch-http/tests/search_settings.rs b/meilisearch-http/tests/search_settings.rs index 1e5762d65..6effbdda0 100644 --- a/meilisearch-http/tests/search_settings.rs +++ b/meilisearch-http/tests/search_settings.rs @@ -4,10 +4,10 @@ use std::convert::Into; mod common; -#[test] -fn search_with_settings_basic() { +#[actix_rt::test] +async fn search_with_settings_basic() { let mut server = common::Server::with_uid("movies"); - server.populate_movies(); + server.populate_movies().await; let config = json!({ "rankingRules": [ @@ -49,7 +49,7 @@ fn search_with_settings_basic() { "acceptNewFields": false, }); - server.update_all_settings(config); + server.update_all_settings(config).await; let query = "q=the%20avangers&limit=3"; let expect = json!([ @@ -106,14 +106,14 @@ fn search_with_settings_basic() { } ]); - let (response, _status_code) = server.search(query); + let (response, _status_code) = server.search(query).await; assert_json_eq!(expect, response["hits"].clone(), ordered: false); } -#[test] -fn search_with_settings_stop_words() { +#[actix_rt::test] +async fn search_with_settings_stop_words() { let mut server = common::Server::with_uid("movies"); - server.populate_movies(); + server.populate_movies().await; let config = json!({ "rankingRules": [ @@ -155,7 +155,7 @@ fn search_with_settings_stop_words() { "acceptNewFields": false, }); - server.update_all_settings(config); + server.update_all_settings(config).await; let query = "q=the%20avangers&limit=3"; let expect = json!([ @@ -212,14 +212,14 @@ fn search_with_settings_stop_words() { } ]); - let (response, _status_code) = server.search(query); + let (response, _status_code) = server.search(query).await; assert_json_eq!(expect, response["hits"].clone(), ordered: false); } -#[test] -fn search_with_settings_synonyms() { +#[actix_rt::test] +async fn search_with_settings_synonyms() { let mut server = common::Server::with_uid("movies"); - server.populate_movies(); + server.populate_movies().await; let config = json!({ "rankingRules": [ @@ -266,7 +266,7 @@ fn search_with_settings_synonyms() { "acceptNewFields": false, }); - server.update_all_settings(config); + server.update_all_settings(config).await; let query = "q=avangers&limit=3"; let expect = json!([ @@ -323,14 +323,14 @@ fn search_with_settings_synonyms() { } ]); - let (response, _status_code) = server.search(query); + let (response, _status_code) = server.search(query).await; assert_json_eq!(expect, response["hits"].clone(), ordered: false); } -#[test] -fn search_with_settings_ranking_rules() { +#[actix_rt::test] +async fn search_with_settings_ranking_rules() { let mut server = common::Server::with_uid("movies"); - server.populate_movies(); + server.populate_movies().await; let config = json!({ "rankingRules": [ @@ -372,7 +372,7 @@ fn search_with_settings_ranking_rules() { "acceptNewFields": false, }); - server.update_all_settings(config); + server.update_all_settings(config).await; let query = "q=avangers&limit=3"; let expect = json!([ @@ -429,14 +429,14 @@ fn search_with_settings_ranking_rules() { } ]); - let (response, _status_code) = server.search(query); + let (response, _status_code) = server.search(query).await; assert_json_eq!(expect, response["hits"].clone(), ordered: false); } -#[test] -fn search_with_settings_searchable_attributes() { +#[actix_rt::test] +async fn search_with_settings_searchable_attributes() { let mut server = common::Server::with_uid("movies"); - server.populate_movies(); + server.populate_movies().await; let config = json!({ "rankingRules": [ @@ -477,7 +477,7 @@ fn search_with_settings_searchable_attributes() { "acceptNewFields": false, }); - server.update_all_settings(config); + server.update_all_settings(config).await; let query = "q=avangers&limit=3"; let expect = json!([ @@ -534,14 +534,14 @@ fn search_with_settings_searchable_attributes() { } ]); - let (response, _status_code) = server.search(query); + let (response, _status_code) = server.search(query).await; assert_json_eq!(expect, response["hits"].clone(), ordered: false); } -#[test] -fn search_with_settings_displayed_attributes() { +#[actix_rt::test] +async fn search_with_settings_displayed_attributes() { let mut server = common::Server::with_uid("movies"); - server.populate_movies(); + server.populate_movies().await; let config = json!({ "rankingRules": [ @@ -577,7 +577,7 @@ fn search_with_settings_displayed_attributes() { "acceptNewFields": false, }); - server.update_all_settings(config); + server.update_all_settings(config).await; let query = "q=avangers&limit=3"; let expect = json!([ @@ -604,14 +604,14 @@ fn search_with_settings_displayed_attributes() { } ]); - let (response, _status_code) = server.search(query); + let (response, _status_code) = server.search(query).await; assert_json_eq!(expect, response["hits"].clone(), ordered: false); } -#[test] -fn search_with_settings_searchable_attributes_2() { +#[actix_rt::test] +async fn search_with_settings_searchable_attributes_2() { let mut server = common::Server::with_uid("movies"); - server.populate_movies(); + server.populate_movies().await; let config = json!({ "rankingRules": [ @@ -647,7 +647,7 @@ fn search_with_settings_searchable_attributes_2() { "acceptNewFields": false, }); - server.update_all_settings(config); + server.update_all_settings(config).await; let query = "q=avangers&limit=3"; let expect = json!([ @@ -674,6 +674,6 @@ fn search_with_settings_searchable_attributes_2() { } ]); - let (response, _status_code) = server.search(query); + let (response, _status_code) = server.search(query).await; assert_json_eq!(expect, response["hits"].clone(), ordered: false); } diff --git a/meilisearch-http/tests/settings.rs b/meilisearch-http/tests/settings.rs index e2b57ee31..63f91da56 100644 --- a/meilisearch-http/tests/settings.rs +++ b/meilisearch-http/tests/settings.rs @@ -4,10 +4,10 @@ use std::convert::Into; mod common; -#[test] -fn write_all_and_delete() { +#[actix_rt::test] +async fn write_all_and_delete() { let mut server = common::Server::with_uid("movies"); - server.populate_movies(); + server.populate_movies().await; // 2 - Send the settings @@ -51,21 +51,21 @@ fn write_all_and_delete() { "acceptNewFields": false, }); - server.update_all_settings(body.clone()); + server.update_all_settings(body.clone()).await; // 3 - Get all settings and compare to the previous one - let (response, _status_code) = server.get_all_settings(); + let (response, _status_code) = server.get_all_settings().await; assert_json_eq!(body, response, ordered: false); // 4 - Delete all settings - server.delete_all_settings(); + server.delete_all_settings().await; // 5 - Get all settings and check if they are set to default values - let (response, _status_code) = server.get_all_settings(); + let (response, _status_code) = server.get_all_settings().await; let expect = json!({ "rankingRules": [ @@ -125,10 +125,10 @@ fn write_all_and_delete() { assert_json_eq!(expect, response, ordered: false); } -#[test] -fn write_all_and_update() { +#[actix_rt::test] +async fn write_all_and_update() { let mut server = common::Server::with_uid("movies"); - server.populate_movies(); + server.populate_movies().await; // 2 - Send the settings @@ -172,11 +172,11 @@ fn write_all_and_update() { "acceptNewFields": false, }); - server.update_all_settings(body.clone()); + server.update_all_settings(body.clone()).await; // 3 - Get all settings and compare to the previous one - let (response, _status_code) = server.get_all_settings(); + let (response, _status_code) = server.get_all_settings().await; assert_json_eq!(body, response, ordered: false); @@ -213,11 +213,11 @@ fn write_all_and_update() { "acceptNewFields": false, }); - server.update_all_settings(body); + server.update_all_settings(body).await; // 5 - Get all settings and check if the content is the same of (4) - let (response, _status_code) = server.get_all_settings(); + let (response, _status_code) = server.get_all_settings().await; let expected = json!({ "rankingRules": [ @@ -253,13 +253,13 @@ fn write_all_and_update() { assert_json_eq!(expected, response, ordered: false); } -#[test] -fn test_default_settings() { +#[actix_rt::test] +async fn test_default_settings() { let mut server = common::Server::with_uid("movies"); let body = json!({ "uid": "movies", }); - server.create_index(body); + server.create_index(body).await; // 1 - Get all settings and compare to the previous one @@ -280,19 +280,19 @@ fn test_default_settings() { "acceptNewFields": true, }); - let (response, _status_code) = server.get_all_settings(); + let (response, _status_code) = server.get_all_settings().await; assert_json_eq!(body, response, ordered: false); } -#[test] -fn test_default_settings_2() { +#[actix_rt::test] +async fn test_default_settings_2() { let mut server = common::Server::with_uid("movies"); let body = json!({ "uid": "movies", "primaryKey": "id", }); - server.create_index(body); + server.create_index(body).await; // 1 - Get all settings and compare to the previous one @@ -317,19 +317,19 @@ fn test_default_settings_2() { "acceptNewFields": true, }); - let (response, _status_code) = server.get_all_settings(); + let (response, _status_code) = server.get_all_settings().await; assert_json_eq!(body, response, ordered: false); } // Test issue https://github.com/meilisearch/MeiliSearch/issues/516 -#[test] -fn write_setting_and_update_partial() { +#[actix_rt::test] +async fn write_setting_and_update_partial() { let mut server = common::Server::with_uid("movies"); let body = json!({ "uid": "movies", }); - server.create_index(body); + server.create_index(body).await; // 2 - Send the settings @@ -352,7 +352,7 @@ fn write_setting_and_update_partial() { ] }); - server.update_all_settings(body.clone()); + server.update_all_settings(body.clone()).await; // 2 - Send the settings @@ -380,7 +380,7 @@ fn write_setting_and_update_partial() { "acceptNewFields": false, }); - server.update_all_settings(body.clone()); + server.update_all_settings(body.clone()).await; // 2 - Send the settings @@ -424,7 +424,7 @@ fn write_setting_and_update_partial() { "acceptNewFields": false, }); - let (response, _status_code) = server.get_all_settings(); + let (response, _status_code) = server.get_all_settings().await; assert_json_eq!(expected, response, ordered: false); } diff --git a/meilisearch-http/tests/settings_accept_new_fields.rs b/meilisearch-http/tests/settings_accept_new_fields.rs index 7293badd4..6127c6478 100644 --- a/meilisearch-http/tests/settings_accept_new_fields.rs +++ b/meilisearch-http/tests/settings_accept_new_fields.rs @@ -3,14 +3,14 @@ use serde_json::json; mod common; -#[test] -fn index_new_fields_default() { +#[actix_rt::test] +async fn index_new_fields_default() { let mut server = common::Server::with_uid("movies"); let body = json!({ "uid": "movies", "primaryKey": "id", }); - server.create_index(body); + server.create_index(body).await; // 1 - Add a document @@ -19,7 +19,7 @@ fn index_new_fields_default() { "title": "I'm a legend", }]); - server.add_or_replace_multiple_documents(body); + server.add_or_replace_multiple_documents(body).await; // 2 - Get the complete document @@ -28,7 +28,7 @@ fn index_new_fields_default() { "title": "I'm a legend", }); - let (response, status_code) = server.get_document(1); + let (response, status_code) = server.get_document(1).await; assert_eq!(status_code, 200); assert_json_eq!(response, expected); @@ -40,7 +40,7 @@ fn index_new_fields_default() { "description": "A bad copy of the original movie I'm a lengend" }]); - server.add_or_replace_multiple_documents(body); + server.add_or_replace_multiple_documents(body).await; // 4 - Get the complete document @@ -50,23 +50,23 @@ fn index_new_fields_default() { "description": "A bad copy of the original movie I'm a lengend" }); - let (response, status_code) = server.get_document(2); + let (response, status_code) = server.get_document(2).await; assert_eq!(status_code, 200); assert_json_eq!(response, expected); } -#[test] -fn index_new_fields_true() { +#[actix_rt::test] +async fn index_new_fields_true() { let mut server = common::Server::with_uid("movies"); let body = json!({ "uid": "movies", "primaryKey": "id", }); - server.create_index(body); + server.create_index(body).await; // 1 - Set indexNewFields = true - server.update_accept_new_fields(json!(true)); + server.update_accept_new_fields(json!(true)).await; // 2 - Add a document @@ -75,7 +75,7 @@ fn index_new_fields_true() { "title": "I'm a legend", }]); - server.add_or_replace_multiple_documents(body); + server.add_or_replace_multiple_documents(body).await; // 3 - Get the complete document @@ -84,7 +84,7 @@ fn index_new_fields_true() { "title": "I'm a legend", }); - let (response, status_code) = server.get_document(1); + let (response, status_code) = server.get_document(1).await; assert_eq!(status_code, 200); assert_json_eq!(response, expected); @@ -96,7 +96,7 @@ fn index_new_fields_true() { "description": "A bad copy of the original movie I'm a lengend" }]); - server.add_or_replace_multiple_documents(body); + server.add_or_replace_multiple_documents(body).await; // 5 - Get the complete document @@ -106,23 +106,23 @@ fn index_new_fields_true() { "description": "A bad copy of the original movie I'm a lengend" }); - let (response, status_code) = server.get_document(2); + let (response, status_code) = server.get_document(2).await; assert_eq!(status_code, 200); assert_json_eq!(response, expected); } -#[test] -fn index_new_fields_false() { +#[actix_rt::test] +async fn index_new_fields_false() { let mut server = common::Server::with_uid("movies"); let body = json!({ "uid": "movies", "primaryKey": "id", }); - server.create_index(body); + server.create_index(body).await; // 1 - Set indexNewFields = false - server.update_accept_new_fields(json!(false)); + server.update_accept_new_fields(json!(false)).await; // 2 - Add a document @@ -131,7 +131,7 @@ fn index_new_fields_false() { "title": "I'm a legend", }]); - server.add_or_replace_multiple_documents(body); + server.add_or_replace_multiple_documents(body).await; // 3 - Get the complete document @@ -139,7 +139,7 @@ fn index_new_fields_false() { "id": 1, }); - let (response, status_code) = server.get_document(1); + let (response, status_code) = server.get_document(1).await; assert_eq!(status_code, 200); assert_json_eq!(response, expected); @@ -151,7 +151,7 @@ fn index_new_fields_false() { "description": "A bad copy of the original movie I'm a lengend" }]); - server.add_or_replace_multiple_documents(body); + server.add_or_replace_multiple_documents(body).await; // 5 - Get the complete document @@ -159,23 +159,23 @@ fn index_new_fields_false() { "id": 2, }); - let (response, status_code) = server.get_document(2); + let (response, status_code) = server.get_document(2).await; assert_eq!(status_code, 200); assert_json_eq!(response, expected); } -#[test] -fn index_new_fields_true_then_false() { +#[actix_rt::test] +async fn index_new_fields_true_then_false() { let mut server = common::Server::with_uid("movies"); let body = json!({ "uid": "movies", "primaryKey": "id", }); - server.create_index(body); + server.create_index(body).await; // 1 - Set indexNewFields = true - server.update_accept_new_fields(json!(true)); + server.update_accept_new_fields(json!(true)).await; // 2 - Add a document @@ -184,7 +184,7 @@ fn index_new_fields_true_then_false() { "title": "I'm a legend", }]); - server.add_or_replace_multiple_documents(body); + server.add_or_replace_multiple_documents(body).await; // 3 - Get the complete document @@ -193,13 +193,13 @@ fn index_new_fields_true_then_false() { "title": "I'm a legend", }); - let (response, status_code) = server.get_document(1); + let (response, status_code) = server.get_document(1).await; assert_eq!(status_code, 200); assert_json_eq!(response, expected); // 4 - Set indexNewFields = false - server.update_accept_new_fields(json!(false)); + server.update_accept_new_fields(json!(false)).await; // 5 - Add a document with more fields @@ -209,7 +209,7 @@ fn index_new_fields_true_then_false() { "description": "A bad copy of the original movie I'm a lengend" }]); - server.add_or_replace_multiple_documents(body); + server.add_or_replace_multiple_documents(body).await; // 6 - Get the complete document @@ -218,23 +218,23 @@ fn index_new_fields_true_then_false() { "title": "I'm not a legend", }); - let (response, status_code) = server.get_document(2); + let (response, status_code) = server.get_document(2).await; assert_eq!(status_code, 200); assert_json_eq!(response, expected); } -#[test] -fn index_new_fields_false_then_true() { +#[actix_rt::test] +async fn index_new_fields_false_then_true() { let mut server = common::Server::with_uid("movies"); let body = json!({ "uid": "movies", "primaryKey": "id", }); - server.create_index(body); + server.create_index(body).await; // 1 - Set indexNewFields = false - server.update_accept_new_fields(json!(false)); + server.update_accept_new_fields(json!(false)).await; // 2 - Add a document @@ -243,7 +243,7 @@ fn index_new_fields_false_then_true() { "title": "I'm a legend", }]); - server.add_or_replace_multiple_documents(body); + server.add_or_replace_multiple_documents(body).await; // 3 - Get the complete document @@ -251,13 +251,13 @@ fn index_new_fields_false_then_true() { "id": 1, }); - let (response, status_code) = server.get_document(1); + let (response, status_code) = server.get_document(1).await; assert_eq!(status_code, 200); assert_json_eq!(response, expected); // 4 - Set indexNewFields = false - server.update_accept_new_fields(json!(true)); + server.update_accept_new_fields(json!(true)).await; // 5 - Add a document with more fields @@ -267,7 +267,7 @@ fn index_new_fields_false_then_true() { "description": "A bad copy of the original movie I'm a lengend" }]); - server.add_or_replace_multiple_documents(body); + server.add_or_replace_multiple_documents(body).await; // 6 - Get the complete document @@ -275,7 +275,7 @@ fn index_new_fields_false_then_true() { "id": 1, }); - let (response, status_code) = server.get_document(1); + let (response, status_code) = server.get_document(1).await; assert_eq!(status_code, 200); assert_json_eq!(response, expected); @@ -284,14 +284,14 @@ fn index_new_fields_false_then_true() { "description": "A bad copy of the original movie I'm a lengend" }); - let (response, status_code) = server.get_document(2); + let (response, status_code) = server.get_document(2).await; assert_eq!(status_code, 200); assert_json_eq!(response, expected); } // Fix issue https://github.com/meilisearch/MeiliSearch/issues/518 -#[test] -fn accept_new_fields_does_not_take_into_account_the_primary_key() { +#[actix_rt::test] +async fn accept_new_fields_does_not_take_into_account_the_primary_key() { let mut server = common::Server::with_uid("movies"); // 1 - Create an index with no primary-key @@ -299,7 +299,7 @@ fn accept_new_fields_does_not_take_into_account_the_primary_key() { let body = json!({ "uid": "movies", }); - let (response, status_code) = server.create_index(body); + let (response, status_code) = server.create_index(body).await; assert_eq!(status_code, 201); assert_eq!(response["primaryKey"], json!(null)); @@ -311,7 +311,7 @@ fn accept_new_fields_does_not_take_into_account_the_primary_key() { "acceptNewFields": false, }); - server.update_all_settings(body); + server.update_all_settings(body).await; // 4 - Add a document @@ -321,11 +321,11 @@ fn accept_new_fields_does_not_take_into_account_the_primary_key() { "comment": "comment test" }]); - server.add_or_replace_multiple_documents(body); + server.add_or_replace_multiple_documents(body).await; // 5 - Get settings, they should not changed - let (response, _status_code) = server.get_all_settings(); + let (response, _status_code) = server.get_all_settings().await; let expected = json!({ "rankingRules": [ diff --git a/meilisearch-http/tests/settings_ranking_rules.rs b/meilisearch-http/tests/settings_ranking_rules.rs index 9c5a82f59..7dbe9c8ea 100644 --- a/meilisearch-http/tests/settings_ranking_rules.rs +++ b/meilisearch-http/tests/settings_ranking_rules.rs @@ -3,10 +3,10 @@ use serde_json::json; mod common; -#[test] -fn write_all_and_delete() { +#[actix_rt::test] +async fn write_all_and_delete() { let mut server = common::Server::with_uid("movies"); - server.populate_movies(); + server.populate_movies().await; // 2 - Send the settings @@ -21,21 +21,21 @@ fn write_all_and_delete() { "desc(rank)", ]); - server.update_ranking_rules(body.clone()); + server.update_ranking_rules(body.clone()).await; // 3 - Get all settings and compare to the previous one - let (response, _status_code) = server.get_ranking_rules(); + let (response, _status_code) = server.get_ranking_rules().await; assert_json_eq!(body, response, ordered: false); // 4 - Delete all settings - server.delete_ranking_rules(); + server.delete_ranking_rules().await; // 5 - Get all settings and check if they are empty - let (response, _status_code) = server.get_ranking_rules(); + let (response, _status_code) = server.get_ranking_rules().await; let expected = json!([ "typo", @@ -49,10 +49,10 @@ fn write_all_and_delete() { assert_json_eq!(expected, response, ordered: false); } -#[test] -fn write_all_and_update() { +#[actix_rt::test] +async fn write_all_and_update() { let mut server = common::Server::with_uid("movies"); - server.populate_movies(); + server.populate_movies().await; // 2 - Send the settings @@ -67,11 +67,11 @@ fn write_all_and_update() { "desc(rank)", ]); - server.update_ranking_rules(body.clone()); + server.update_ranking_rules(body.clone()).await; // 3 - Get all settings and compare to the previous one - let (response, _status_code) = server.get_ranking_rules(); + let (response, _status_code) = server.get_ranking_rules().await; assert_json_eq!(body, response, ordered: false); @@ -87,11 +87,11 @@ fn write_all_and_update() { "desc(release_date)", ]); - server.update_ranking_rules(body); + server.update_ranking_rules(body).await; // 5 - Get all settings and check if the content is the same of (4) - let (response, _status_code) = server.get_ranking_rules(); + let (response, _status_code) = server.get_ranking_rules().await; let expected = json!([ "typo", @@ -106,51 +106,51 @@ fn write_all_and_update() { assert_json_eq!(expected, response, ordered: false); } -#[test] -fn send_undefined_rule() { +#[actix_rt::test] +async fn send_undefined_rule() { let mut server = common::Server::with_uid("movies"); let body = json!({ "uid": "movies", "primaryKey": "id", }); - server.create_index(body); + server.create_index(body).await; let body = json!(["typos",]); - let (_response, status_code) = server.update_ranking_rules_sync(body); + let (_response, status_code) = server.update_ranking_rules_sync(body).await; assert_eq!(status_code, 400); } -#[test] -fn send_malformed_custom_rule() { +#[actix_rt::test] +async fn send_malformed_custom_rule() { let mut server = common::Server::with_uid("movies"); let body = json!({ "uid": "movies", "primaryKey": "id", }); - server.create_index(body); + server.create_index(body).await; let body = json!(["dsc(truc)",]); - let (_response, status_code) = server.update_ranking_rules_sync(body); + let (_response, status_code) = server.update_ranking_rules_sync(body).await; assert_eq!(status_code, 400); } // Test issue https://github.com/meilisearch/MeiliSearch/issues/521 -#[test] -fn write_custom_ranking_and_index_documents() { +#[actix_rt::test] +async fn write_custom_ranking_and_index_documents() { let mut server = common::Server::with_uid("movies"); let body = json!({ "uid": "movies", "primaryKey": "id", }); - server.create_index(body); + server.create_index(body).await; // 1 - Add ranking rules with one custom ranking on a string let body = json!(["asc(title)", "typo"]); - server.update_ranking_rules(body); + server.update_ranking_rules(body).await; // 2 - Add documents @@ -167,7 +167,7 @@ fn write_custom_ranking_and_index_documents() { } ]); - server.add_or_replace_multiple_documents(body); + server.add_or_replace_multiple_documents(body).await; // 3 - Get the first document and compare @@ -177,7 +177,7 @@ fn write_custom_ranking_and_index_documents() { "author": "Exupéry" }); - let (response, status_code) = server.get_document(1); + let (response, status_code) = server.get_document(1).await; assert_eq!(status_code, 200); assert_json_eq!(response, expected, ordered: false); diff --git a/meilisearch-http/tests/settings_stop_words.rs b/meilisearch-http/tests/settings_stop_words.rs index 1adaccb2c..9204b2419 100644 --- a/meilisearch-http/tests/settings_stop_words.rs +++ b/meilisearch-http/tests/settings_stop_words.rs @@ -3,54 +3,54 @@ use serde_json::json; mod common; -#[test] -fn update_stop_words() { +#[actix_rt::test] +async fn update_stop_words() { let mut server = common::Server::with_uid("movies"); - server.populate_movies(); + server.populate_movies().await; // 1 - Get stop words - let (response, _status_code) = server.get_stop_words(); + let (response, _status_code) = server.get_stop_words().await; assert_eq!(response.as_array().unwrap().is_empty(), true); // 2 - Update stop words let body = json!(["the", "a"]); - server.update_stop_words(body.clone()); + server.update_stop_words(body.clone()).await; // 3 - Get all stop words and compare to the previous one - let (response, _status_code) = server.get_stop_words(); + let (response, _status_code) = server.get_stop_words().await; assert_json_eq!(body, response, ordered: false); // 4 - Delete all stop words - server.delete_stop_words(); + server.delete_stop_words().await; // 5 - Get all stop words and check if they are empty - let (response, _status_code) = server.get_stop_words(); + let (response, _status_code) = server.get_stop_words().await; assert_eq!(response.as_array().unwrap().is_empty(), true); } -#[test] -fn add_documents_and_stop_words() { +#[actix_rt::test] +async fn add_documents_and_stop_words() { let mut server = common::Server::with_uid("movies"); - server.populate_movies(); + server.populate_movies().await; // 2 - Update stop words let body = json!(["the", "of"]); - server.update_stop_words(body.clone()); + server.update_stop_words(body.clone()).await; // 3 - Search for a document with stop words - let (response, _status_code) = server.search("q=the%20mask"); + let (response, _status_code) = server.search("q=the%20mask").await; assert!(!response["hits"].as_array().unwrap().is_empty()); // 4 - Search for documents with *only* stop words - let (response, _status_code) = server.search("q=the%20of"); + let (response, _status_code) = server.search("q=the%20of").await; assert!(response["hits"].as_array().unwrap().is_empty()); // 5 - Delete all stop words From ff3149f6fa1d052c3dbf391bea7d1c3878726131 Mon Sep 17 00:00:00 2001 From: Quentin de Quelen Date: Thu, 16 Apr 2020 14:22:56 +0200 Subject: [PATCH 15/21] remove search multi index --- Cargo.lock | 1 - meilisearch-http/Cargo.toml | 4 +- meilisearch-http/src/lib.rs | 1 - meilisearch-http/src/routes/search.rs | 122 +------------------------- 4 files changed, 6 insertions(+), 122 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9b2f212af..bc4b3c6a5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1527,7 +1527,6 @@ dependencies = [ "once_cell", "pretty-bytes", "rand 0.7.3", - "rayon", "serde", "serde_json", "serde_qs", diff --git a/meilisearch-http/Cargo.toml b/meilisearch-http/Cargo.toml index bd6a9aa5e..ca7f3b256 100644 --- a/meilisearch-http/Cargo.toml +++ b/meilisearch-http/Cargo.toml @@ -29,7 +29,6 @@ meilisearch-tokenizer = {path = "../meilisearch-tokenizer", version = "0.10.1"} mime = "0.3.16" pretty-bytes = "0.2.2" rand = "0.7.3" -rayon = "1.3.0" serde = { version = "1.0.105", features = ["derive"] } serde_json = { version = "1.0.50", features = ["preserve_order"] } serde_qs = "0.5.2" @@ -47,13 +46,14 @@ actix-http = "1" actix-files = "0.2.1" actix-cors = "0.2.0" actix-service = "1.0.5" -tokio = { version = "0.2.18", features = ["macros", "time"] } +tokio = { version = "0.2.18", features = ["macros"] } [dev-dependencies] http-service = "0.4.0" http-service-mock = "0.4.0" tempdir = "0.3.7" once_cell = "1.3.1" +tokio = { version = "0.2.18", features = ["macros", "time"] } [dev-dependencies.assert-json-diff] git = "https://github.com/qdequele/assert-json-diff" diff --git a/meilisearch-http/src/lib.rs b/meilisearch-http/src/lib.rs index 13a239a6c..7ce65c5d9 100644 --- a/meilisearch-http/src/lib.rs +++ b/meilisearch-http/src/lib.rs @@ -33,7 +33,6 @@ pub fn create_app( .service(routes::load_html) .service(routes::load_css) .service(routes::search::search_with_url_query) - .service(routes::search::search_multi_index) .service(routes::document::get_document) .service(routes::document::get_all_documents) .wrap(helpers::Authentication::Private) diff --git a/meilisearch-http/src/routes/search.rs b/meilisearch-http/src/routes/search.rs index cd88694ff..92112c4a4 100644 --- a/meilisearch-http/src/routes/search.rs +++ b/meilisearch-http/src/routes/search.rs @@ -1,16 +1,14 @@ -use std::collections::HashMap; -use std::collections::HashSet; +use std::collections::{HashSet, HashMap}; use std::time::Duration; use log::warn; use meilisearch_core::Index; use actix_web as aweb; -use actix_web::{get, post, web}; -use rayon::iter::{IntoParallelIterator, ParallelIterator}; -use serde::{Deserialize, Serialize}; +use actix_web::{get, web}; +use serde::{Deserialize}; use crate::error::ResponseError; -use crate::helpers::meilisearch::{Error, IndexSearchExt, SearchHit, SearchResult}; +use crate::helpers::meilisearch::{Error, IndexSearchExt, SearchResult}; use crate::routes::IndexParam; use crate::Data; @@ -155,115 +153,3 @@ pub async fn search_with_url_query( Ok(web::Json(response)) } - -#[derive(Clone, Deserialize)] -#[serde(rename_all = "camelCase", deny_unknown_fields)] -pub struct SearchMultiBody { - indexes: HashSet, - query: String, - offset: Option, - limit: Option, - attributes_to_retrieve: Option>, - searchable_attributes: Option>, - attributes_to_crop: Option>, - attributes_to_highlight: Option>, - filters: Option, - timeout_ms: Option, - matches: Option, -} - -#[derive(Debug, Clone, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct SearchMultiBodyResponse { - hits: HashMap>, - offset: usize, - hits_per_page: usize, - processing_time_ms: usize, - query: String, -} - -#[post("/indexes/search")] -pub async fn search_multi_index( - data: web::Data, - body: web::Json, -) -> aweb::Result> { - let mut index_list = body.clone().indexes; - - for index in index_list.clone() { - if index == "*" { - index_list = data.db.indexes_uids().into_iter().collect(); - break; - } - } - - let mut offset = 0; - let mut count = 20; - let query = body.query.clone(); - - if let Some(body_offset) = body.offset { - if let Some(limit) = body.limit { - offset = body_offset; - count = limit; - } - } - - let par_body = body.clone(); - let responses_per_index: Vec<(String, SearchResult)> = index_list - .into_par_iter() - .map(move |index_uid| { - let index = data.db.open_index(&index_uid).unwrap(); - - let mut search_builder = index.new_search(par_body.query.clone()); - - search_builder.offset(offset); - search_builder.limit(count); - - if let Some(attributes_to_retrieve) = par_body.attributes_to_retrieve.clone() { - search_builder.attributes_to_retrieve(attributes_to_retrieve); - } - if let Some(attributes_to_crop) = par_body.attributes_to_crop.clone() { - search_builder.attributes_to_crop(attributes_to_crop); - } - if let Some(attributes_to_highlight) = par_body.attributes_to_highlight.clone() { - search_builder.attributes_to_highlight(attributes_to_highlight); - } - if let Some(filters) = par_body.filters.clone() { - search_builder.filters(filters); - } - if let Some(timeout_ms) = par_body.timeout_ms { - search_builder.timeout(Duration::from_millis(timeout_ms)); - } - if let Some(matches) = par_body.matches { - if matches { - search_builder.get_matches(); - } - } - - let reader = data.db.main_read_txn().unwrap(); - let response = search_builder.search(&reader).unwrap(); - - (index_uid, response) - }) - .collect(); - - let mut hits_map = HashMap::new(); - - let mut max_query_time = 0; - - for (index_uid, response) in responses_per_index { - if response.processing_time_ms > max_query_time { - max_query_time = response.processing_time_ms; - } - hits_map.insert(index_uid, response.hits); - } - - let response = SearchMultiBodyResponse { - hits: hits_map, - offset, - hits_per_page: count, - processing_time_ms: max_query_time, - query, - }; - - Ok(web::Json(response)) -} From 4bd7e46ba6051295d4faee8bf2cfcf1917d1307a Mon Sep 17 00:00:00 2001 From: Quentin de Quelen Date: Thu, 16 Apr 2020 18:37:54 +0200 Subject: [PATCH 16/21] revert get document method --- meilisearch-core/examples/from_file.rs | 2 +- meilisearch-core/src/database.rs | 16 ++++++++-------- meilisearch-core/src/store/mod.rs | 6 +++--- meilisearch-http/src/helpers/meilisearch.rs | 2 +- meilisearch-http/src/routes/document.rs | 12 ++++++------ 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/meilisearch-core/examples/from_file.rs b/meilisearch-core/examples/from_file.rs index 4312cd2af..2a30d1ce4 100644 --- a/meilisearch-core/examples/from_file.rs +++ b/meilisearch-core/examples/from_file.rs @@ -381,7 +381,7 @@ fn search_command(command: SearchCommand, database: Database) -> Result<(), Box< .sort_unstable_by_key(|m| (m.char_index, m.char_length)); let start_retrieve = Instant::now(); - let result = index.document::(&reader, Some(fields.clone()), doc.id); + let result = index.document::(&reader, Some(&fields), doc.id); retrieve_duration += start_retrieve.elapsed(); match result { diff --git a/meilisearch-core/src/database.rs b/meilisearch-core/src/database.rs index b2fdfce3a..87fc24c11 100644 --- a/meilisearch-core/src/database.rs +++ b/meilisearch-core/src/database.rs @@ -756,16 +756,16 @@ mod tests { update_reader.abort(); let reader = db.main_read_txn().unwrap(); - let document: Option = index.document::<_, String>(&reader, None, DocumentId(25)).unwrap(); + let document: Option = index.document(&reader, None, DocumentId(25)).unwrap(); assert!(document.is_none()); let document: Option = index - .document::<_, String>(&reader, None, DocumentId(7_900_334_843_754_999_545)) + .document(&reader, None, DocumentId(7_900_334_843_754_999_545)) .unwrap(); assert!(document.is_some()); let document: Option = index - .document::<_, String>(&reader, None, DocumentId(8_367_468_610_878_465_872)) + .document(&reader, None, DocumentId(8_367_468_610_878_465_872)) .unwrap(); assert!(document.is_some()); } @@ -836,16 +836,16 @@ mod tests { update_reader.abort(); let reader = db.main_read_txn().unwrap(); - let document: Option = index.document::<_, String>(&reader, None, DocumentId(25)).unwrap(); + let document: Option = index.document(&reader, None, DocumentId(25)).unwrap(); assert!(document.is_none()); let document: Option = index - .document::<_, String>(&reader, None, DocumentId(7_900_334_843_754_999_545)) + .document(&reader, None, DocumentId(7_900_334_843_754_999_545)) .unwrap(); assert!(document.is_some()); let document: Option = index - .document::<_, String>(&reader, None, DocumentId(8_367_468_610_878_465_872)) + .document(&reader, None, DocumentId(8_367_468_610_878_465_872)) .unwrap(); assert!(document.is_some()); @@ -882,7 +882,7 @@ mod tests { let reader = db.main_read_txn().unwrap(); let document: Option = index - .document::<_, String>(&reader, None, DocumentId(7_900_334_843_754_999_545)) + .document(&reader, None, DocumentId(7_900_334_843_754_999_545)) .unwrap(); let new_doc1 = serde_json::json!({ @@ -893,7 +893,7 @@ mod tests { assert_eq!(document, Some(new_doc1)); let document: Option = index - .document::<_, String>(&reader, None, DocumentId(8_367_468_610_878_465_872)) + .document(&reader, None, DocumentId(8_367_468_610_878_465_872)) .unwrap(); let new_doc2 = serde_json::json!({ diff --git a/meilisearch-core/src/store/mod.rs b/meilisearch-core/src/store/mod.rs index 342f6c266..48bcfcef8 100644 --- a/meilisearch-core/src/store/mod.rs +++ b/meilisearch-core/src/store/mod.rs @@ -214,17 +214,17 @@ pub struct Index { } impl Index { - pub fn document>( + pub fn document( &self, reader: &heed::RoTxn, - attributes: Option>, + attributes: Option<&HashSet<&str>>, document_id: DocumentId, ) -> MResult> { let schema = self.main.schema(reader)?; let schema = schema.ok_or(Error::SchemaMissing)?; let attributes = match attributes { - Some(attributes) => Some(attributes.iter().filter_map(|name| schema.id(name.as_ref())).collect()), + Some(attributes) => Some(attributes.iter().filter_map(|name| schema.id(*name)).collect()), None => None, }; diff --git a/meilisearch-http/src/helpers/meilisearch.rs b/meilisearch-http/src/helpers/meilisearch.rs index d75fdcced..5b0563ae0 100644 --- a/meilisearch-http/src/helpers/meilisearch.rs +++ b/meilisearch-http/src/helpers/meilisearch.rs @@ -257,7 +257,7 @@ impl<'a> SearchBuilder<'a> { for doc in docs { let mut document: IndexMap = self .index - .document(reader, Some(all_attributes.clone()), doc.id) + .document(reader, Some(&all_attributes), doc.id) .map_err(|e| Error::RetrieveDocument(doc.id.0, e.to_string()))? .ok_or(Error::DocumentNotFound(doc.id.0))?; diff --git a/meilisearch-http/src/routes/document.rs b/meilisearch-http/src/routes/document.rs index c7dc20bc0..54183108f 100644 --- a/meilisearch-http/src/routes/document.rs +++ b/meilisearch-http/src/routes/document.rs @@ -1,3 +1,4 @@ +use std::collections::HashSet; use std::collections::BTreeSet; use actix_web as aweb; @@ -35,7 +36,7 @@ pub async fn get_document( .map_err(|err| ResponseError::Internal(err.to_string()))?; let response = index - .document::(&reader, None, document_id) + .document::(&reader, None, document_id) .map_err(|_| ResponseError::DocumentNotFound(path.document_id.clone()))? .ok_or(ResponseError::DocumentNotFound(path.document_id.clone()))?; @@ -109,14 +110,13 @@ pub async fn get_all_documents( let documents_ids = documents_ids.map_err(|err| ResponseError::Internal(err.to_string()))?; - let attributes = params - .attributes_to_retrieve - .clone() - .map(|a| a.split(',').map(|a| a.to_string()).collect()); + let attributes: Option> = params + .attributes_to_retrieve.as_ref() + .map(|a| a.split(',').collect()); let mut response = Vec::::new(); for document_id in documents_ids { - if let Ok(Some(document)) = index.document(&reader, attributes.clone(), document_id) { + if let Ok(Some(document)) = index.document(&reader, attributes.as_ref(), document_id) { response.push(document); } } From e74d2c187261814ec2d2c4d4982663fb1dbd8a44 Mon Sep 17 00:00:00 2001 From: Quentin de Quelen Date: Fri, 17 Apr 2020 14:52:13 +0200 Subject: [PATCH 17/21] simplify error handling by impl errors traits on ResponseError --- meilisearch-http/src/error.rs | 176 +++++++++-- meilisearch-http/src/helpers/meilisearch.rs | 110 ++----- meilisearch-http/src/routes/document.rs | 158 ++++------ meilisearch-http/src/routes/health.rs | 42 +-- meilisearch-http/src/routes/index.rs | 235 +++++--------- meilisearch-http/src/routes/search.rs | 32 +- meilisearch-http/src/routes/setting.rs | 331 ++++++-------------- meilisearch-http/src/routes/stats.rs | 63 +--- meilisearch-http/src/routes/stop_words.rs | 61 ++-- meilisearch-http/src/routes/synonym.rs | 69 ++-- 10 files changed, 470 insertions(+), 807 deletions(-) diff --git a/meilisearch-http/src/error.rs b/meilisearch-http/src/error.rs index ef2a910d5..e05853062 100644 --- a/meilisearch-http/src/error.rs +++ b/meilisearch-http/src/error.rs @@ -7,39 +7,109 @@ use serde_json::json; #[derive(Debug)] pub enum ResponseError { - Internal(String), - BadRequest(String), - MissingAuthorizationHeader, - InvalidToken(String), - NotFound(String), - IndexNotFound(String), - DocumentNotFound(String), - MissingHeader(String), - FilterParsing(String), BadParameter(String, String), - OpenIndex(String), + BadRequest(String), CreateIndex(String), + DocumentNotFound(String), + IndexNotFound(String), + Internal(String), InvalidIndexUid, + InvalidToken(String), Maintenance, + MissingAuthorizationHeader, + MissingFilterValue, + MissingHeader(String), + NotFound(String), + OpenIndex(String), + FilterParsing(String), + RetrieveDocument(u64, String), + SearchDocuments(String), + UnknownFilteredAttribute, +} + +impl ResponseError { + pub fn internal(err: impl fmt::Display) -> ResponseError { + ResponseError::Internal(err.to_string()) + } + + pub fn bad_request(err: impl fmt::Display) -> ResponseError { + ResponseError::BadRequest(err.to_string()) + } + + pub fn missing_authorization_header() -> ResponseError { + ResponseError::MissingAuthorizationHeader + } + + pub fn invalid_token(err: impl fmt::Display) -> ResponseError { + ResponseError::InvalidToken(err.to_string()) + } + + pub fn not_found(err: impl fmt::Display) -> ResponseError { + ResponseError::NotFound(err.to_string()) + } + + pub fn index_not_found(err: impl fmt::Display) -> ResponseError { + ResponseError::IndexNotFound(err.to_string()) + } + + pub fn document_not_found(err: impl fmt::Display) -> ResponseError { + ResponseError::DocumentNotFound(err.to_string()) + } + + pub fn missing_header(err: impl fmt::Display) -> ResponseError { + ResponseError::MissingHeader(err.to_string()) + } + + pub fn bad_parameter(param: impl fmt::Display, err: impl fmt::Display) -> ResponseError { + ResponseError::BadParameter(param.to_string(), err.to_string()) + } + + pub fn open_index(err: impl fmt::Display) -> ResponseError { + ResponseError::OpenIndex(err.to_string()) + } + + pub fn create_index(err: impl fmt::Display) -> ResponseError { + ResponseError::CreateIndex(err.to_string()) + } + + pub fn invalid_index_uid() -> ResponseError { + ResponseError::InvalidIndexUid + } + + pub fn maintenance() -> ResponseError { + ResponseError::Maintenance + } + + pub fn retrieve_document(doc_id: u64, err: impl fmt::Display) -> ResponseError { + ResponseError::RetrieveDocument(doc_id, err.to_string()) + } + + pub fn search_documents(err: impl fmt::Display) -> ResponseError { + ResponseError::SearchDocuments(err.to_string()) + } } impl fmt::Display for ResponseError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::Internal(err) => write!(f, "{}", err), - Self::BadRequest(err) => write!(f, "{}", err), - Self::MissingAuthorizationHeader => write!(f, "You must have an authorization token"), - Self::InvalidToken(err) => write!(f, "Invalid API key: {}", err), - Self::NotFound(err) => write!(f, "{} not found", err), - Self::IndexNotFound(index_uid) => write!(f, "Index {} not found", index_uid), - Self::DocumentNotFound(document_id) => write!(f, "Document with id {} not found", document_id), - Self::MissingHeader(header) => write!(f, "Header {} is missing", header), Self::BadParameter(param, err) => write!(f, "Url parameter {} error: {}", param, err), - Self::OpenIndex(err) => write!(f, "Impossible to open index; {}", err), + Self::BadRequest(err) => write!(f, "{}", err), Self::CreateIndex(err) => write!(f, "Impossible to create index; {}", err), - Self::InvalidIndexUid => write!(f, "Index must have a valid uid; Index uid can be of type integer or string only composed of alphanumeric characters, hyphens (-) and underscores (_)."), - Self::Maintenance => write!(f, "Server is in maintenance, please try again later"), + Self::DocumentNotFound(document_id) => write!(f, "Document with id {} not found", document_id), + Self::IndexNotFound(index_uid) => write!(f, "Index {} not found", index_uid), + Self::Internal(err) => write!(f, "{}", err), + Self::InvalidIndexUid => f.write_str("Index must have a valid uid; Index uid can be of type integer or string only composed of alphanumeric characters, hyphens (-) and underscores (_)."), + Self::InvalidToken(err) => write!(f, "Invalid API key: {}", err), + Self::Maintenance => f.write_str("Server is in maintenance, please try again later"), Self::FilterParsing(err) => write!(f, "parsing error: {}", err), + Self::MissingAuthorizationHeader => f.write_str("You must have an authorization token"), + Self::MissingFilterValue => f.write_str("a filter doesn't have a value to compare it with"), + Self::MissingHeader(header) => write!(f, "Header {} is missing", header), + Self::NotFound(err) => write!(f, "{} not found", err), + Self::OpenIndex(err) => write!(f, "Impossible to open index; {}", err), + Self::RetrieveDocument(id, err) => write!(f, "impossible to retrieve the document with id: {}; {}", id, err), + Self::SearchDocuments(err) => write!(f, "impossible to search documents; {}", err), + Self::UnknownFilteredAttribute => f.write_str("a filter is specifying an unknown schema attribute"), } } } @@ -53,20 +123,66 @@ impl aweb::error::ResponseError for ResponseError { fn status_code(&self) -> StatusCode { match *self { - Self::Internal(_) => StatusCode::INTERNAL_SERVER_ERROR, - Self::BadRequest(_) => StatusCode::BAD_REQUEST, - Self::MissingAuthorizationHeader => StatusCode::FORBIDDEN, - Self::InvalidToken(_) => StatusCode::UNAUTHORIZED, - Self::NotFound(_) => StatusCode::NOT_FOUND, - Self::IndexNotFound(_) => StatusCode::NOT_FOUND, - Self::DocumentNotFound(_) => StatusCode::NOT_FOUND, - Self::MissingHeader(_) => StatusCode::UNAUTHORIZED, Self::BadParameter(_, _) => StatusCode::BAD_REQUEST, - Self::OpenIndex(_) => StatusCode::BAD_REQUEST, + Self::BadRequest(_) => StatusCode::BAD_REQUEST, Self::CreateIndex(_) => StatusCode::BAD_REQUEST, + Self::DocumentNotFound(_) => StatusCode::NOT_FOUND, + Self::IndexNotFound(_) => StatusCode::NOT_FOUND, + Self::Internal(_) => StatusCode::INTERNAL_SERVER_ERROR, Self::InvalidIndexUid => StatusCode::BAD_REQUEST, + Self::InvalidToken(_) => StatusCode::UNAUTHORIZED, Self::Maintenance => StatusCode::SERVICE_UNAVAILABLE, Self::FilterParsing(_) => StatusCode::BAD_REQUEST, + Self::MissingAuthorizationHeader => StatusCode::FORBIDDEN, + Self::MissingFilterValue => StatusCode::BAD_REQUEST, + Self::MissingHeader(_) => StatusCode::UNAUTHORIZED, + Self::NotFound(_) => StatusCode::NOT_FOUND, + Self::OpenIndex(_) => StatusCode::BAD_REQUEST, + Self::RetrieveDocument(_, _) => StatusCode::BAD_REQUEST, + Self::SearchDocuments(_) => StatusCode::BAD_REQUEST, + Self::UnknownFilteredAttribute => StatusCode::BAD_REQUEST, } } } + +impl From for ResponseError { + fn from(err: meilisearch_core::HeedError) -> ResponseError { + ResponseError::Internal(err.to_string()) + } +} + +impl From for ResponseError { + fn from(err: meilisearch_core::FstError) -> ResponseError { + ResponseError::Internal(err.to_string()) + } +} + +impl From for ResponseError { + fn from(err: meilisearch_core::Error) -> ResponseError { + use meilisearch_core::pest_error::LineColLocation::*; + match err { + meilisearch_core::Error::FilterParseError(e) => { + let (line, column) = match e.line_col { + Span((line, _), (column, _)) => (line, column), + Pos((line, column)) => (line, column), + }; + let message = format!("parsing error on line {} at column {}: {}", line, column, e.variant.message()); + + ResponseError::FilterParsing(message) + }, + _ => ResponseError::Internal(err.to_string()), + } + } +} + +impl From for ResponseError { + fn from(err: meilisearch_schema::Error) -> ResponseError { + ResponseError::Internal(err.to_string()) + } +} + +impl From for ResponseError { + fn from(err: actix_http::Error) -> ResponseError { + ResponseError::Internal(err.to_string()) + } +} diff --git a/meilisearch-http/src/helpers/meilisearch.rs b/meilisearch-http/src/helpers/meilisearch.rs index 5b0563ae0..2cd9d3379 100644 --- a/meilisearch-http/src/helpers/meilisearch.rs +++ b/meilisearch-http/src/helpers/meilisearch.rs @@ -1,8 +1,5 @@ use std::cmp::Ordering; use std::collections::{HashMap, HashSet}; -use std::convert::From; -use std::error; -use std::fmt; use std::hash::{Hash, Hasher}; use std::time::{Duration, Instant}; @@ -19,74 +16,7 @@ use serde_json::Value; use siphasher::sip::SipHasher; use slice_group_by::GroupBy; -#[derive(Debug)] -pub enum Error { - SearchDocuments(String), - RetrieveDocument(u64, String), - DocumentNotFound(u64), - CropFieldWrongType(String), - FilterParsing(String), - AttributeNotFoundOnDocument(String), - AttributeNotFoundOnSchema(String), - MissingFilterValue, - UnknownFilteredAttribute, - Internal(String), -} - -impl error::Error for Error {} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use Error::*; - - match self { - SearchDocuments(err) => write!(f, "impossible to search documents; {}", err), - RetrieveDocument(id, err) => write!( - f, - "impossible to retrieve the document with id: {}; {}", - id, err - ), - DocumentNotFound(id) => write!(f, "document {} not found", id), - CropFieldWrongType(field) => { - write!(f, "the field {} cannot be cropped it's not a string", field) - } - AttributeNotFoundOnDocument(field) => { - write!(f, "field {} is not found on document", field) - } - AttributeNotFoundOnSchema(field) => write!(f, "field {} is not found on schema", field), - MissingFilterValue => f.write_str("a filter doesn't have a value to compare it with"), - UnknownFilteredAttribute => { - f.write_str("a filter is specifying an unknown schema attribute") - } - Internal(err) => write!(f, "internal error; {}", err), - FilterParsing(err) => write!(f, "filter parsing error: {}", err), - } - } -} - -impl From for Error { - fn from(error: meilisearch_core::Error) -> Self { - use meilisearch_core::pest_error::LineColLocation::*; - match error { - meilisearch_core::Error::FilterParseError(e) => { - let (line, column) = match e.line_col { - Span((line, _), (column, _)) => (line, column), - Pos((line, column)) => (line, column), - }; - let message = format!("parsing error on line {} at column {}: {}", line, column, e.variant.message()); - - Error::FilterParsing(message) - }, - _ => Error::Internal(error.to_string()), - } - } -} - -impl From for Error { - fn from(error: heed::Error) -> Self { - Error::Internal(error.to_string()) - } -} +use crate::error::ResponseError; pub trait IndexSearchExt { fn new_search(&self, query: String) -> SearchBuilder; @@ -169,17 +99,14 @@ impl<'a> SearchBuilder<'a> { self } - pub fn search(&self, reader: &heed::RoTxn) -> Result { - let schema = self.index.main.schema(reader); - let schema = schema.map_err(|e| Error::Internal(e.to_string()))?; - let schema = match schema { - Some(schema) => schema, - None => return Err(Error::Internal(String::from("missing schema"))), - }; + pub fn search(&self, reader: &heed::RoTxn) -> Result { + let schema = self + .index + .main + .schema(reader)? + .ok_or(ResponseError::internal("missing schema"))?; - let ranked_map = self.index.main.ranked_map(reader); - let ranked_map = ranked_map.map_err(|e| Error::Internal(e.to_string()))?; - let ranked_map = ranked_map.unwrap_or_default(); + let ranked_map = self.index.main.ranked_map(reader)?.unwrap_or_default(); // Change criteria let mut query_builder = match self.get_criteria(reader, &ranked_map, &schema)? { @@ -221,9 +148,8 @@ impl<'a> SearchBuilder<'a> { } let start = Instant::now(); - let result = - query_builder.query(reader, &self.query, self.offset..(self.offset + self.limit)); - let (docs, nb_hits) = result.map_err(|e| Error::SearchDocuments(e.to_string()))?; + let result = query_builder.query(reader, &self.query, self.offset..(self.offset + self.limit)); + let (docs, nb_hits) = result.map_err(ResponseError::search_documents)?; let time_ms = start.elapsed().as_millis() as usize; let mut all_attributes: HashSet<&str> = HashSet::new(); @@ -255,11 +181,19 @@ impl<'a> SearchBuilder<'a> { let mut hits = Vec::with_capacity(self.limit); for doc in docs { + // retrieve the content of document in kv store + let attributes: Option> = self + .attributes_to_retrieve + .as_ref() + .map(|a| a.iter().map(|a| a.as_str()).collect()); + let mut document: IndexMap = self .index - .document(reader, Some(&all_attributes), doc.id) - .map_err(|e| Error::RetrieveDocument(doc.id.0, e.to_string()))? - .ok_or(Error::DocumentNotFound(doc.id.0))?; + .document(reader, attributes.as_ref(), doc.id) + .map_err(|e| ResponseError::retrieve_document(doc.id.0, e))? + .ok_or(ResponseError::internal( + "Impossible to retrieve a document id returned by the engine", + ))?; let mut formatted = document.iter() .filter(|(key, _)| all_formatted.contains(key.as_str())) @@ -320,7 +254,7 @@ impl<'a> SearchBuilder<'a> { reader: &heed::RoTxn, ranked_map: &'a RankedMap, schema: &Schema, - ) -> Result>, Error> { + ) -> Result>, ResponseError> { let ranking_rules = self.index.main.ranking_rules(reader)?; if let Some(ranking_rules) = ranking_rules { diff --git a/meilisearch-http/src/routes/document.rs b/meilisearch-http/src/routes/document.rs index 54183108f..27407fa4d 100644 --- a/meilisearch-http/src/routes/document.rs +++ b/meilisearch-http/src/routes/document.rs @@ -1,7 +1,5 @@ -use std::collections::HashSet; -use std::collections::BTreeSet; +use std::collections::{BTreeSet, HashSet}; -use actix_web as aweb; use actix_web::{delete, get, post, put, web, HttpResponse}; use indexmap::IndexMap; use serde::Deserialize; @@ -23,22 +21,18 @@ pub struct DocumentParam { pub async fn get_document( data: web::Data, path: web::Path, -) -> aweb::Result { +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; - let document_id = meilisearch_core::serde::compute_document_id(path.document_id.clone()); + .ok_or(ResponseError::index_not_found(&path.index_uid))?; + let document_id = meilisearch_core::serde::compute_document_id(&path.document_id); - let reader = data - .db - .main_read_txn() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + let reader = data.db.main_read_txn()?; let response = index - .document::(&reader, None, document_id) - .map_err(|_| ResponseError::DocumentNotFound(path.document_id.clone()))? - .ok_or(ResponseError::DocumentNotFound(path.document_id.clone()))?; + .document::(&reader, None, document_id)? + .ok_or(ResponseError::document_not_found(&path.document_id))?; Ok(HttpResponse::Ok().json(response)) } @@ -47,28 +41,21 @@ pub async fn get_document( pub async fn delete_document( data: web::Data, path: web::Path, -) -> aweb::Result { +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; - let document_id = meilisearch_core::serde::compute_document_id(path.document_id.clone()); + .ok_or(ResponseError::index_not_found(&path.index_uid))?; + let document_id = meilisearch_core::serde::compute_document_id(&path.document_id); - let mut update_writer = data - .db - .update_write_txn() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + let mut update_writer = data.db.update_write_txn()?; let mut documents_deletion = index.documents_deletion(); documents_deletion.delete_document_by_id(document_id); - let update_id = documents_deletion - .finalize(&mut update_writer) - .map_err(|err| ResponseError::Internal(err.to_string()))?; + let update_id = documents_deletion.finalize(&mut update_writer)?; - update_writer - .commit() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + update_writer.commit()?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -86,32 +73,29 @@ pub async fn get_all_documents( data: web::Data, path: web::Path, params: web::Query, -) -> aweb::Result { +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; + .ok_or(ResponseError::index_not_found(&path.index_uid))?; let offset = params.offset.unwrap_or(0); let limit = params.limit.unwrap_or(20); - let reader = data - .db - .main_read_txn() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + let reader = data.db.main_read_txn()?; let documents_ids: Result, _> = index .documents_fields_counts - .documents_ids(&reader) - .map_err(|_| ResponseError::Internal(path.index_uid.clone()))? + .documents_ids(&reader)? .skip(offset) .take(limit) .collect(); - let documents_ids = documents_ids.map_err(|err| ResponseError::Internal(err.to_string()))?; + let documents_ids = documents_ids?; let attributes: Option> = params - .attributes_to_retrieve.as_ref() + .attributes_to_retrieve + .as_ref() .map(|a| a.split(',').collect()); let mut response = Vec::::new(); @@ -145,48 +129,33 @@ async fn update_multiple_documents( params: web::Query, body: web::Json>, is_partial: bool, -) -> aweb::Result { +) -> Result { let index = data .db - .open_index(path.index_uid.clone()) - .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; + .open_index(&path.index_uid) + .ok_or(ResponseError::index_not_found(&path.index_uid))?; - let reader = data - .db - .main_read_txn() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + let reader = data.db.main_read_txn()?; let mut schema = index .main - .schema(&reader) - .map_err(|err| ResponseError::Internal(err.to_string()))? - .ok_or(ResponseError::Internal( - "Impossible to retrieve the schema".to_string(), - ))?; + .schema(&reader)? + .ok_or(ResponseError::internal("Impossible to retrieve the schema"))?; if schema.primary_key().is_none() { - let id = match params.primary_key.clone() { - Some(id) => id, - None => body.first().and_then(|docs| find_primary_key(docs)).ok_or( - ResponseError::BadRequest("Could not infer a primary key".to_string()), - )?, + let id = match ¶ms.primary_key { + Some(id) => id.to_string(), + None => body + .first() + .and_then(find_primary_key) + .ok_or(ResponseError::bad_request("Could not infer a primary key"))?, }; - let mut writer = data - .db - .main_write_txn() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + let mut writer = data.db.main_write_txn()?; - schema - .set_primary_key(&id) - .map_err(|e| ResponseError::Internal(e.to_string()))?; - index - .main - .put_schema(&mut writer, &schema) - .map_err(|e| ResponseError::Internal(e.to_string()))?; - writer - .commit() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + schema.set_primary_key(&id)?; + index.main.put_schema(&mut writer, &schema)?; + writer.commit()?; } let mut document_addition = if is_partial { @@ -199,16 +168,9 @@ async fn update_multiple_documents( document_addition.update_document(document); } - let mut update_writer = data - .db - .update_write_txn() - .map_err(|err| ResponseError::Internal(err.to_string()))?; - let update_id = document_addition - .finalize(&mut update_writer) - .map_err(|e| ResponseError::Internal(e.to_string()))?; - update_writer - .commit() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + let mut update_writer = data.db.update_write_txn()?; + let update_id = document_addition.finalize(&mut update_writer)?; + update_writer.commit()?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -219,7 +181,7 @@ pub async fn add_documents( path: web::Path, params: web::Query, body: web::Json>, -) -> aweb::Result { +) -> Result { update_multiple_documents(data, path, params, body, false).await } @@ -229,7 +191,7 @@ pub async fn update_documents( path: web::Path, params: web::Query, body: web::Json>, -) -> aweb::Result { +) -> Result { update_multiple_documents(data, path, params, body, true).await } @@ -238,16 +200,13 @@ pub async fn delete_documents( data: web::Data, path: web::Path, body: web::Json>, -) -> aweb::Result { +) -> Result { let index = data .db - .open_index(path.index_uid.clone()) - .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; + .open_index(&path.index_uid) + .ok_or(ResponseError::index_not_found(&path.index_uid))?; - let mut writer = data - .db - .update_write_txn() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + let mut writer = data.db.update_write_txn()?; let mut documents_deletion = index.documents_deletion(); @@ -258,13 +217,9 @@ pub async fn delete_documents( } } - let update_id = documents_deletion - .finalize(&mut writer) - .map_err(|e| ResponseError::Internal(e.to_string()))?; + let update_id = documents_deletion.finalize(&mut writer)?; - writer - .commit() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + writer.commit()?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -273,24 +228,17 @@ pub async fn delete_documents( pub async fn clear_all_documents( data: web::Data, path: web::Path, -) -> aweb::Result { +) -> Result { let index = data .db - .open_index(path.index_uid.clone()) - .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; + .open_index(&path.index_uid) + .ok_or(ResponseError::index_not_found(&path.index_uid))?; - let mut writer = data - .db - .update_write_txn() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + let mut writer = data.db.update_write_txn()?; - let update_id = index - .clear_all(&mut writer) - .map_err(|e| ResponseError::Internal(e.to_string()))?; + let update_id = index.clear_all(&mut writer)?; - writer - .commit() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + writer.commit()?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } diff --git a/meilisearch-http/src/routes/health.rs b/meilisearch-http/src/routes/health.rs index 8b4433303..825d79ef5 100644 --- a/meilisearch-http/src/routes/health.rs +++ b/meilisearch-http/src/routes/health.rs @@ -1,6 +1,5 @@ use crate::error::ResponseError; use crate::Data; -use actix_web as aweb; use actix_web::{get, put, web, HttpResponse}; use heed::types::{Str, Unit}; use serde::Deserialize; @@ -8,49 +7,32 @@ use serde::Deserialize; const UNHEALTHY_KEY: &str = "_is_unhealthy"; #[get("/health")] -pub async fn get_health(data: web::Data) -> aweb::Result { - let reader = data - .db - .main_read_txn() - .map_err(|err| ResponseError::Internal(err.to_string()))?; +pub async fn get_health(data: web::Data) -> Result { + let reader = data.db.main_read_txn()?; let common_store = data.db.common_store(); if let Ok(Some(_)) = common_store.get::<_, Str, Unit>(&reader, UNHEALTHY_KEY) { - return Err(ResponseError::Maintenance.into()); + return Err(ResponseError::Maintenance); } Ok(HttpResponse::Ok().finish()) } -pub async fn set_healthy(data: web::Data) -> aweb::Result { - let mut writer = data - .db - .main_write_txn() - .map_err(|err| ResponseError::Internal(err.to_string()))?; +pub async fn set_healthy(data: web::Data) -> Result { + let mut writer = data.db.main_write_txn()?; let common_store = data.db.common_store(); - common_store - .delete::<_, Str>(&mut writer, UNHEALTHY_KEY) - .map_err(|e| ResponseError::Internal(e.to_string()))?; - writer - .commit() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + common_store.delete::<_, Str>(&mut writer, UNHEALTHY_KEY)?; + writer.commit()?; Ok(HttpResponse::Ok().finish()) } -pub async fn set_unhealthy(data: web::Data) -> aweb::Result { - let mut writer = data - .db - .main_write_txn() - .map_err(|err| ResponseError::Internal(err.to_string()))?; +pub async fn set_unhealthy(data: web::Data) -> Result { + let mut writer = data.db.main_write_txn()?; let common_store = data.db.common_store(); - common_store - .put::<_, Str, Unit>(&mut writer, UNHEALTHY_KEY, &()) - .map_err(|e| ResponseError::Internal(e.to_string()))?; - writer - .commit() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + common_store.put::<_, Str, Unit>(&mut writer, UNHEALTHY_KEY, &())?; + writer.commit()?; Ok(HttpResponse::Ok().finish()) } @@ -64,7 +46,7 @@ pub struct HealtBody { pub async fn change_healthyness( data: web::Data, body: web::Json, -) -> aweb::Result { +) -> Result { if body.health { set_healthy(data).await } else { diff --git a/meilisearch-http/src/routes/index.rs b/meilisearch-http/src/routes/index.rs index 8e6dac8ca..d46160e76 100644 --- a/meilisearch-http/src/routes/index.rs +++ b/meilisearch-http/src/routes/index.rs @@ -1,4 +1,3 @@ -use actix_web as aweb; use actix_web::{delete, get, post, put, web, HttpResponse}; use chrono::{DateTime, Utc}; use log::error; @@ -29,11 +28,8 @@ pub struct IndexResponse { } #[get("/indexes")] -pub async fn list_indexes(data: web::Data) -> aweb::Result { - let reader = data - .db - .main_read_txn() - .map_err(|err| ResponseError::Internal(err.to_string()))?; +pub async fn list_indexes(data: web::Data) -> Result { + let reader = data.db.main_read_txn()?; let mut response = Vec::new(); @@ -42,26 +38,20 @@ pub async fn list_indexes(data: web::Data) -> aweb::Result { match index { Some(index) => { - let name = index - .main - .name(&reader) - .map_err(|e| ResponseError::Internal(e.to_string()))? - .ok_or(ResponseError::Internal( - "Impossible to get the name of an index".to_string(), - ))?; + let name = index.main.name(&reader)?.ok_or(ResponseError::internal( + "Impossible to get the name of an index", + ))?; let created_at = index .main - .created_at(&reader) - .map_err(|e| ResponseError::Internal(e.to_string()))? - .ok_or(ResponseError::Internal( - "Impossible to get the create date of an index".to_string(), + .created_at(&reader)? + .ok_or(ResponseError::internal( + "Impossible to get the create date of an index", ))?; let updated_at = index .main - .updated_at(&reader) - .map_err(|e| ResponseError::Internal(e.to_string()))? - .ok_or(ResponseError::Internal( - "Impossible to get the last update date of an index".to_string(), + .updated_at(&reader)? + .ok_or(ResponseError::internal( + "Impossible to get the last update date of an index", ))?; let primary_key = match index.main.schema(&reader) { @@ -95,37 +85,28 @@ pub async fn list_indexes(data: web::Data) -> aweb::Result { pub async fn get_index( data: web::Data, path: web::Path, -) -> aweb::Result { +) -> Result { let index = data .db - .open_index(path.index_uid.clone()) - .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; + .open_index(&path.index_uid) + .ok_or(ResponseError::index_not_found(&path.index_uid))?; - let reader = data - .db - .main_read_txn() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + let reader = data.db.main_read_txn()?; - let name = index - .main - .name(&reader) - .map_err(|e| ResponseError::Internal(e.to_string()))? - .ok_or(ResponseError::Internal( - "Impossible to get the name of an index".to_string(), - ))?; + let name = index.main.name(&reader)?.ok_or(ResponseError::internal( + "Impossible to get the name of an index", + ))?; let created_at = index .main - .created_at(&reader) - .map_err(|e| ResponseError::Internal(e.to_string()))? - .ok_or(ResponseError::Internal( - "Impossible to get the create date of an index".to_string(), + .created_at(&reader)? + .ok_or(ResponseError::internal( + "Impossible to get the create date of an index", ))?; let updated_at = index .main - .updated_at(&reader) - .map_err(|e| ResponseError::Internal(e.to_string()))? - .ok_or(ResponseError::Internal( - "Impossible to get the last update date of an index".to_string(), + .updated_at(&reader)? + .ok_or(ResponseError::internal( + "Impossible to get the last update date of an index", ))?; let primary_key = match index.main.schema(&reader) { @@ -157,11 +138,11 @@ pub struct IndexCreateRequest { pub async fn create_index( data: web::Data, body: web::Json, -) -> aweb::Result { +) -> Result { if let (None, None) = (body.name.clone(), body.uid.clone()) { - return Err( - ResponseError::BadRequest("Index creation must have an uid".to_string()).into(), - ); + return Err(ResponseError::bad_request( + "Index creation must have an uid", + )); } let uid = match body.uid.clone() { @@ -172,7 +153,7 @@ pub async fn create_index( { uid } else { - return Err(ResponseError::InvalidIndexUid.into()); + return Err(ResponseError::InvalidIndexUid); } } None => loop { @@ -186,50 +167,33 @@ pub async fn create_index( let created_index = data .db .create_index(&uid) - .map_err(|e| ResponseError::CreateIndex(e.to_string()))?; + .map_err(ResponseError::create_index)?; - let mut writer = data - .db - .main_write_txn() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + let mut writer = data.db.main_write_txn()?; let name = body.name.clone().unwrap_or(uid.clone()); - created_index - .main - .put_name(&mut writer, &name) - .map_err(|e| ResponseError::Internal(e.to_string()))?; + created_index.main.put_name(&mut writer, &name)?; let created_at = created_index .main - .created_at(&writer) - .map_err(|e| ResponseError::Internal(e.to_string()))? - .ok_or(ResponseError::Internal("".to_string()))?; + .created_at(&writer)? + .ok_or(ResponseError::internal("Impossible to read created at"))?; let updated_at = created_index .main - .updated_at(&writer) - .map_err(|e| ResponseError::Internal(e.to_string()))? - .ok_or(ResponseError::Internal("".to_string()))?; + .updated_at(&writer)? + .ok_or(ResponseError::internal("Impossible to read updated at"))?; if let Some(id) = body.primary_key.clone() { - if let Some(mut schema) = created_index - .main - .schema(&writer) - .map_err(|e| ResponseError::Internal(e.to_string()))? - { + if let Some(mut schema) = created_index.main.schema(&writer)? { schema .set_primary_key(&id) - .map_err(|e| ResponseError::BadRequest(e.to_string()))?; - created_index - .main - .put_schema(&mut writer, &schema) - .map_err(|e| ResponseError::Internal(e.to_string()))?; + .map_err(ResponseError::bad_request)?; + created_index.main.put_schema(&mut writer, &schema)?; } } - writer - .commit() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + writer.commit()?; Ok(HttpResponse::Created().json(IndexResponse { name, @@ -262,83 +226,53 @@ pub async fn update_index( data: web::Data, path: web::Path, body: web::Json, -) -> aweb::Result { +) -> Result { let index = data .db - .open_index(path.index_uid.clone()) - .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; + .open_index(&path.index_uid) + .ok_or(ResponseError::index_not_found(&path.index_uid))?; - let mut writer = data - .db - .main_write_txn() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + let mut writer = data.db.main_write_txn()?; if let Some(name) = body.name.clone() { - index - .main - .put_name(&mut writer, &name) - .map_err(|e| ResponseError::Internal(e.to_string()))?; + index.main.put_name(&mut writer, &name)?; } if let Some(id) = body.primary_key.clone() { - if let Some(mut schema) = index - .main - .schema(&writer) - .map_err(|e| ResponseError::Internal(e.to_string()))? - { + if let Some(mut schema) = index.main.schema(&writer)? { match schema.primary_key() { Some(_) => { - return Err(ResponseError::BadRequest( - "The primary key cannot be updated".to_string(), - ) - .into()); + return Err(ResponseError::bad_request( + "The primary key cannot be updated", + )); } None => { - schema - .set_primary_key(&id) - .map_err(|e| ResponseError::Internal(e.to_string()))?; - index - .main - .put_schema(&mut writer, &schema) - .map_err(|e| ResponseError::Internal(e.to_string()))?; + schema.set_primary_key(&id)?; + index.main.put_schema(&mut writer, &schema)?; } } } } - index - .main - .put_updated_at(&mut writer) - .map_err(|e| ResponseError::Internal(e.to_string()))?; - writer - .commit() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + index.main.put_updated_at(&mut writer)?; + writer.commit()?; - let reader = data - .db - .main_read_txn() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + let reader = data.db.main_read_txn()?; - let name = index - .main - .name(&reader) - .map_err(|e| ResponseError::Internal(e.to_string()))? - .ok_or(ResponseError::Internal( - "Impossible to get the name of an index".to_string(), - ))?; + let name = index.main.name(&reader)?.ok_or(ResponseError::internal( + "Impossible to get the name of an index", + ))?; let created_at = index .main - .created_at(&reader) - .map_err(|e| ResponseError::Internal(e.to_string()))? - .ok_or(ResponseError::Internal( - "Impossible to get the create date of an index".to_string(), + .created_at(&reader)? + .ok_or(ResponseError::internal( + "Impossible to get the create date of an index", ))?; let updated_at = index .main - .updated_at(&reader) - .map_err(|e| ResponseError::Internal(e.to_string()))? - .ok_or(ResponseError::Internal( - "Impossible to get the last update date of an index".to_string(), + .updated_at(&reader)? + .ok_or(ResponseError::internal( + "Impossible to get the last update date of an index", ))?; let primary_key = match index.main.schema(&reader) { @@ -362,12 +296,10 @@ pub async fn update_index( pub async fn delete_index( data: web::Data, path: web::Path, -) -> aweb::Result { - data.db - .delete_index(&path.index_uid) - .map_err(|e| ResponseError::Internal(e.to_string()))?; +) -> Result { + data.db.delete_index(&path.index_uid)?; - HttpResponse::NoContent().await + Ok(HttpResponse::NoContent().finish()) } #[derive(Default, Deserialize)] @@ -380,24 +312,22 @@ pub struct UpdateParam { pub async fn get_update_status( data: web::Data, path: web::Path, -) -> aweb::Result { +) -> Result { let index = data .db - .open_index(path.index_uid.clone()) - .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; + .open_index(&path.index_uid) + .ok_or(ResponseError::index_not_found(&path.index_uid))?; - let reader = data - .db - .update_read_txn() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + let reader = data.db.update_read_txn()?; - let status = index - .update_status(&reader, path.update_id) - .map_err(|e| ResponseError::Internal(e.to_string()))?; + let status = index.update_status(&reader, path.update_id)?; match status { Some(status) => Ok(HttpResponse::Ok().json(status)), - None => Err(ResponseError::NotFound(format!("Update {} not found", path.update_id)).into()), + None => Err(ResponseError::NotFound(format!( + "Update {} not found", + path.update_id + ))), } } @@ -405,20 +335,15 @@ pub async fn get_update_status( pub async fn get_all_updates_status( data: web::Data, path: web::Path, -) -> aweb::Result { +) -> Result { let index = data .db - .open_index(path.index_uid.clone()) - .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; + .open_index(&path.index_uid) + .ok_or(ResponseError::index_not_found(&path.index_uid))?; - let reader = data - .db - .update_read_txn() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + let reader = data.db.update_read_txn()?; - let response = index - .all_updates_status(&reader) - .map_err(|err| ResponseError::Internal(err.to_string()))?; + let response = index.all_updates_status(&reader)?; Ok(HttpResponse::Ok().json(response)) } diff --git a/meilisearch-http/src/routes/search.rs b/meilisearch-http/src/routes/search.rs index 92112c4a4..3dcfc2a93 100644 --- a/meilisearch-http/src/routes/search.rs +++ b/meilisearch-http/src/routes/search.rs @@ -2,13 +2,11 @@ use std::collections::{HashSet, HashMap}; use std::time::Duration; use log::warn; -use meilisearch_core::Index; -use actix_web as aweb; use actix_web::{get, web}; -use serde::{Deserialize}; +use serde::Deserialize; use crate::error::ResponseError; -use crate::helpers::meilisearch::{Error, IndexSearchExt, SearchResult}; +use crate::helpers::meilisearch::{IndexSearchExt, SearchResult}; use crate::routes::IndexParam; use crate::Data; @@ -32,24 +30,18 @@ pub async fn search_with_url_query( data: web::Data, path: web::Path, params: web::Query, -) -> aweb::Result> { +) -> Result, ResponseError> { let index = data .db - .open_index(path.index_uid.clone()) - .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; + .open_index(&path.index_uid) + .ok_or(ResponseError::index_not_found(&path.index_uid))?; - let reader = data - .db - .main_read_txn() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + let reader = data.db.main_read_txn()?; let schema = index .main - .schema(&reader) - .map_err(|err| ResponseError::Internal(err.to_string()))? - .ok_or(ResponseError::Internal( - "Impossible to retrieve the schema".to_string(), - ))?; + .schema(&reader)? + .ok_or(ResponseError::internal("Impossible to retrieve the schema"))?; let mut search_builder = index.new_search(params.q.clone()); @@ -145,11 +137,5 @@ pub async fn search_with_url_query( } } - let response = match search_builder.search(&reader) { - Ok(response) => response, - Err(Error::Internal(message)) => return Err(ResponseError::Internal(message).into()), - Err(others) => return Err(ResponseError::BadRequest(others.to_string()).into()), - }; - - Ok(web::Json(response)) + Ok(web::Json(search_builder.search(&reader)?)) } diff --git a/meilisearch-http/src/routes/setting.rs b/meilisearch-http/src/routes/setting.rs index ca37ee1ac..928736dd1 100644 --- a/meilisearch-http/src/routes/setting.rs +++ b/meilisearch-http/src/routes/setting.rs @@ -1,4 +1,3 @@ -use actix_web as aweb; use actix_web::{delete, get, post, web, HttpResponse}; use meilisearch_core::settings::{Settings, SettingsUpdate, UpdateState, DEFAULT_RANKING_RULES}; use std::collections::{BTreeMap, BTreeSet, HashSet}; @@ -12,26 +11,19 @@ pub async fn update_all( data: web::Data, path: web::Path, body: web::Json, -) -> aweb::Result { +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; + .ok_or(ResponseError::index_not_found(&path.index_uid))?; - let mut writer = data - .db - .update_write_txn() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + let mut writer = data.db.update_write_txn()?; let settings = body .into_inner() .into_update() - .map_err(|e| ResponseError::BadRequest(e.to_string()))?; - let update_id = index - .settings_update(&mut writer, settings) - .map_err(|err| ResponseError::Internal(err.to_string()))?; - writer - .commit() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + .map_err(ResponseError::bad_request)?; + let update_id = index.settings_update(&mut writer, settings)?; + writer.commit()?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -40,71 +32,42 @@ pub async fn update_all( pub async fn get_all( data: web::Data, path: web::Path, -) -> aweb::Result { +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; + .ok_or(ResponseError::index_not_found(&path.index_uid))?; - let reader = data - .db - .main_read_txn() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + let reader = data.db.main_read_txn()?; - let stop_words_fst = index - .main - .stop_words_fst(&reader) - .map_err(|err| ResponseError::Internal(err.to_string()))?; - let stop_words = stop_words_fst - .unwrap_or_default() - .stream() - .into_strs() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + let stop_words_fst = index.main.stop_words_fst(&reader)?; + let stop_words = stop_words_fst.unwrap_or_default().stream().into_strs()?; let stop_words: BTreeSet = stop_words.into_iter().collect(); - let synonyms_fst = index - .main - .synonyms_fst(&reader) - .map_err(|err| ResponseError::Internal(err.to_string()))? - .unwrap_or_default(); - let synonyms_list = synonyms_fst - .stream() - .into_strs() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + let synonyms_fst = index.main.synonyms_fst(&reader)?.unwrap_or_default(); + let synonyms_list = synonyms_fst.stream().into_strs()?; let mut synonyms = BTreeMap::new(); let index_synonyms = &index.synonyms; for synonym in synonyms_list { - let alternative_list = index_synonyms - .synonyms(&reader, synonym.as_bytes()) - .map_err(|err| ResponseError::Internal(err.to_string()))?; + let alternative_list = index_synonyms.synonyms(&reader, synonym.as_bytes())?; if let Some(list) = alternative_list { - let list = list - .stream() - .into_strs() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + let list = list.stream().into_strs()?; synonyms.insert(synonym, list); } } let ranking_rules = index .main - .ranking_rules(&reader) - .map_err(|err| ResponseError::Internal(err.to_string()))? + .ranking_rules(&reader)? .unwrap_or(DEFAULT_RANKING_RULES.to_vec()) .into_iter() .map(|r| r.to_string()) .collect(); - let distinct_attribute = index - .main - .distinct_attribute(&reader) - .map_err(|err| ResponseError::Internal(err.to_string()))?; + let distinct_attribute = index.main.distinct_attribute(&reader)?; - let schema = index - .main - .schema(&reader) - .map_err(|err| ResponseError::Internal(err.to_string()))?; + let schema = index.main.schema(&reader)?; let searchable_attributes = schema.clone().map(|s| { s.indexed_name() @@ -139,15 +102,12 @@ pub async fn get_all( pub async fn delete_all( data: web::Data, path: web::Path, -) -> aweb::Result { +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; - let mut writer = data - .db - .update_write_txn() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + .ok_or(ResponseError::index_not_found(&path.index_uid))?; + let mut writer = data.db.update_write_txn()?; let settings = SettingsUpdate { ranking_rules: UpdateState::Clear, @@ -160,12 +120,8 @@ pub async fn delete_all( accept_new_fields: UpdateState::Clear, }; - let update_id = index - .settings_update(&mut writer, settings) - .map_err(|err| ResponseError::Internal(err.to_string()))?; - writer - .commit() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + let update_id = index.settings_update(&mut writer, settings)?; + writer.commit()?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -174,20 +130,16 @@ pub async fn delete_all( pub async fn get_rules( data: web::Data, path: web::Path, -) -> aweb::Result { +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; - let reader = data - .db - .main_read_txn() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + .ok_or(ResponseError::index_not_found(&path.index_uid))?; + let reader = data.db.main_read_txn()?; let ranking_rules = index .main - .ranking_rules(&reader) - .map_err(|err| ResponseError::Internal(err.to_string()))? + .ranking_rules(&reader)? .unwrap_or(DEFAULT_RANKING_RULES.to_vec()) .into_iter() .map(|r| r.to_string()) @@ -201,30 +153,21 @@ pub async fn update_rules( data: web::Data, path: web::Path, body: web::Json>>, -) -> aweb::Result { +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; + .ok_or(ResponseError::index_not_found(&path.index_uid))?; let settings = Settings { ranking_rules: Some(body.into_inner()), ..Settings::default() }; - let mut writer = data - .db - .update_write_txn() - .map_err(|err| ResponseError::Internal(err.to_string()))?; - let settings = settings - .into_update() - .map_err(|e| ResponseError::BadRequest(e.to_string()))?; - let update_id = index - .settings_update(&mut writer, settings) - .map_err(|err| ResponseError::Internal(err.to_string()))?; - writer - .commit() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + let mut writer = data.db.update_write_txn()?; + let settings = settings.into_update().map_err(ResponseError::bad_request)?; + let update_id = index.settings_update(&mut writer, settings)?; + writer.commit()?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -233,28 +176,21 @@ pub async fn update_rules( pub async fn delete_rules( data: web::Data, path: web::Path, -) -> aweb::Result { +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; - let mut writer = data - .db - .update_write_txn() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + .ok_or(ResponseError::index_not_found(&path.index_uid))?; + let mut writer = data.db.update_write_txn()?; let settings = SettingsUpdate { ranking_rules: UpdateState::Clear, ..SettingsUpdate::default() }; - let update_id = index - .settings_update(&mut writer, settings) - .map_err(|err| ResponseError::Internal(err.to_string()))?; + let update_id = index.settings_update(&mut writer, settings)?; - writer - .commit() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + writer.commit()?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -263,19 +199,13 @@ pub async fn delete_rules( pub async fn get_distinct( data: web::Data, path: web::Path, -) -> aweb::Result { +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; - let reader = data - .db - .main_read_txn() - .map_err(|err| ResponseError::Internal(err.to_string()))?; - let distinct_attribute = index - .main - .distinct_attribute(&reader) - .map_err(|err| ResponseError::Internal(err.to_string()))?; + .ok_or(ResponseError::index_not_found(&path.index_uid))?; + let reader = data.db.main_read_txn()?; + let distinct_attribute = index.main.distinct_attribute(&reader)?; Ok(HttpResponse::Ok().json(distinct_attribute)) } @@ -285,30 +215,21 @@ pub async fn update_distinct( data: web::Data, path: web::Path, body: web::Json>, -) -> aweb::Result { +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; + .ok_or(ResponseError::index_not_found(&path.index_uid))?; let settings = Settings { distinct_attribute: Some(body.into_inner()), ..Settings::default() }; - let mut writer = data - .db - .update_write_txn() - .map_err(|err| ResponseError::Internal(err.to_string()))?; - let settings = settings - .into_update() - .map_err(|e| ResponseError::BadRequest(e.to_string()))?; - let update_id = index - .settings_update(&mut writer, settings) - .map_err(|err| ResponseError::Internal(err.to_string()))?; - writer - .commit() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + let mut writer = data.db.update_write_txn()?; + let settings = settings.into_update().map_err(ResponseError::bad_request)?; + let update_id = index.settings_update(&mut writer, settings)?; + writer.commit()?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -317,28 +238,21 @@ pub async fn update_distinct( pub async fn delete_distinct( data: web::Data, path: web::Path, -) -> aweb::Result { +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; - let mut writer = data - .db - .update_write_txn() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + .ok_or(ResponseError::index_not_found(&path.index_uid))?; + let mut writer = data.db.update_write_txn()?; let settings = SettingsUpdate { distinct_attribute: UpdateState::Clear, ..SettingsUpdate::default() }; - let update_id = index - .settings_update(&mut writer, settings) - .map_err(|err| ResponseError::Internal(err.to_string()))?; + let update_id = index.settings_update(&mut writer, settings)?; - writer - .commit() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + writer.commit()?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -347,19 +261,13 @@ pub async fn delete_distinct( pub async fn get_searchable( data: web::Data, path: web::Path, -) -> aweb::Result { +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; - let reader = data - .db - .main_read_txn() - .map_err(|err| ResponseError::Internal(err.to_string()))?; - let schema = index - .main - .schema(&reader) - .map_err(|err| ResponseError::Internal(err.to_string()))?; + .ok_or(ResponseError::index_not_found(&path.index_uid))?; + let reader = data.db.main_read_txn()?; + let schema = index.main.schema(&reader)?; let searchable_attributes: Option> = schema.map(|s| s.indexed_name().iter().map(|i| (*i).to_string()).collect()); @@ -371,30 +279,21 @@ pub async fn update_searchable( data: web::Data, path: web::Path, body: web::Json>>, -) -> aweb::Result { +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; + .ok_or(ResponseError::index_not_found(&path.index_uid))?; let settings = Settings { searchable_attributes: Some(body.into_inner()), ..Settings::default() }; - let mut writer = data - .db - .update_write_txn() - .map_err(|err| ResponseError::Internal(err.to_string()))?; - let settings = settings - .into_update() - .map_err(|e| ResponseError::BadRequest(e.to_string()))?; - let update_id = index - .settings_update(&mut writer, settings) - .map_err(|err| ResponseError::Internal(err.to_string()))?; - writer - .commit() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + let mut writer = data.db.update_write_txn()?; + let settings = settings.into_update().map_err(ResponseError::bad_request)?; + let update_id = index.settings_update(&mut writer, settings)?; + writer.commit()?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -403,27 +302,20 @@ pub async fn update_searchable( pub async fn delete_searchable( data: web::Data, path: web::Path, -) -> aweb::Result { +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; + .ok_or(ResponseError::index_not_found(&path.index_uid))?; let settings = SettingsUpdate { searchable_attributes: UpdateState::Clear, ..SettingsUpdate::default() }; - let mut writer = data - .db - .update_write_txn() - .map_err(|err| ResponseError::Internal(err.to_string()))?; - let update_id = index - .settings_update(&mut writer, settings) - .map_err(|err| ResponseError::Internal(err.to_string()))?; - writer - .commit() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + let mut writer = data.db.update_write_txn()?; + let update_id = index.settings_update(&mut writer, settings)?; + writer.commit()?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -432,20 +324,14 @@ pub async fn delete_searchable( pub async fn get_displayed( data: web::Data, path: web::Path, -) -> aweb::Result { +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; - let reader = data - .db - .main_read_txn() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + .ok_or(ResponseError::index_not_found(&path.index_uid))?; + let reader = data.db.main_read_txn()?; - let schema = index - .main - .schema(&reader) - .map_err(|err| ResponseError::Internal(err.to_string()))?; + let schema = index.main.schema(&reader)?; let displayed_attributes: Option> = schema.map(|s| { s.displayed_name() @@ -462,30 +348,21 @@ pub async fn update_displayed( data: web::Data, path: web::Path, body: web::Json>>, -) -> aweb::Result { +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; + .ok_or(ResponseError::index_not_found(&path.index_uid))?; let settings = Settings { displayed_attributes: Some(body.into_inner()), ..Settings::default() }; - let mut writer = data - .db - .update_write_txn() - .map_err(|err| ResponseError::Internal(err.to_string()))?; - let settings = settings - .into_update() - .map_err(|e| ResponseError::BadRequest(e.to_string()))?; - let update_id = index - .settings_update(&mut writer, settings) - .map_err(|err| ResponseError::Internal(err.to_string()))?; - writer - .commit() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + let mut writer = data.db.update_write_txn()?; + let settings = settings.into_update().map_err(ResponseError::bad_request)?; + let update_id = index.settings_update(&mut writer, settings)?; + writer.commit()?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -494,27 +371,20 @@ pub async fn update_displayed( pub async fn delete_displayed( data: web::Data, path: web::Path, -) -> aweb::Result { +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; + .ok_or(ResponseError::index_not_found(&path.index_uid))?; let settings = SettingsUpdate { displayed_attributes: UpdateState::Clear, ..SettingsUpdate::default() }; - let mut writer = data - .db - .update_write_txn() - .map_err(|err| ResponseError::Internal(err.to_string()))?; - let update_id = index - .settings_update(&mut writer, settings) - .map_err(|err| ResponseError::Internal(err.to_string()))?; - writer - .commit() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + let mut writer = data.db.update_write_txn()?; + let update_id = index.settings_update(&mut writer, settings)?; + writer.commit()?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -523,20 +393,14 @@ pub async fn delete_displayed( pub async fn get_accept_new_fields( data: web::Data, path: web::Path, -) -> aweb::Result { +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; - let reader = data - .db - .main_read_txn() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + .ok_or(ResponseError::index_not_found(&path.index_uid))?; + let reader = data.db.main_read_txn()?; - let schema = index - .main - .schema(&reader) - .map_err(|err| ResponseError::Internal(err.to_string()))?; + let schema = index.main.schema(&reader)?; let accept_new_fields = schema.map(|s| s.accept_new_fields()); @@ -548,30 +412,21 @@ pub async fn update_accept_new_fields( data: web::Data, path: web::Path, body: web::Json>, -) -> aweb::Result { +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; + .ok_or(ResponseError::index_not_found(&path.index_uid))?; let settings = Settings { accept_new_fields: Some(body.into_inner()), ..Settings::default() }; - let mut writer = data - .db - .update_write_txn() - .map_err(|err| ResponseError::Internal(err.to_string()))?; - let settings = settings - .into_update() - .map_err(|e| ResponseError::BadRequest(e.to_string()))?; - let update_id = index - .settings_update(&mut writer, settings) - .map_err(|err| ResponseError::Internal(err.to_string()))?; - writer - .commit() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + let mut writer = data.db.update_write_txn()?; + let settings = settings.into_update().map_err(ResponseError::bad_request)?; + let update_id = index.settings_update(&mut writer, settings)?; + writer.commit()?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } diff --git a/meilisearch-http/src/routes/stats.rs b/meilisearch-http/src/routes/stats.rs index ee804457e..9f2449e65 100644 --- a/meilisearch-http/src/routes/stats.rs +++ b/meilisearch-http/src/routes/stats.rs @@ -1,6 +1,5 @@ use std::collections::HashMap; -use actix_web as aweb; use actix_web::{get, web}; use chrono::{DateTime, Utc}; use log::error; @@ -25,36 +24,22 @@ pub struct IndexStatsResponse { pub async fn index_stats( data: web::Data, path: web::Path, -) -> aweb::Result> { +) -> Result, ResponseError> { let index = data .db - .open_index(path.index_uid.clone()) - .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; + .open_index(&path.index_uid) + .ok_or(ResponseError::index_not_found(&path.index_uid))?; - let reader = data - .db - .main_read_txn() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + let reader = data.db.main_read_txn()?; - let number_of_documents = index - .main - .number_of_documents(&reader) - .map_err(|err| ResponseError::Internal(err.to_string()))?; + let number_of_documents = index.main.number_of_documents(&reader)?; - let fields_frequency = index - .main - .fields_frequency(&reader) - .map_err(|err| ResponseError::Internal(err.to_string()))? - .unwrap_or_default(); + let fields_frequency = index.main.fields_frequency(&reader)?.unwrap_or_default(); - let update_reader = data - .db - .update_read_txn() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + let update_reader = data.db.update_read_txn()?; let is_indexing = data - .is_indexing(&update_reader, &path.index_uid) - .map_err(|err| ResponseError::Internal(err.to_string()))? + .is_indexing(&update_reader, &path.index_uid)? .unwrap_or_default(); Ok(web::Json(IndexStatsResponse { @@ -73,37 +58,23 @@ pub struct StatsResult { } #[get("/stats")] -pub async fn get_stats(data: web::Data) -> aweb::Result> { +pub async fn get_stats(data: web::Data) -> Result, ResponseError> { let mut index_list = HashMap::new(); - let reader = data - .db - .main_read_txn() - .map_err(|err| ResponseError::Internal(err.to_string()))?; - let update_reader = data - .db - .update_read_txn() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + let reader = data.db.main_read_txn()?; + let update_reader = data.db.update_read_txn()?; let indexes_set = data.db.indexes_uids(); for index_uid in indexes_set { let index = data.db.open_index(&index_uid); match index { Some(index) => { - let number_of_documents = index - .main - .number_of_documents(&reader) - .map_err(|err| ResponseError::Internal(err.to_string()))?; + let number_of_documents = index.main.number_of_documents(&reader)?; - let fields_frequency = index - .main - .fields_frequency(&reader) - .map_err(|err| ResponseError::Internal(err.to_string()))? - .unwrap_or_default(); + let fields_frequency = index.main.fields_frequency(&reader)?.unwrap_or_default(); let is_indexing = data - .is_indexing(&update_reader, &index_uid) - .map_err(|err| ResponseError::Internal(err.to_string()))? + .is_indexing(&update_reader, &index_uid)? .unwrap_or_default(); let response = IndexStatsResponse { @@ -120,16 +91,14 @@ pub async fn get_stats(data: web::Data) -> aweb::Result, path: web::Path) -> aweb::Result { +pub async fn get( + data: web::Data, + path: web::Path, +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; - let reader = data - .db - .main_read_txn() - .map_err(|err| ResponseError::Internal(err.to_string()))?; - let stop_words_fst = index - .main - .stop_words_fst(&reader) - .map_err(|err| ResponseError::Internal(err.to_string()))?; - let stop_words = stop_words_fst - .unwrap_or_default() - .stream() - .into_strs() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + .ok_or(ResponseError::index_not_found(&path.index_uid))?; + let reader = data.db.main_read_txn()?; + let stop_words_fst = index.main.stop_words_fst(&reader)?; + let stop_words = stop_words_fst.unwrap_or_default().stream().into_strs()?; Ok(HttpResponse::Ok().json(stop_words)) } @@ -36,27 +27,20 @@ pub async fn update( data: web::Data, path: web::Path, body: web::Json>, -) -> aweb::Result { +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; + .ok_or(ResponseError::index_not_found(&path.index_uid))?; let settings = SettingsUpdate { stop_words: UpdateState::Update(body.into_inner()), ..SettingsUpdate::default() }; - let mut writer = data - .db - .update_write_txn() - .map_err(|err| ResponseError::Internal(err.to_string()))?; - let update_id = index - .settings_update(&mut writer, settings) - .map_err(|err| ResponseError::Internal(err.to_string()))?; - writer - .commit() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + let mut writer = data.db.update_write_txn()?; + let update_id = index.settings_update(&mut writer, settings)?; + writer.commit()?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -65,27 +49,20 @@ pub async fn update( pub async fn delete( data: web::Data, path: web::Path, -) -> aweb::Result { +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; + .ok_or(ResponseError::index_not_found(&path.index_uid))?; let settings = SettingsUpdate { stop_words: UpdateState::Clear, ..SettingsUpdate::default() }; - let mut writer = data - .db - .update_write_txn() - .map_err(|err| ResponseError::Internal(err.to_string()))?; - let update_id = index - .settings_update(&mut writer, settings) - .map_err(|err| ResponseError::Internal(err.to_string()))?; - writer - .commit() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + let mut writer = data.db.update_write_txn()?; + let update_id = index.settings_update(&mut writer, settings)?; + writer.commit()?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } diff --git a/meilisearch-http/src/routes/synonym.rs b/meilisearch-http/src/routes/synonym.rs index 7892988a6..adf76ba99 100644 --- a/meilisearch-http/src/routes/synonym.rs +++ b/meilisearch-http/src/routes/synonym.rs @@ -1,6 +1,5 @@ use std::collections::BTreeMap; -use actix_web as aweb; use actix_web::{delete, get, post, web, HttpResponse}; use indexmap::IndexMap; use meilisearch_core::settings::{SettingsUpdate, UpdateState}; @@ -10,41 +9,27 @@ use crate::routes::{IndexParam, IndexUpdateResponse}; use crate::Data; #[get("/indexes/{index_uid}/settings/synonyms")] -pub async fn get(data: web::Data, path: web::Path) -> aweb::Result { +pub async fn get( + data: web::Data, + path: web::Path, +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; + .ok_or(ResponseError::index_not_found(&path.index_uid))?; - let reader = data - .db - .main_read_txn() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + let reader = data.db.main_read_txn()?; - let synonyms_fst = index - .main - .synonyms_fst(&reader) - .map_err(|err| ResponseError::Internal(err.to_string()))? - .unwrap_or_default(); - let synonyms_list = synonyms_fst - .stream() - .into_strs() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + let synonyms_fst = index.main.synonyms_fst(&reader)?.unwrap_or_default(); + let synonyms_list = synonyms_fst.stream().into_strs()?; let mut synonyms = IndexMap::new(); - let index_synonyms = &index.synonyms; - for synonym in synonyms_list { - let alternative_list = index_synonyms - .synonyms(&reader, synonym.as_bytes()) - .map_err(|err| ResponseError::Internal(err.to_string()))?; + let alternative_list = index_synonyms.synonyms(&reader, synonym.as_bytes())?; if let Some(list) = alternative_list { - let list = list - .stream() - .into_strs() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + let list = list.stream().into_strs()?; synonyms.insert(synonym, list); } } @@ -57,27 +42,20 @@ pub async fn update( data: web::Data, path: web::Path, body: web::Json>>, -) -> aweb::Result { +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; + .ok_or(ResponseError::index_not_found(&path.index_uid))?; let settings = SettingsUpdate { synonyms: UpdateState::Update(body.into_inner()), ..SettingsUpdate::default() }; - let mut writer = data - .db - .update_write_txn() - .map_err(|err| ResponseError::Internal(err.to_string()))?; - let update_id = index - .settings_update(&mut writer, settings) - .map_err(|err| ResponseError::Internal(err.to_string()))?; - writer - .commit() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + let mut writer = data.db.update_write_txn()?; + let update_id = index.settings_update(&mut writer, settings)?; + writer.commit()?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -86,28 +64,21 @@ pub async fn update( pub async fn delete( data: web::Data, path: web::Path, -) -> aweb::Result { +) -> Result { let index = data .db .open_index(&path.index_uid) - .ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?; + .ok_or(ResponseError::index_not_found(&path.index_uid))?; let settings = SettingsUpdate { synonyms: UpdateState::Clear, ..SettingsUpdate::default() }; - let mut writer = data - .db - .update_write_txn() - .map_err(|err| ResponseError::Internal(err.to_string()))?; - let update_id = index - .settings_update(&mut writer, settings) - .map_err(|err| ResponseError::Internal(err.to_string()))?; + let mut writer = data.db.update_write_txn()?; + let update_id = index.settings_update(&mut writer, settings)?; - writer - .commit() - .map_err(|err| ResponseError::Internal(err.to_string()))?; + writer.commit()?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } From bc8ff49de35c49a5b016d817a1a019493b9105b7 Mon Sep 17 00:00:00 2001 From: Quentin de Quelen Date: Wed, 22 Apr 2020 17:43:51 +0200 Subject: [PATCH 18/21] update authorization middleware with actix-web-macros --- Cargo.lock | 12 ++ meilisearch-http/Cargo.toml | 1 + meilisearch-http/src/error.rs | 36 +++--- meilisearch-http/src/lib.rs | 58 ++-------- meilisearch-http/src/routes/document.rs | 57 ++++++--- meilisearch-http/src/routes/health.rs | 27 +++-- meilisearch-http/src/routes/index.rs | 55 +++++---- meilisearch-http/src/routes/key.rs | 17 ++- meilisearch-http/src/routes/search.rs | 14 ++- meilisearch-http/src/routes/setting.rs | 134 ++++++++++++++++------ meilisearch-http/src/routes/stats.rs | 50 ++++---- meilisearch-http/src/routes/stop_words.rs | 29 +++-- meilisearch-http/src/routes/synonym.rs | 29 +++-- 13 files changed, 323 insertions(+), 196 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bc4b3c6a5..8e54e7599 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -290,6 +290,17 @@ dependencies = [ "syn", ] +[[package]] +name = "actix-web-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5ec7f5e4b361aeb648381a33cf81bd0e7a9d2cf9b49cf05fb4e161d8096bb25" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "adler32" version = "1.0.4" @@ -1507,6 +1518,7 @@ dependencies = [ "actix-rt", "actix-service", "actix-web", + "actix-web-macros", "assert-json-diff", "chrono", "crossbeam-channel", diff --git a/meilisearch-http/Cargo.toml b/meilisearch-http/Cargo.toml index ca7f3b256..c7331b871 100644 --- a/meilisearch-http/Cargo.toml +++ b/meilisearch-http/Cargo.toml @@ -46,6 +46,7 @@ actix-http = "1" actix-files = "0.2.1" actix-cors = "0.2.0" actix-service = "1.0.5" +actix-web-macros = "0.1.0" tokio = { version = "0.2.18", features = ["macros"] } [dev-dependencies] diff --git a/meilisearch-http/src/error.rs b/meilisearch-http/src/error.rs index e05853062..db0ce6c2d 100644 --- a/meilisearch-http/src/error.rs +++ b/meilisearch-http/src/error.rs @@ -17,14 +17,12 @@ pub enum ResponseError { InvalidToken(String), Maintenance, MissingAuthorizationHeader, - MissingFilterValue, MissingHeader(String), NotFound(String), OpenIndex(String), FilterParsing(String), RetrieveDocument(u64, String), SearchDocuments(String), - UnknownFilteredAttribute, } impl ResponseError { @@ -103,13 +101,11 @@ impl fmt::Display for ResponseError { Self::Maintenance => f.write_str("Server is in maintenance, please try again later"), Self::FilterParsing(err) => write!(f, "parsing error: {}", err), Self::MissingAuthorizationHeader => f.write_str("You must have an authorization token"), - Self::MissingFilterValue => f.write_str("a filter doesn't have a value to compare it with"), Self::MissingHeader(header) => write!(f, "Header {} is missing", header), Self::NotFound(err) => write!(f, "{} not found", err), Self::OpenIndex(err) => write!(f, "Impossible to open index; {}", err), Self::RetrieveDocument(id, err) => write!(f, "impossible to retrieve the document with id: {}; {}", id, err), Self::SearchDocuments(err) => write!(f, "impossible to search documents; {}", err), - Self::UnknownFilteredAttribute => f.write_str("a filter is specifying an unknown schema attribute"), } } } @@ -123,24 +119,22 @@ impl aweb::error::ResponseError for ResponseError { fn status_code(&self) -> StatusCode { match *self { - Self::BadParameter(_, _) => StatusCode::BAD_REQUEST, - Self::BadRequest(_) => StatusCode::BAD_REQUEST, - Self::CreateIndex(_) => StatusCode::BAD_REQUEST, - Self::DocumentNotFound(_) => StatusCode::NOT_FOUND, - Self::IndexNotFound(_) => StatusCode::NOT_FOUND, - Self::Internal(_) => StatusCode::INTERNAL_SERVER_ERROR, - Self::InvalidIndexUid => StatusCode::BAD_REQUEST, - Self::InvalidToken(_) => StatusCode::UNAUTHORIZED, - Self::Maintenance => StatusCode::SERVICE_UNAVAILABLE, - Self::FilterParsing(_) => StatusCode::BAD_REQUEST, + Self::BadParameter(_, _) + | Self::BadRequest(_) + | Self::CreateIndex(_) + | Self::InvalidIndexUid + | Self::OpenIndex(_) + | Self::RetrieveDocument(_, _) + | Self::SearchDocuments(_) + | Self::FilterParsing(_) => StatusCode::BAD_REQUEST, + Self::DocumentNotFound(_) + | Self::IndexNotFound(_) + | Self::NotFound(_) => StatusCode::NOT_FOUND, + Self::InvalidToken(_) + | Self::MissingHeader(_) => StatusCode::UNAUTHORIZED, Self::MissingAuthorizationHeader => StatusCode::FORBIDDEN, - Self::MissingFilterValue => StatusCode::BAD_REQUEST, - Self::MissingHeader(_) => StatusCode::UNAUTHORIZED, - Self::NotFound(_) => StatusCode::NOT_FOUND, - Self::OpenIndex(_) => StatusCode::BAD_REQUEST, - Self::RetrieveDocument(_, _) => StatusCode::BAD_REQUEST, - Self::SearchDocuments(_) => StatusCode::BAD_REQUEST, - Self::UnknownFilteredAttribute => StatusCode::BAD_REQUEST, + Self::Internal(_) => StatusCode::INTERNAL_SERVER_ERROR, + Self::Maintenance => StatusCode::SERVICE_UNAVAILABLE, } } } diff --git a/meilisearch-http/src/lib.rs b/meilisearch-http/src/lib.rs index 7ce65c5d9..d4f2822ad 100644 --- a/meilisearch-http/src/lib.rs +++ b/meilisearch-http/src/lib.rs @@ -29,57 +29,17 @@ pub fn create_app( App::new() .app_data(web::Data::new(data.clone())) .app_data(web::JsonConfig::default().limit(1024 * 1024 * 10)) // Json Limit of 10Mb - .wrap(helpers::Authentication::Public) .service(routes::load_html) .service(routes::load_css) - .service(routes::search::search_with_url_query) - .service(routes::document::get_document) - .service(routes::document::get_all_documents) - .wrap(helpers::Authentication::Private) - .service(routes::index::list_indexes) - .service(routes::index::get_index) - .service(routes::index::create_index) - .service(routes::index::update_index) - .service(routes::index::delete_index) - .service(routes::index::get_update_status) - .service(routes::index::get_all_updates_status) - .service(routes::document::delete_document) - .service(routes::document::add_documents) - .service(routes::document::update_documents) - .service(routes::document::delete_documents) - .service(routes::document::clear_all_documents) - .service(routes::setting::update_all) - .service(routes::setting::get_all) - .service(routes::setting::delete_all) - .service(routes::setting::get_rules) - .service(routes::setting::update_rules) - .service(routes::setting::delete_rules) - .service(routes::setting::get_distinct) - .service(routes::setting::update_distinct) - .service(routes::setting::delete_distinct) - .service(routes::setting::get_searchable) - .service(routes::setting::update_searchable) - .service(routes::setting::delete_searchable) - .service(routes::setting::get_displayed) - .service(routes::setting::update_displayed) - .service(routes::setting::delete_displayed) - .service(routes::setting::get_accept_new_fields) - .service(routes::setting::update_accept_new_fields) - .service(routes::stop_words::get) - .service(routes::stop_words::update) - .service(routes::stop_words::delete) - .service(routes::synonym::get) - .service(routes::synonym::update) - .service(routes::synonym::delete) - .service(routes::stats::index_stats) - .service(routes::stats::get_stats) - .service(routes::stats::get_version) - .service(routes::stats::get_sys_info) - .service(routes::stats::get_sys_info_pretty) - .service(routes::health::get_health) - .service(routes::health::change_healthyness) - .wrap(helpers::Authentication::Admin) - .service(routes::key::list) + .configure(routes::document::services) + .configure(routes::index::services) + .configure(routes::search::services) + .configure(routes::setting::services) + .configure(routes::stop_words::services) + .configure(routes::synonym::services) + .configure(routes::health::services) + .configure(routes::stats::services) + .configure(routes::key::services) } pub fn index_update_callback(index_uid: &str, data: &Data, status: ProcessedUpdateResult) { diff --git a/meilisearch-http/src/routes/document.rs b/meilisearch-http/src/routes/document.rs index 27407fa4d..eea5d760d 100644 --- a/meilisearch-http/src/routes/document.rs +++ b/meilisearch-http/src/routes/document.rs @@ -1,24 +1,39 @@ use std::collections::{BTreeSet, HashSet}; -use actix_web::{delete, get, post, put, web, HttpResponse}; +use actix_web::{web, HttpResponse}; +use actix_web_macros::{delete, get, post, put}; use indexmap::IndexMap; use serde::Deserialize; use serde_json::Value; use crate::error::ResponseError; +use crate::helpers::Authentication; use crate::routes::{IndexParam, IndexUpdateResponse}; use crate::Data; type Document = IndexMap; #[derive(Default, Deserialize)] -pub struct DocumentParam { +struct DocumentParam { index_uid: String, document_id: String, } -#[get("/indexes/{index_uid}/documents/{document_id}")] -pub async fn get_document( +pub fn services(cfg: &mut web::ServiceConfig) { + cfg.service(get_document) + .service(delete_document) + .service(get_all_documents) + .service(add_documents) + .service(update_documents) + .service(delete_documents) + .service(clear_all_documents); +} + +#[get( + "/indexes/{index_uid}/documents/{document_id}", + wrap = "Authentication::Public" +)] +async fn get_document( data: web::Data, path: web::Path, ) -> Result { @@ -37,8 +52,11 @@ pub async fn get_document( Ok(HttpResponse::Ok().json(response)) } -#[delete("/indexes/{index_uid}/documents/{document_id}")] -pub async fn delete_document( +#[delete( + "/indexes/{index_uid}/documents/{document_id}", + wrap = "Authentication::Private" +)] +async fn delete_document( data: web::Data, path: web::Path, ) -> Result { @@ -62,14 +80,14 @@ pub async fn delete_document( #[derive(Default, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] -pub struct BrowseQuery { +struct BrowseQuery { offset: Option, limit: Option, attributes_to_retrieve: Option, } -#[get("/indexes/{index_uid}/documents")] -pub async fn get_all_documents( +#[get("/indexes/{index_uid}/documents", wrap = "Authentication::Public")] +async fn get_all_documents( data: web::Data, path: web::Path, params: web::Query, @@ -119,7 +137,7 @@ fn find_primary_key(document: &IndexMap) -> Option { #[derive(Default, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] -pub struct UpdateDocumentsQuery { +struct UpdateDocumentsQuery { primary_key: Option, } @@ -175,8 +193,8 @@ async fn update_multiple_documents( Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } -#[post("/indexes/{index_uid}/documents")] -pub async fn add_documents( +#[post("/indexes/{index_uid}/documents", wrap = "Authentication::Private")] +async fn add_documents( data: web::Data, path: web::Path, params: web::Query, @@ -185,8 +203,8 @@ pub async fn add_documents( update_multiple_documents(data, path, params, body, false).await } -#[put("/indexes/{index_uid}/documents")] -pub async fn update_documents( +#[put("/indexes/{index_uid}/documents", wrap = "Authentication::Private")] +async fn update_documents( data: web::Data, path: web::Path, params: web::Query, @@ -195,8 +213,11 @@ pub async fn update_documents( update_multiple_documents(data, path, params, body, true).await } -#[post("/indexes/{index_uid}/documents/delete-batch")] -pub async fn delete_documents( +#[post( + "/indexes/{index_uid}/documents/delete-batch", + wrap = "Authentication::Private" +)] +async fn delete_documents( data: web::Data, path: web::Path, body: web::Json>, @@ -224,8 +245,8 @@ pub async fn delete_documents( Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } -#[delete("/indexes/{index_uid}/documents")] -pub async fn clear_all_documents( +#[delete("/indexes/{index_uid}/documents", wrap = "Authentication::Private")] +async fn clear_all_documents( data: web::Data, path: web::Path, ) -> Result { diff --git a/meilisearch-http/src/routes/health.rs b/meilisearch-http/src/routes/health.rs index 825d79ef5..4f7795e48 100644 --- a/meilisearch-http/src/routes/health.rs +++ b/meilisearch-http/src/routes/health.rs @@ -1,13 +1,20 @@ -use crate::error::ResponseError; -use crate::Data; -use actix_web::{get, put, web, HttpResponse}; +use actix_web::{web, HttpResponse}; +use actix_web_macros::{get, put}; use heed::types::{Str, Unit}; use serde::Deserialize; +use crate::error::ResponseError; +use crate::helpers::Authentication; +use crate::Data; + const UNHEALTHY_KEY: &str = "_is_unhealthy"; -#[get("/health")] -pub async fn get_health(data: web::Data) -> Result { +pub fn services(cfg: &mut web::ServiceConfig) { + cfg.service(get_health).service(change_healthyness); +} + +#[get("/health", wrap = "Authentication::Private")] +async fn get_health(data: web::Data) -> Result { let reader = data.db.main_read_txn()?; let common_store = data.db.common_store(); @@ -19,7 +26,7 @@ pub async fn get_health(data: web::Data) -> Result) -> Result { +async fn set_healthy(data: web::Data) -> Result { let mut writer = data.db.main_write_txn()?; let common_store = data.db.common_store(); common_store.delete::<_, Str>(&mut writer, UNHEALTHY_KEY)?; @@ -28,7 +35,7 @@ pub async fn set_healthy(data: web::Data) -> Result) -> Result { +async fn set_unhealthy(data: web::Data) -> Result { let mut writer = data.db.main_write_txn()?; let common_store = data.db.common_store(); common_store.put::<_, Str, Unit>(&mut writer, UNHEALTHY_KEY, &())?; @@ -38,12 +45,12 @@ pub async fn set_unhealthy(data: web::Data) -> Result, body: web::Json, ) -> Result { diff --git a/meilisearch-http/src/routes/index.rs b/meilisearch-http/src/routes/index.rs index d46160e76..b664d0121 100644 --- a/meilisearch-http/src/routes/index.rs +++ b/meilisearch-http/src/routes/index.rs @@ -1,13 +1,25 @@ -use actix_web::{delete, get, post, put, web, HttpResponse}; +use actix_web::{web, HttpResponse}; +use actix_web_macros::{delete, get, post, put}; use chrono::{DateTime, Utc}; use log::error; use rand::seq::SliceRandom; use serde::{Deserialize, Serialize}; use crate::error::ResponseError; +use crate::helpers::Authentication; use crate::routes::IndexParam; use crate::Data; +pub fn services(cfg: &mut web::ServiceConfig) { + cfg.service(list_indexes) + .service(get_index) + .service(create_index) + .service(update_index) + .service(delete_index) + .service(get_update_status) + .service(get_all_updates_status); +} + fn generate_uid() -> String { let mut rng = rand::thread_rng(); let sample = b"abcdefghijklmnopqrstuvwxyz0123456789"; @@ -19,7 +31,7 @@ fn generate_uid() -> String { #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] -pub struct IndexResponse { +struct IndexResponse { name: String, uid: String, created_at: DateTime, @@ -27,8 +39,8 @@ pub struct IndexResponse { primary_key: Option, } -#[get("/indexes")] -pub async fn list_indexes(data: web::Data) -> Result { +#[get("/indexes", wrap = "Authentication::Private")] +async fn list_indexes(data: web::Data) -> Result { let reader = data.db.main_read_txn()?; let mut response = Vec::new(); @@ -81,8 +93,8 @@ pub async fn list_indexes(data: web::Data) -> Result, path: web::Path, ) -> Result { @@ -128,14 +140,14 @@ pub async fn get_index( #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] -pub struct IndexCreateRequest { +struct IndexCreateRequest { name: Option, uid: Option, primary_key: Option, } -#[post("/indexes")] -pub async fn create_index( +#[post("/indexes", wrap = "Authentication::Private")] +async fn create_index( data: web::Data, body: web::Json, ) -> Result { @@ -206,14 +218,14 @@ pub async fn create_index( #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] -pub struct UpdateIndexRequest { +struct UpdateIndexRequest { name: Option, primary_key: Option, } #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] -pub struct UpdateIndexResponse { +struct UpdateIndexResponse { name: String, uid: String, created_at: DateTime, @@ -221,8 +233,8 @@ pub struct UpdateIndexResponse { primary_key: Option, } -#[put("/indexes/{index_uid}")] -pub async fn update_index( +#[put("/indexes/{index_uid}", wrap = "Authentication::Private")] +async fn update_index( data: web::Data, path: web::Path, body: web::Json, @@ -292,8 +304,8 @@ pub async fn update_index( })) } -#[delete("/indexes/{index_uid}")] -pub async fn delete_index( +#[delete("/indexes/{index_uid}", wrap = "Authentication::Private")] +async fn delete_index( data: web::Data, path: web::Path, ) -> Result { @@ -303,13 +315,16 @@ pub async fn delete_index( } #[derive(Default, Deserialize)] -pub struct UpdateParam { +struct UpdateParam { index_uid: String, update_id: u64, } -#[get("/indexes/{index_uid}/updates/{update_id}")] -pub async fn get_update_status( +#[get( + "/indexes/{index_uid}/updates/{update_id}", + wrap = "Authentication::Private" +)] +async fn get_update_status( data: web::Data, path: web::Path, ) -> Result { @@ -331,8 +346,8 @@ pub async fn get_update_status( } } -#[get("/indexes/{index_uid}/updates")] -pub async fn get_all_updates_status( +#[get("/indexes/{index_uid}/updates", wrap = "Authentication::Private")] +async fn get_all_updates_status( data: web::Data, path: web::Path, ) -> Result { diff --git a/meilisearch-http/src/routes/key.rs b/meilisearch-http/src/routes/key.rs index 76ab3a116..7cca1bbb1 100644 --- a/meilisearch-http/src/routes/key.rs +++ b/meilisearch-http/src/routes/key.rs @@ -1,15 +1,22 @@ -use crate::Data; -use actix_web::{get, web}; +use actix_web::web; +use actix_web_macros::get; use serde::Serialize; +use crate::helpers::Authentication; +use crate::Data; + +pub fn services(cfg: &mut web::ServiceConfig) { + cfg.service(list); +} + #[derive(Default, Serialize)] -pub struct KeysResponse { +struct KeysResponse { private: Option, public: Option, } -#[get("/keys")] -pub async fn list(data: web::Data) -> web::Json { +#[get("/keys", wrap = "Authentication::Admin")] +async fn list(data: web::Data) -> web::Json { let api_keys = data.api_keys.clone(); web::Json(KeysResponse { private: api_keys.private, diff --git a/meilisearch-http/src/routes/search.rs b/meilisearch-http/src/routes/search.rs index 3dcfc2a93..f448553bf 100644 --- a/meilisearch-http/src/routes/search.rs +++ b/meilisearch-http/src/routes/search.rs @@ -2,17 +2,23 @@ use std::collections::{HashSet, HashMap}; use std::time::Duration; use log::warn; -use actix_web::{get, web}; +use actix_web::web; +use actix_web_macros::get; use serde::Deserialize; use crate::error::ResponseError; use crate::helpers::meilisearch::{IndexSearchExt, SearchResult}; +use crate::helpers::Authentication; use crate::routes::IndexParam; use crate::Data; +pub fn services(cfg: &mut web::ServiceConfig) { + cfg.service(search_with_url_query); +} + #[derive(Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] -pub struct SearchQuery { +struct SearchQuery { q: String, offset: Option, limit: Option, @@ -25,8 +31,8 @@ pub struct SearchQuery { matches: Option, } -#[get("/indexes/{index_uid}/search")] -pub async fn search_with_url_query( +#[get("/indexes/{index_uid}/search", wrap = "Authentication::Public")] +async fn search_with_url_query( data: web::Data, path: web::Path, params: web::Query, diff --git a/meilisearch-http/src/routes/setting.rs b/meilisearch-http/src/routes/setting.rs index 928736dd1..2af339931 100644 --- a/meilisearch-http/src/routes/setting.rs +++ b/meilisearch-http/src/routes/setting.rs @@ -1,13 +1,35 @@ -use actix_web::{delete, get, post, web, HttpResponse}; +use actix_web::{web, HttpResponse}; +use actix_web_macros::{delete, get, post}; use meilisearch_core::settings::{Settings, SettingsUpdate, UpdateState, DEFAULT_RANKING_RULES}; use std::collections::{BTreeMap, BTreeSet, HashSet}; use crate::error::ResponseError; +use crate::helpers::Authentication; use crate::routes::{IndexParam, IndexUpdateResponse}; use crate::Data; -#[post("/indexes/{index_uid}/settings")] -pub async fn update_all( +pub fn services(cfg: &mut web::ServiceConfig) { + cfg.service(update_all) + .service(get_all) + .service(delete_all) + .service(get_rules) + .service(update_rules) + .service(delete_rules) + .service(get_distinct) + .service(update_distinct) + .service(delete_distinct) + .service(get_searchable) + .service(update_searchable) + .service(delete_searchable) + .service(get_displayed) + .service(update_displayed) + .service(delete_displayed) + .service(get_accept_new_fields) + .service(update_accept_new_fields); +} + +#[post("/indexes/{index_uid}/settings", wrap = "Authentication::Private")] +async fn update_all( data: web::Data, path: web::Path, body: web::Json, @@ -28,8 +50,8 @@ pub async fn update_all( Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } -#[get("/indexes/{index_uid}/settings")] -pub async fn get_all( +#[get("/indexes/{index_uid}/settings", wrap = "Authentication::Private")] +async fn get_all( data: web::Data, path: web::Path, ) -> Result { @@ -98,8 +120,8 @@ pub async fn get_all( Ok(HttpResponse::Ok().json(settings)) } -#[delete("/indexes/{index_uid}/settings")] -pub async fn delete_all( +#[delete("/indexes/{index_uid}/settings", wrap = "Authentication::Private")] +async fn delete_all( data: web::Data, path: web::Path, ) -> Result { @@ -126,8 +148,11 @@ pub async fn delete_all( Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } -#[get("/indexes/{index_uid}/settings/ranking-rules")] -pub async fn get_rules( +#[get( + "/indexes/{index_uid}/settings/ranking-rules", + wrap = "Authentication::Private" +)] +async fn get_rules( data: web::Data, path: web::Path, ) -> Result { @@ -148,8 +173,11 @@ pub async fn get_rules( Ok(HttpResponse::Ok().json(ranking_rules)) } -#[post("/indexes/{index_uid}/settings/ranking-rules")] -pub async fn update_rules( +#[post( + "/indexes/{index_uid}/settings/ranking-rules", + wrap = "Authentication::Private" +)] +async fn update_rules( data: web::Data, path: web::Path, body: web::Json>>, @@ -172,8 +200,11 @@ pub async fn update_rules( Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } -#[delete("/indexes/{index_uid}/settings/ranking-rules")] -pub async fn delete_rules( +#[delete( + "/indexes/{index_uid}/settings/ranking-rules", + wrap = "Authentication::Private" +)] +async fn delete_rules( data: web::Data, path: web::Path, ) -> Result { @@ -195,8 +226,11 @@ pub async fn delete_rules( Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } -#[get("/indexes/{index_uid}/settings/distinct-attribute")] -pub async fn get_distinct( +#[get( + "/indexes/{index_uid}/settings/distinct-attribute", + wrap = "Authentication::Private" +)] +async fn get_distinct( data: web::Data, path: web::Path, ) -> Result { @@ -210,8 +244,11 @@ pub async fn get_distinct( Ok(HttpResponse::Ok().json(distinct_attribute)) } -#[post("/indexes/{index_uid}/settings/distinct-attribute")] -pub async fn update_distinct( +#[post( + "/indexes/{index_uid}/settings/distinct-attribute", + wrap = "Authentication::Private" +)] +async fn update_distinct( data: web::Data, path: web::Path, body: web::Json>, @@ -234,8 +271,11 @@ pub async fn update_distinct( Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } -#[delete("/indexes/{index_uid}/settings/distinct-attribute")] -pub async fn delete_distinct( +#[delete( + "/indexes/{index_uid}/settings/distinct-attribute", + wrap = "Authentication::Private" +)] +async fn delete_distinct( data: web::Data, path: web::Path, ) -> Result { @@ -257,8 +297,11 @@ pub async fn delete_distinct( Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } -#[get("/indexes/{index_uid}/settings/searchable-attributes")] -pub async fn get_searchable( +#[get( + "/indexes/{index_uid}/settings/searchable-attributes", + wrap = "Authentication::Private" +)] +async fn get_searchable( data: web::Data, path: web::Path, ) -> Result { @@ -274,8 +317,11 @@ pub async fn get_searchable( Ok(HttpResponse::Ok().json(searchable_attributes)) } -#[post("/indexes/{index_uid}/settings/searchable-attributes")] -pub async fn update_searchable( +#[post( + "/indexes/{index_uid}/settings/searchable-attributes", + wrap = "Authentication::Private" +)] +async fn update_searchable( data: web::Data, path: web::Path, body: web::Json>>, @@ -298,8 +344,11 @@ pub async fn update_searchable( Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } -#[delete("/indexes/{index_uid}/settings/searchable-attributes")] -pub async fn delete_searchable( +#[delete( + "/indexes/{index_uid}/settings/searchable-attributes", + wrap = "Authentication::Private" +)] +async fn delete_searchable( data: web::Data, path: web::Path, ) -> Result { @@ -320,8 +369,11 @@ pub async fn delete_searchable( Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } -#[get("/indexes/{index_uid}/settings/displayed-attributes")] -pub async fn get_displayed( +#[get( + "/indexes/{index_uid}/settings/displayed-attributes", + wrap = "Authentication::Private" +)] +async fn get_displayed( data: web::Data, path: web::Path, ) -> Result { @@ -343,8 +395,11 @@ pub async fn get_displayed( Ok(HttpResponse::Ok().json(displayed_attributes)) } -#[post("/indexes/{index_uid}/settings/displayed-attributes")] -pub async fn update_displayed( +#[post( + "/indexes/{index_uid}/settings/displayed-attributes", + wrap = "Authentication::Private" +)] +async fn update_displayed( data: web::Data, path: web::Path, body: web::Json>>, @@ -367,8 +422,11 @@ pub async fn update_displayed( Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } -#[delete("/indexes/{index_uid}/settings/displayed-attributes")] -pub async fn delete_displayed( +#[delete( + "/indexes/{index_uid}/settings/displayed-attributes", + wrap = "Authentication::Private" +)] +async fn delete_displayed( data: web::Data, path: web::Path, ) -> Result { @@ -389,8 +447,11 @@ pub async fn delete_displayed( Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } -#[get("/indexes/{index_uid}/settings/accept-new-fields")] -pub async fn get_accept_new_fields( +#[get( + "/indexes/{index_uid}/settings/accept-new-fields", + wrap = "Authentication::Private" +)] +async fn get_accept_new_fields( data: web::Data, path: web::Path, ) -> Result { @@ -407,8 +468,11 @@ pub async fn get_accept_new_fields( Ok(HttpResponse::Ok().json(accept_new_fields)) } -#[post("/indexes/{index_uid}/settings/accept-new-fields")] -pub async fn update_accept_new_fields( +#[post( + "/indexes/{index_uid}/settings/accept-new-fields", + wrap = "Authentication::Private" +)] +async fn update_accept_new_fields( data: web::Data, path: web::Path, body: web::Json>, diff --git a/meilisearch-http/src/routes/stats.rs b/meilisearch-http/src/routes/stats.rs index 9f2449e65..8a793f9bb 100644 --- a/meilisearch-http/src/routes/stats.rs +++ b/meilisearch-http/src/routes/stats.rs @@ -1,6 +1,7 @@ use std::collections::HashMap; -use actix_web::{get, web}; +use actix_web::web; +use actix_web_macros::get; use chrono::{DateTime, Utc}; use log::error; use pretty_bytes::converter::convert; @@ -9,19 +10,28 @@ use sysinfo::{NetworkExt, ProcessExt, ProcessorExt, System, SystemExt}; use walkdir::WalkDir; use crate::error::ResponseError; +use crate::helpers::Authentication; use crate::routes::IndexParam; use crate::Data; +pub fn services(cfg: &mut web::ServiceConfig) { + cfg.service(index_stats) + .service(get_stats) + .service(get_version) + .service(get_sys_info) + .service(get_sys_info_pretty); +} + #[derive(Serialize)] #[serde(rename_all = "camelCase")] -pub struct IndexStatsResponse { +struct IndexStatsResponse { number_of_documents: u64, is_indexing: bool, fields_frequency: HashMap, } -#[get("/indexes/{index_uid}/stats")] -pub async fn index_stats( +#[get("/indexes/{index_uid}/stats", wrap = "Authentication::Private")] +async fn index_stats( data: web::Data, path: web::Path, ) -> Result, ResponseError> { @@ -51,14 +61,14 @@ pub async fn index_stats( #[derive(Serialize)] #[serde(rename_all = "camelCase")] -pub struct StatsResult { +struct StatsResult { database_size: u64, last_update: Option>, indexes: HashMap, } -#[get("/stats")] -pub async fn get_stats(data: web::Data) -> Result, ResponseError> { +#[get("/stats", wrap = "Authentication::Private")] +async fn get_stats(data: web::Data) -> Result, ResponseError> { let mut index_list = HashMap::new(); let reader = data.db.main_read_txn()?; @@ -109,14 +119,14 @@ pub async fn get_stats(data: web::Data) -> Result, #[derive(Serialize)] #[serde(rename_all = "camelCase")] -pub struct VersionResponse { +struct VersionResponse { commit_sha: String, build_date: String, pkg_version: String, } -#[get("/version")] -pub async fn get_version() -> web::Json { +#[get("/version", wrap = "Authentication::Private")] +async fn get_version() -> web::Json { web::Json(VersionResponse { commit_sha: env!("VERGEN_SHA").to_string(), build_date: env!("VERGEN_BUILD_TIMESTAMP").to_string(), @@ -126,7 +136,7 @@ pub async fn get_version() -> web::Json { #[derive(Serialize)] #[serde(rename_all = "camelCase")] -pub struct SysGlobal { +struct SysGlobal { total_memory: u64, used_memory: u64, total_swap: u64, @@ -150,7 +160,7 @@ impl SysGlobal { #[derive(Serialize)] #[serde(rename_all = "camelCase")] -pub struct SysProcess { +struct SysProcess { memory: u64, cpu: f32, } @@ -166,7 +176,7 @@ impl SysProcess { #[derive(Serialize)] #[serde(rename_all = "camelCase")] -pub struct SysInfo { +struct SysInfo { memory_usage: f64, processor_usage: Vec, global: SysGlobal, @@ -184,8 +194,8 @@ impl SysInfo { } } -#[get("/sys-info")] -pub async fn get_sys_info(data: web::Data) -> web::Json { +#[get("/sys-info", wrap = "Authentication::Private")] +async fn get_sys_info(data: web::Data) -> web::Json { let mut sys = System::new(); let mut info = SysInfo::new(); @@ -221,7 +231,7 @@ pub async fn get_sys_info(data: web::Data) -> web::Json { #[derive(Serialize)] #[serde(rename_all = "camelCase")] -pub struct SysGlobalPretty { +struct SysGlobalPretty { total_memory: String, used_memory: String, total_swap: String, @@ -245,7 +255,7 @@ impl SysGlobalPretty { #[derive(Serialize)] #[serde(rename_all = "camelCase")] -pub struct SysProcessPretty { +struct SysProcessPretty { memory: String, cpu: String, } @@ -261,7 +271,7 @@ impl SysProcessPretty { #[derive(Serialize)] #[serde(rename_all = "camelCase")] -pub struct SysInfoPretty { +struct SysInfoPretty { memory_usage: String, processor_usage: Vec, global: SysGlobalPretty, @@ -279,8 +289,8 @@ impl SysInfoPretty { } } -#[get("/sys-info/pretty")] -pub async fn get_sys_info_pretty(data: web::Data) -> web::Json { +#[get("/sys-info/pretty", wrap = "Authentication::Private")] +async fn get_sys_info_pretty(data: web::Data) -> web::Json { let mut sys = System::new(); let mut info = SysInfoPretty::new(); diff --git a/meilisearch-http/src/routes/stop_words.rs b/meilisearch-http/src/routes/stop_words.rs index fc498a363..21fc9281f 100644 --- a/meilisearch-http/src/routes/stop_words.rs +++ b/meilisearch-http/src/routes/stop_words.rs @@ -1,13 +1,22 @@ -use actix_web::{delete, get, post, web, HttpResponse}; +use actix_web::{web, HttpResponse}; +use actix_web_macros::{delete, get, post}; use meilisearch_core::settings::{SettingsUpdate, UpdateState}; use std::collections::BTreeSet; use crate::error::ResponseError; +use crate::helpers::Authentication; use crate::routes::{IndexParam, IndexUpdateResponse}; use crate::Data; -#[get("/indexes/{index_uid}/settings/stop-words")] -pub async fn get( +pub fn services(cfg: &mut web::ServiceConfig) { + cfg.service(get).service(update).service(delete); +} + +#[get( + "/indexes/{index_uid}/settings/stop-words", + wrap = "Authentication::Private" +)] +async fn get( data: web::Data, path: web::Path, ) -> Result { @@ -22,8 +31,11 @@ pub async fn get( Ok(HttpResponse::Ok().json(stop_words)) } -#[post("/indexes/{index_uid}/settings/stop-words")] -pub async fn update( +#[post( + "/indexes/{index_uid}/settings/stop-words", + wrap = "Authentication::Private" +)] +async fn update( data: web::Data, path: web::Path, body: web::Json>, @@ -45,8 +57,11 @@ pub async fn update( Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } -#[delete("/indexes/{index_uid}/settings/stop-words")] -pub async fn delete( +#[delete( + "/indexes/{index_uid}/settings/stop-words", + wrap = "Authentication::Private" +)] +async fn delete( data: web::Data, path: web::Path, ) -> Result { diff --git a/meilisearch-http/src/routes/synonym.rs b/meilisearch-http/src/routes/synonym.rs index adf76ba99..973f591ab 100644 --- a/meilisearch-http/src/routes/synonym.rs +++ b/meilisearch-http/src/routes/synonym.rs @@ -1,15 +1,24 @@ use std::collections::BTreeMap; -use actix_web::{delete, get, post, web, HttpResponse}; +use actix_web::{web, HttpResponse}; +use actix_web_macros::{delete, get, post}; use indexmap::IndexMap; use meilisearch_core::settings::{SettingsUpdate, UpdateState}; use crate::error::ResponseError; +use crate::helpers::Authentication; use crate::routes::{IndexParam, IndexUpdateResponse}; use crate::Data; -#[get("/indexes/{index_uid}/settings/synonyms")] -pub async fn get( +pub fn services(cfg: &mut web::ServiceConfig) { + cfg.service(get).service(update).service(delete); +} + +#[get( + "/indexes/{index_uid}/settings/synonyms", + wrap = "Authentication::Private" +)] +async fn get( data: web::Data, path: web::Path, ) -> Result { @@ -37,8 +46,11 @@ pub async fn get( Ok(HttpResponse::Ok().json(synonyms)) } -#[post("/indexes/{index_uid}/settings/synonyms")] -pub async fn update( +#[post( + "/indexes/{index_uid}/settings/synonyms", + wrap = "Authentication::Private" +)] +async fn update( data: web::Data, path: web::Path, body: web::Json>>, @@ -60,8 +72,11 @@ pub async fn update( Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } -#[delete("/indexes/{index_uid}/settings/synonyms")] -pub async fn delete( +#[delete( + "/indexes/{index_uid}/settings/synonyms", + wrap = "Authentication::Private" +)] +async fn delete( data: web::Data, path: web::Path, ) -> Result { From ea308eb798888dd73d141fa41424a67652ee241b Mon Sep 17 00:00:00 2001 From: Quentin de Quelen Date: Fri, 24 Apr 2020 18:18:40 +0200 Subject: [PATCH 19/21] remove timeout search query parameter fix requested changes --- meilisearch-http/src/helpers/authentication.rs | 1 + meilisearch-http/src/helpers/meilisearch.rs | 13 ++----------- meilisearch-http/src/main.rs | 6 +++--- meilisearch-http/src/routes/search.rs | 6 ------ 4 files changed, 6 insertions(+), 20 deletions(-) diff --git a/meilisearch-http/src/helpers/authentication.rs b/meilisearch-http/src/helpers/authentication.rs index de62e22f3..f91b7c3c5 100644 --- a/meilisearch-http/src/helpers/authentication.rs +++ b/meilisearch-http/src/helpers/authentication.rs @@ -17,6 +17,7 @@ pub enum Authentication { Admin, } + impl Transform for Authentication where S: Service, Error = Error>, diff --git a/meilisearch-http/src/helpers/meilisearch.rs b/meilisearch-http/src/helpers/meilisearch.rs index 2cd9d3379..59bef52f4 100644 --- a/meilisearch-http/src/helpers/meilisearch.rs +++ b/meilisearch-http/src/helpers/meilisearch.rs @@ -1,7 +1,7 @@ use std::cmp::Ordering; use std::collections::{HashMap, HashSet}; use std::hash::{Hash, Hasher}; -use std::time::{Duration, Instant}; +use std::time::Instant; use indexmap::IndexMap; use log::error; @@ -33,7 +33,6 @@ impl IndexSearchExt for Index { attributes_to_retrieve: None, attributes_to_highlight: None, filters: None, - timeout: Duration::from_millis(30), matches: false, } } @@ -48,7 +47,6 @@ pub struct SearchBuilder<'a> { attributes_to_retrieve: Option>, attributes_to_highlight: Option>, filters: Option, - timeout: Duration, matches: bool, } @@ -89,11 +87,6 @@ impl<'a> SearchBuilder<'a> { self } - pub fn timeout(&mut self, value: Duration) -> &SearchBuilder { - self.timeout = value; - self - } - pub fn get_matches(&mut self) -> &SearchBuilder { self.matches = true; self @@ -130,8 +123,6 @@ impl<'a> SearchBuilder<'a> { }); } - query_builder.with_fetch_timeout(self.timeout); - if let Some(field) = self.index.main.distinct_attribute(reader)? { if let Some(field_id) = schema.id(&field) { query_builder.with_distinct(1, move |id| { @@ -192,7 +183,7 @@ impl<'a> SearchBuilder<'a> { .document(reader, attributes.as_ref(), doc.id) .map_err(|e| ResponseError::retrieve_document(doc.id.0, e))? .ok_or(ResponseError::internal( - "Impossible to retrieve a document id returned by the engine", + "Impossible to retrieve the document; Corrupted data", ))?; let mut formatted = document.iter() diff --git a/meilisearch-http/src/main.rs b/meilisearch-http/src/main.rs index f802dab33..637bfaddb 100644 --- a/meilisearch-http/src/main.rs +++ b/meilisearch-http/src/main.rs @@ -15,11 +15,11 @@ mod analytics; #[global_allocator] static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; -#[tokio::main] +#[actix_rt::main] async fn main() -> Result<(), MainError> { let opt = Opt::from_args(); - let local = tokio::task::LocalSet::new(); - let _sys = actix_rt::System::run_in_tokio("server", &local); + // let local = tokio::task::LocalSet::new(); + // let _sys = actix_rt::System::run_in_tokio("server", &local); match opt.env.as_ref() { "production" => { diff --git a/meilisearch-http/src/routes/search.rs b/meilisearch-http/src/routes/search.rs index f448553bf..e64550448 100644 --- a/meilisearch-http/src/routes/search.rs +++ b/meilisearch-http/src/routes/search.rs @@ -1,5 +1,4 @@ use std::collections::{HashSet, HashMap}; -use std::time::Duration; use log::warn; use actix_web::web; @@ -27,7 +26,6 @@ struct SearchQuery { crop_length: Option, attributes_to_highlight: Option, filters: Option, - timeout_ms: Option, matches: Option, } @@ -133,10 +131,6 @@ async fn search_with_url_query( search_builder.filters(filters.to_string()); } - if let Some(timeout_ms) = params.timeout_ms { - search_builder.timeout(Duration::from_millis(timeout_ms)); - } - if let Some(matches) = params.matches { if matches { search_builder.get_matches(); From 36c7fd0cf18a2aa78e3943bf95499b353977322d Mon Sep 17 00:00:00 2001 From: Quentin de Quelen Date: Fri, 24 Apr 2020 15:00:52 +0200 Subject: [PATCH 20/21] fix requested changes --- meilisearch-http/Cargo.toml | 18 +++++----- meilisearch-http/src/data.rs | 2 +- meilisearch-http/src/error.rs | 4 +-- .../src/helpers/authentication.rs | 4 ++- meilisearch-http/src/main.rs | 2 -- meilisearch-http/src/routes/document.rs | 18 ++++++---- meilisearch-http/src/routes/index.rs | 16 ++++----- meilisearch-http/src/routes/key.rs | 7 ++-- meilisearch-http/src/routes/mod.rs | 4 +-- meilisearch-http/src/routes/search.rs | 7 ++-- meilisearch-http/src/routes/setting.rs | 14 +++----- meilisearch-http/src/routes/stats.rs | 35 ++++++++++--------- 12 files changed, 68 insertions(+), 63 deletions(-) diff --git a/meilisearch-http/Cargo.toml b/meilisearch-http/Cargo.toml index c7331b871..e3c858980 100644 --- a/meilisearch-http/Cargo.toml +++ b/meilisearch-http/Cargo.toml @@ -14,6 +14,13 @@ name = "meilisearch" path = "src/main.rs" [dependencies] +actix-cors = "0.2.0" +actix-files = "0.2.1" +actix-http = "1" +actix-rt = "1" +actix-service = "1.0.5" +actix-web = "2" +actix-web-macros = "0.1.0" chrono = { version = "0.4.11", features = ["serde"] } crossbeam-channel = "0.4.2" env_logger = "0.7.1" @@ -34,20 +41,13 @@ serde_json = { version = "1.0.50", features = ["preserve_order"] } serde_qs = "0.5.2" sha2 = "0.8.1" siphasher = "0.3.2" +slice-group-by = "0.2.6" structopt = "0.3.12" sysinfo = "0.12.0" +tokio = { version = "0.2.18", features = ["macros"] } ureq = { version = "0.12.0", features = ["tls"], default-features = false } walkdir = "2.3.1" whoami = "0.8.1" -slice-group-by = "0.2.6" -actix-rt = "1" -actix-web = "2" -actix-http = "1" -actix-files = "0.2.1" -actix-cors = "0.2.0" -actix-service = "1.0.5" -actix-web-macros = "0.1.0" -tokio = { version = "0.2.18", features = ["macros"] } [dev-dependencies] http-service = "0.4.0" diff --git a/meilisearch-http/src/data.rs b/meilisearch-http/src/data.rs index 97a3526a7..cdfdfb80d 100644 --- a/meilisearch-http/src/data.rs +++ b/meilisearch-http/src/data.rs @@ -37,7 +37,7 @@ pub struct DataInner { pub server_pid: Pid, } -#[derive(Default, Clone)] +#[derive(Clone)] pub struct ApiKeys { pub public: Option, pub private: Option, diff --git a/meilisearch-http/src/error.rs b/meilisearch-http/src/error.rs index db0ce6c2d..638726ece 100644 --- a/meilisearch-http/src/error.rs +++ b/meilisearch-http/src/error.rs @@ -91,11 +91,11 @@ impl fmt::Display for ResponseError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::BadParameter(param, err) => write!(f, "Url parameter {} error: {}", param, err), - Self::BadRequest(err) => write!(f, "{}", err), + Self::BadRequest(err) => f.write_str(err), Self::CreateIndex(err) => write!(f, "Impossible to create index; {}", err), Self::DocumentNotFound(document_id) => write!(f, "Document with id {} not found", document_id), Self::IndexNotFound(index_uid) => write!(f, "Index {} not found", index_uid), - Self::Internal(err) => write!(f, "{}", err), + Self::Internal(err) => f.write_str(err), Self::InvalidIndexUid => f.write_str("Index must have a valid uid; Index uid can be of type integer or string only composed of alphanumeric characters, hyphens (-) and underscores (_)."), Self::InvalidToken(err) => write!(f, "Invalid API key: {}", err), Self::Maintenance => f.write_str("Server is in maintenance, please try again later"), diff --git a/meilisearch-http/src/helpers/authentication.rs b/meilisearch-http/src/helpers/authentication.rs index f91b7c3c5..f42898683 100644 --- a/meilisearch-http/src/helpers/authentication.rs +++ b/meilisearch-http/src/helpers/authentication.rs @@ -33,7 +33,7 @@ where fn new_transform(&self, service: S) -> Self::Future { ok(LoggingMiddleware { - acl: (*self).clone(), + acl: self.clone(), service: Rc::new(RefCell::new(service)), }) } @@ -61,6 +61,8 @@ where fn call(&mut self, req: ServiceRequest) -> Self::Future { let mut svc = self.service.clone(); + // This unwrap is left because this error should never appear. If that's the case, then + // it means that actix-web has an issue or someone changes the type `Data`. let data = req.app_data::().unwrap(); if data.api_keys.master.is_none() { diff --git a/meilisearch-http/src/main.rs b/meilisearch-http/src/main.rs index 637bfaddb..620571bfc 100644 --- a/meilisearch-http/src/main.rs +++ b/meilisearch-http/src/main.rs @@ -18,8 +18,6 @@ static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; #[actix_rt::main] async fn main() -> Result<(), MainError> { let opt = Opt::from_args(); - // let local = tokio::task::LocalSet::new(); - // let _sys = actix_rt::System::run_in_tokio("server", &local); match opt.env.as_ref() { "production" => { diff --git a/meilisearch-http/src/routes/document.rs b/meilisearch-http/src/routes/document.rs index eea5d760d..6fab12570 100644 --- a/meilisearch-http/src/routes/document.rs +++ b/meilisearch-http/src/routes/document.rs @@ -13,7 +13,7 @@ use crate::Data; type Document = IndexMap; -#[derive(Default, Deserialize)] +#[derive(Deserialize)] struct DocumentParam { index_uid: String, document_id: String, @@ -46,7 +46,7 @@ async fn get_document( let reader = data.db.main_read_txn()?; let response = index - .document::(&reader, None, document_id)? + .document(&reader, None, document_id)? .ok_or(ResponseError::document_not_found(&path.document_id))?; Ok(HttpResponse::Ok().json(response)) @@ -78,7 +78,7 @@ async fn delete_document( Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } -#[derive(Default, Deserialize)] +#[derive(Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] struct BrowseQuery { offset: Option, @@ -116,9 +116,11 @@ async fn get_all_documents( .as_ref() .map(|a| a.split(',').collect()); - let mut response = Vec::::new(); + let mut response = Vec::new(); for document_id in documents_ids { - if let Ok(Some(document)) = index.document(&reader, attributes.as_ref(), document_id) { + if let Ok(Some(document)) = + index.document::(&reader, attributes.as_ref(), document_id) + { response.push(document); } } @@ -135,7 +137,7 @@ fn find_primary_key(document: &IndexMap) -> Option { None } -#[derive(Default, Deserialize)] +#[derive(Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] struct UpdateDocumentsQuery { primary_key: Option, @@ -171,7 +173,9 @@ async fn update_multiple_documents( let mut writer = data.db.main_write_txn()?; - schema.set_primary_key(&id)?; + schema + .set_primary_key(&id) + .map_err(ResponseError::bad_request)?; index.main.put_schema(&mut writer, &schema)?; writer.commit()?; } diff --git a/meilisearch-http/src/routes/index.rs b/meilisearch-http/src/routes/index.rs index b664d0121..d43967bdd 100644 --- a/meilisearch-http/src/routes/index.rs +++ b/meilisearch-http/src/routes/index.rs @@ -157,13 +157,13 @@ async fn create_index( )); } - let uid = match body.uid.clone() { + let uid = match &body.uid { Some(uid) => { if uid .chars() .all(|x| x.is_ascii_alphanumeric() || x == '-' || x == '_') { - uid + uid.to_owned() } else { return Err(ResponseError::InvalidIndexUid); } @@ -183,8 +183,8 @@ async fn create_index( let mut writer = data.db.main_write_txn()?; - let name = body.name.clone().unwrap_or(uid.clone()); - created_index.main.put_name(&mut writer, &name)?; + let name = body.name.as_ref().unwrap_or(&uid); + created_index.main.put_name(&mut writer, name)?; let created_at = created_index .main @@ -208,7 +208,7 @@ async fn create_index( writer.commit()?; Ok(HttpResponse::Created().json(IndexResponse { - name, + name: name.to_string(), uid, created_at, updated_at, @@ -246,8 +246,8 @@ async fn update_index( let mut writer = data.db.main_write_txn()?; - if let Some(name) = body.name.clone() { - index.main.put_name(&mut writer, &name)?; + if let Some(name) = &body.name { + index.main.put_name(&mut writer, name)?; } if let Some(id) = body.primary_key.clone() { @@ -314,7 +314,7 @@ async fn delete_index( Ok(HttpResponse::NoContent().finish()) } -#[derive(Default, Deserialize)] +#[derive(Deserialize)] struct UpdateParam { index_uid: String, update_id: u64, diff --git a/meilisearch-http/src/routes/key.rs b/meilisearch-http/src/routes/key.rs index 7cca1bbb1..46e59cd6b 100644 --- a/meilisearch-http/src/routes/key.rs +++ b/meilisearch-http/src/routes/key.rs @@ -1,4 +1,5 @@ use actix_web::web; +use actix_web::HttpResponse; use actix_web_macros::get; use serde::Serialize; @@ -9,16 +10,16 @@ pub fn services(cfg: &mut web::ServiceConfig) { cfg.service(list); } -#[derive(Default, Serialize)] +#[derive(Serialize)] struct KeysResponse { private: Option, public: Option, } #[get("/keys", wrap = "Authentication::Admin")] -async fn list(data: web::Data) -> web::Json { +async fn list(data: web::Data) -> HttpResponse { let api_keys = data.api_keys.clone(); - web::Json(KeysResponse { + HttpResponse::Ok().json(KeysResponse { private: api_keys.private, public: api_keys.public, }) diff --git a/meilisearch-http/src/routes/mod.rs b/meilisearch-http/src/routes/mod.rs index 33b71c684..a63e0dc75 100644 --- a/meilisearch-http/src/routes/mod.rs +++ b/meilisearch-http/src/routes/mod.rs @@ -11,12 +11,12 @@ pub mod stats; pub mod stop_words; pub mod synonym; -#[derive(Default, Deserialize)] +#[derive(Deserialize)] pub struct IndexParam { index_uid: String, } -#[derive(Default, Serialize)] +#[derive(Serialize)] #[serde(rename_all = "camelCase")] pub struct IndexUpdateResponse { pub update_id: u64, diff --git a/meilisearch-http/src/routes/search.rs b/meilisearch-http/src/routes/search.rs index e64550448..1189e79a8 100644 --- a/meilisearch-http/src/routes/search.rs +++ b/meilisearch-http/src/routes/search.rs @@ -2,11 +2,12 @@ use std::collections::{HashSet, HashMap}; use log::warn; use actix_web::web; +use actix_web::HttpResponse; use actix_web_macros::get; use serde::Deserialize; use crate::error::ResponseError; -use crate::helpers::meilisearch::{IndexSearchExt, SearchResult}; +use crate::helpers::meilisearch::IndexSearchExt; use crate::helpers::Authentication; use crate::routes::IndexParam; use crate::Data; @@ -34,7 +35,7 @@ async fn search_with_url_query( data: web::Data, path: web::Path, params: web::Query, -) -> Result, ResponseError> { +) -> Result { let index = data .db .open_index(&path.index_uid) @@ -137,5 +138,5 @@ async fn search_with_url_query( } } - Ok(web::Json(search_builder.search(&reader)?)) + Ok(HttpResponse::Ok().json(search_builder.search(&reader)?)) } diff --git a/meilisearch-http/src/routes/setting.rs b/meilisearch-http/src/routes/setting.rs index 2af339931..262af8b84 100644 --- a/meilisearch-http/src/routes/setting.rs +++ b/meilisearch-http/src/routes/setting.rs @@ -94,14 +94,14 @@ async fn get_all( let searchable_attributes = schema.clone().map(|s| { s.indexed_name() .iter() - .map(|s| (*s).to_string()) + .map(|s| s.to_string()) .collect::>() }); let displayed_attributes = schema.clone().map(|s| { s.displayed_name() .iter() - .map(|s| (*s).to_string()) + .map(|s| s.to_string()) .collect::>() }); @@ -312,7 +312,7 @@ async fn get_searchable( let reader = data.db.main_read_txn()?; let schema = index.main.schema(&reader)?; let searchable_attributes: Option> = - schema.map(|s| s.indexed_name().iter().map(|i| (*i).to_string()).collect()); + schema.map(|s| s.indexed_name().iter().map(|i| i.to_string()).collect()); Ok(HttpResponse::Ok().json(searchable_attributes)) } @@ -385,12 +385,8 @@ async fn get_displayed( let schema = index.main.schema(&reader)?; - let displayed_attributes: Option> = schema.map(|s| { - s.displayed_name() - .iter() - .map(|i| (*i).to_string()) - .collect() - }); + let displayed_attributes: Option> = + schema.map(|s| s.displayed_name().iter().map(|i| i.to_string()).collect()); Ok(HttpResponse::Ok().json(displayed_attributes)) } diff --git a/meilisearch-http/src/routes/stats.rs b/meilisearch-http/src/routes/stats.rs index 8a793f9bb..92945e588 100644 --- a/meilisearch-http/src/routes/stats.rs +++ b/meilisearch-http/src/routes/stats.rs @@ -1,6 +1,7 @@ use std::collections::HashMap; use actix_web::web; +use actix_web::HttpResponse; use actix_web_macros::get; use chrono::{DateTime, Utc}; use log::error; @@ -34,7 +35,7 @@ struct IndexStatsResponse { async fn index_stats( data: web::Data, path: web::Path, -) -> Result, ResponseError> { +) -> Result { let index = data .db .open_index(&path.index_uid) @@ -48,11 +49,13 @@ async fn index_stats( let update_reader = data.db.update_read_txn()?; - let is_indexing = data - .is_indexing(&update_reader, &path.index_uid)? - .unwrap_or_default(); + let is_indexing = + data.is_indexing(&update_reader, &path.index_uid)? + .ok_or(ResponseError::internal( + "Impossible to know if the database is indexing", + ))?; - Ok(web::Json(IndexStatsResponse { + Ok(HttpResponse::Ok().json(IndexStatsResponse { number_of_documents, is_indexing, fields_frequency, @@ -68,7 +71,7 @@ struct StatsResult { } #[get("/stats", wrap = "Authentication::Private")] -async fn get_stats(data: web::Data) -> Result, ResponseError> { +async fn get_stats(data: web::Data) -> Result { let mut index_list = HashMap::new(); let reader = data.db.main_read_txn()?; @@ -83,9 +86,9 @@ async fn get_stats(data: web::Data) -> Result, Resp let fields_frequency = index.main.fields_frequency(&reader)?.unwrap_or_default(); - let is_indexing = data - .is_indexing(&update_reader, &index_uid)? - .unwrap_or_default(); + let is_indexing = data.is_indexing(&update_reader, &index_uid)?.ok_or( + ResponseError::internal("Impossible to know if the database is indexing"), + )?; let response = IndexStatsResponse { number_of_documents, @@ -110,7 +113,7 @@ async fn get_stats(data: web::Data) -> Result, Resp let last_update = data.last_update(&reader)?; - Ok(web::Json(StatsResult { + Ok(HttpResponse::Ok().json(StatsResult { database_size, last_update, indexes: index_list, @@ -126,8 +129,8 @@ struct VersionResponse { } #[get("/version", wrap = "Authentication::Private")] -async fn get_version() -> web::Json { - web::Json(VersionResponse { +async fn get_version() -> HttpResponse { + HttpResponse::Ok().json(VersionResponse { commit_sha: env!("VERGEN_SHA").to_string(), build_date: env!("VERGEN_BUILD_TIMESTAMP").to_string(), pkg_version: env!("CARGO_PKG_VERSION").to_string(), @@ -195,7 +198,7 @@ impl SysInfo { } #[get("/sys-info", wrap = "Authentication::Private")] -async fn get_sys_info(data: web::Data) -> web::Json { +async fn get_sys_info(data: web::Data) -> HttpResponse { let mut sys = System::new(); let mut info = SysInfo::new(); @@ -226,7 +229,7 @@ async fn get_sys_info(data: web::Data) -> web::Json { } sys.refresh_all(); - web::Json(info) + HttpResponse::Ok().json(info) } #[derive(Serialize)] @@ -290,7 +293,7 @@ impl SysInfoPretty { } #[get("/sys-info/pretty", wrap = "Authentication::Private")] -async fn get_sys_info_pretty(data: web::Data) -> web::Json { +async fn get_sys_info_pretty(data: web::Data) -> HttpResponse { let mut sys = System::new(); let mut info = SysInfoPretty::new(); @@ -328,5 +331,5 @@ async fn get_sys_info_pretty(data: web::Data) -> web::Json sys.refresh_all(); - web::Json(info) + HttpResponse::Ok().json(info) } From 99866ba484351e1a622fe883eba4f2c2f94de0ea Mon Sep 17 00:00:00 2001 From: qdequele Date: Sun, 26 Apr 2020 20:54:35 +0200 Subject: [PATCH 21/21] fix test after rebase --- Cargo.lock | 1 - meilisearch-http/Cargo.toml | 1 - meilisearch-http/src/helpers/meilisearch.rs | 8 +- meilisearch-http/src/routes/document.rs | 3 +- meilisearch-http/tests/search.rs | 123 ++++++++++++++------ 5 files changed, 92 insertions(+), 44 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8e54e7599..c84dafd39 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1536,7 +1536,6 @@ dependencies = [ "meilisearch-schema", "meilisearch-tokenizer", "mime", - "once_cell", "pretty-bytes", "rand 0.7.3", "serde", diff --git a/meilisearch-http/Cargo.toml b/meilisearch-http/Cargo.toml index e3c858980..c2a7167d7 100644 --- a/meilisearch-http/Cargo.toml +++ b/meilisearch-http/Cargo.toml @@ -53,7 +53,6 @@ whoami = "0.8.1" http-service = "0.4.0" http-service-mock = "0.4.0" tempdir = "0.3.7" -once_cell = "1.3.1" tokio = { version = "0.2.18", features = ["macros", "time"] } [dev-dependencies.assert-json-diff] diff --git a/meilisearch-http/src/helpers/meilisearch.rs b/meilisearch-http/src/helpers/meilisearch.rs index 59bef52f4..6c8046826 100644 --- a/meilisearch-http/src/helpers/meilisearch.rs +++ b/meilisearch-http/src/helpers/meilisearch.rs @@ -172,15 +172,9 @@ impl<'a> SearchBuilder<'a> { let mut hits = Vec::with_capacity(self.limit); for doc in docs { - // retrieve the content of document in kv store - let attributes: Option> = self - .attributes_to_retrieve - .as_ref() - .map(|a| a.iter().map(|a| a.as_str()).collect()); - let mut document: IndexMap = self .index - .document(reader, attributes.as_ref(), doc.id) + .document(reader, Some(&all_attributes), doc.id) .map_err(|e| ResponseError::retrieve_document(doc.id.0, e))? .ok_or(ResponseError::internal( "Impossible to retrieve the document; Corrupted data", diff --git a/meilisearch-http/src/routes/document.rs b/meilisearch-http/src/routes/document.rs index 6fab12570..ad90df8ca 100644 --- a/meilisearch-http/src/routes/document.rs +++ b/meilisearch-http/src/routes/document.rs @@ -41,11 +41,12 @@ async fn get_document( .db .open_index(&path.index_uid) .ok_or(ResponseError::index_not_found(&path.index_uid))?; + let document_id = meilisearch_core::serde::compute_document_id(&path.document_id); let reader = data.db.main_read_txn()?; - let response = index + let response: Document = index .document(&reader, None, document_id)? .ok_or(ResponseError::document_not_found(&path.document_id))?; diff --git a/meilisearch-http/tests/search.rs b/meilisearch-http/tests/search.rs index 7f3409b20..c16cdc469 100644 --- a/meilisearch-http/tests/search.rs +++ b/meilisearch-http/tests/search.rs @@ -1,23 +1,18 @@ use std::convert::Into; -use std::sync::Mutex; use assert_json_diff::assert_json_eq; -use once_cell::sync::Lazy; use serde_json::json; mod common; -static GLOBAL_SERVER: Lazy> = Lazy::new(|| { - let mut server = common::Server::with_uid("movies"); - server.populate_movies(); - Mutex::new(server) -}); - // Search // q: Captain // limit: 3 #[actix_rt::test] async fn search_with_limit() { + let mut server = common::Server::with_uid("movies"); + server.populate_movies().await; + let query = "q=captain&limit=3"; let expected = json!([ @@ -74,7 +69,7 @@ async fn search_with_limit() { } ]); - let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query).await; + let (response, _status_code) = server.search(query).await; assert_json_eq!(expected, response["hits"].clone(), ordered: false); } @@ -84,6 +79,9 @@ async fn search_with_limit() { // offset: 1 #[actix_rt::test] async fn search_with_offset() { + let mut server = common::Server::with_uid("movies"); + server.populate_movies().await; + let query = "q=captain&limit=3&offset=1"; let expected = json!([ @@ -141,7 +139,7 @@ async fn search_with_offset() { } ]); - let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query).await; + let (response, _status_code) = server.search(query).await; assert_json_eq!(expected, response["hits"].clone(), ordered: false); } @@ -151,6 +149,9 @@ async fn search_with_offset() { // attributeToHighlight: * #[actix_rt::test] async fn search_with_attribute_to_highlight_wildcard() { + let mut server = common::Server::with_uid("movies"); + server.populate_movies().await; + let query = "q=captain&limit=1&attributesToHighlight=*"; let expected = json!([ @@ -190,7 +191,7 @@ async fn search_with_attribute_to_highlight_wildcard() { } ]); - let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query).await; + let (response, _status_code) = server.search(query).await; assert_json_eq!(expected, response["hits"].clone(), ordered: false); } @@ -200,6 +201,9 @@ async fn search_with_attribute_to_highlight_wildcard() { // attributeToHighlight: title #[actix_rt::test] async fn search_with_attribute_to_highlight_1() { + let mut server = common::Server::with_uid("movies"); + server.populate_movies().await; + let query = "q=captain&limit=1&attributesToHighlight=title"; let expected = json!([ @@ -239,7 +243,7 @@ async fn search_with_attribute_to_highlight_1() { } ]); - let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query).await; + let (response, _status_code) = server.search(query).await; assert_json_eq!(expected, response["hits"].clone(), ordered: false); } @@ -249,6 +253,9 @@ async fn search_with_attribute_to_highlight_1() { // attributeToHighlight: title,tagline #[actix_rt::test] async fn search_with_attribute_to_highlight_title_tagline() { + let mut server = common::Server::with_uid("movies"); + server.populate_movies().await; + let query = "q=captain&limit=1&attributesToHighlight=title,tagline"; let expected = json!([ @@ -288,7 +295,7 @@ async fn search_with_attribute_to_highlight_title_tagline() { } ]); - let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query).await; + let (response, _status_code) = server.search(query).await; assert_json_eq!(expected, response["hits"].clone(), ordered: false); } @@ -298,6 +305,9 @@ async fn search_with_attribute_to_highlight_title_tagline() { // attributeToHighlight: title,overview #[actix_rt::test] async fn search_with_attribute_to_highlight_title_overview() { + let mut server = common::Server::with_uid("movies"); + server.populate_movies().await; + let query = "q=captain&limit=1&attributesToHighlight=title,overview"; let expected = json!([ @@ -337,7 +347,7 @@ async fn search_with_attribute_to_highlight_title_overview() { } ]); - let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query).await; + let (response, _status_code) = server.search(query).await; assert_json_eq!(expected, response["hits"].clone(), ordered: false); } @@ -347,6 +357,9 @@ async fn search_with_attribute_to_highlight_title_overview() { // matches: true #[actix_rt::test] async fn search_with_matches() { + let mut server = common::Server::with_uid("movies"); + server.populate_movies().await; + let query = "q=captain&limit=1&matches=true"; let expected = json!([ @@ -383,7 +396,7 @@ async fn search_with_matches() { } ]); - let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query).await; + let (response, _status_code) = server.search(query).await; assert_json_eq!(expected, response["hits"].clone(), ordered: false); } @@ -394,6 +407,9 @@ async fn search_with_matches() { // cropLength: 20 #[actix_rt::test] async fn search_witch_crop() { + let mut server = common::Server::with_uid("movies"); + server.populate_movies().await; + let query = "q=captain&limit=1&attributesToCrop=overview&cropLength=20"; let expected = json!([ @@ -433,7 +449,7 @@ async fn search_witch_crop() { } ]); - let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query).await; + let (response, _status_code) = server.search(query).await; assert_json_eq!(expected, response["hits"].clone(), ordered: false); } @@ -443,6 +459,9 @@ async fn search_witch_crop() { // attributesToRetrieve: [title,tagline,overview,poster_path] #[actix_rt::test] async fn search_with_attributes_to_retrieve() { + let mut server = common::Server::with_uid("movies"); + server.populate_movies().await; + let query = "q=captain&limit=1&attributesToRetrieve=title,tagline,overview,poster_path"; let expected = json!([ @@ -454,7 +473,7 @@ async fn search_with_attributes_to_retrieve() { } ]); - let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query).await; + let (response, _status_code) = server.search(query).await; assert_json_eq!(expected, response["hits"].clone(), ordered: false); } @@ -464,6 +483,9 @@ async fn search_with_attributes_to_retrieve() { // attributesToRetrieve: * #[actix_rt::test] async fn search_with_attributes_to_retrieve_wildcard() { + let mut server = common::Server::with_uid("movies"); + server.populate_movies().await; + let query = "q=captain&limit=1&attributesToRetrieve=*"; let expected = json!([ @@ -486,7 +508,7 @@ async fn search_with_attributes_to_retrieve_wildcard() { } ]); - let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query).await; + let (response, _status_code) = server.search(query).await; assert_json_eq!(expected, response["hits"].clone(), ordered: false); } @@ -496,6 +518,9 @@ async fn search_with_attributes_to_retrieve_wildcard() { // filters: director:Anthony%20Russo #[actix_rt::test] async fn search_with_filter() { + let mut server = common::Server::with_uid("movies"); + server.populate_movies().await; + let query = "q=captain&filters=director%20%3D%20%22Anthony%20Russo%22&limit=3"; let expected = json!([ { @@ -550,7 +575,7 @@ async fn search_with_filter() { "vote_count": 10497 } ]); - let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query); + let (response, _status_code) = server.search(query).await; assert_json_eq!(expected, response["hits"].clone(), ordered: false); let expected = json!([ @@ -574,7 +599,7 @@ async fn search_with_filter() { // filters: title = "american pie 2" let query = "q=american&filters=title%20%3D%20%22american%20pie%202%22"; - let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query); + let (response, _status_code) = server.search(query).await; assert_json_eq!(expected, response["hits"].clone(), ordered: false); let expected = json!([ @@ -615,7 +640,7 @@ async fn search_with_filter() { ]); // limit: 3, director = "anthony russo" AND (title = "captain america: civil war" OR title = "Captain America: The Winter Soldier") let query = "q=a&limit=3&filters=director%20%3D%20%22anthony%20russo%22%20AND%20%20(title%20%3D%20%22captain%20america%3A%20civil%20war%22%20OR%20title%20%3D%20%22Captain%20America%3A%20The%20Winter%20Soldier%22)"; - let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query); + let (response, _status_code) = server.search(query).await; assert_json_eq!(expected, response["hits"].clone(), ordered: false); let expected = json!([ @@ -673,7 +698,7 @@ async fn search_with_filter() { ]); // director = "anthony russo" AND (title = "captain america: civil war" OR vote_average > 8.0) let query = "q=a&limit=3&filters=director%20%3D%20%22anthony%20russo%22%20AND%20%20(title%20%3D%20%22captain%20america%3A%20civil%20war%22%20OR%20vote_average%20%3E%208.0)"; - let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query); + let (response, _status_code) = server.search(query).await; assert_json_eq!(expected, response["hits"].clone(), ordered: false); let expected = json!([ @@ -730,12 +755,12 @@ async fn search_with_filter() { ]); // NOT director = "anthony russo" AND vote_average > 7.5 let query = "q=a&limit=3&filters=NOT%20director%20%3D%20%22anthony%20russo%22%20AND%20vote_average%20%3E%207.5"; - let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query); + let (response, _status_code) = server.search(query).await; assert_json_eq!(expected, response["hits"].clone(), ordered: false); let expected = json!([]); let query = "q=a&filters=NOT%20director%20%3D%20%22anthony%20russo%22%20AND%20title%20%20%3D%20%22Avengers%3A%20Endgame%22"; - let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query); + let (response, _status_code) = server.search(query).await; assert_json_eq!(expected, response["hits"].clone(), ordered: false); } @@ -746,6 +771,9 @@ async fn search_with_filter() { // matches: true #[actix_rt::test] async fn search_with_attributes_to_highlight_and_matches() { + let mut server = common::Server::with_uid("movies"); + server.populate_movies().await; + let query = "q=captain&limit=1&attributesToHighlight=title,overview&matches=true"; let expected = json!( [ @@ -799,7 +827,7 @@ async fn search_with_attributes_to_highlight_and_matches() { } ]); - let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query).await; + let (response, _status_code) = server.search(query).await; assert_json_eq!(expected, response["hits"].clone(), ordered: false); } @@ -812,6 +840,9 @@ async fn search_with_attributes_to_highlight_and_matches() { // attributesToCrop: overview #[actix_rt::test] async fn search_with_attributes_to_highlight_and_matches_and_crop() { + let mut server = common::Server::with_uid("movies"); + server.populate_movies().await; + let query = "q=captain&limit=1&attributesToCrop=overview&cropLength=20&attributesToHighlight=title,overview&matches=true"; let expected = json!([ @@ -865,7 +896,7 @@ async fn search_with_attributes_to_highlight_and_matches_and_crop() { } ]); - let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query).await; + let (response, _status_code) = server.search(query).await; assert_json_eq!(expected, response["hits"].clone(), ordered: false); } @@ -876,6 +907,9 @@ async fn search_with_attributes_to_highlight_and_matches_and_crop() { // attributesToHighlight: [title] #[actix_rt::test] async fn search_with_differents_attributes() { + let mut server = common::Server::with_uid("movies"); + server.populate_movies().await; + let query = "q=captain&limit=1&attributesToRetrieve=title,producer,director&attributesToHighlight=title"; let expected = json!([ @@ -889,7 +923,7 @@ async fn search_with_differents_attributes() { } ]); - let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query).await; + let (response, _status_code) = server.search(query).await; assert_json_eq!(expected, response["hits"].clone(), ordered: false); } @@ -901,6 +935,9 @@ async fn search_with_differents_attributes() { // cropLength: 10 #[actix_rt::test] async fn search_with_differents_attributes_2() { + let mut server = common::Server::with_uid("movies"); + server.populate_movies().await; + let query = "q=captain&limit=1&attributesToRetrieve=title,producer,director&attributesToCrop=overview&cropLength=10"; let expected = json!([ @@ -914,7 +951,7 @@ async fn search_with_differents_attributes_2() { } ]); - let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query).await; + let (response, _status_code) = server.search(query).await; assert_json_eq!(expected, response["hits"].clone(), ordered: false); } @@ -925,6 +962,9 @@ async fn search_with_differents_attributes_2() { // attributesToCrop: [overview:10] #[actix_rt::test] async fn search_with_differents_attributes_3() { + let mut server = common::Server::with_uid("movies"); + server.populate_movies().await; + let query = "q=captain&limit=1&attributesToRetrieve=title,producer,director&attributesToCrop=overview:10"; let expected = json!([ @@ -938,7 +978,7 @@ async fn search_with_differents_attributes_3() { } ]); - let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query).await; + let (response, _status_code) = server.search(query).await; assert_json_eq!(expected, response["hits"].clone(), ordered: false); } @@ -949,6 +989,9 @@ async fn search_with_differents_attributes_3() { // attributesToCrop: [overview:10,title:0] #[actix_rt::test] async fn search_with_differents_attributes_4() { + let mut server = common::Server::with_uid("movies"); + server.populate_movies().await; + let query = "q=captain&limit=1&attributesToRetrieve=title,producer,director&attributesToCrop=overview:10,title:0"; let expected = json!([ @@ -963,7 +1006,7 @@ async fn search_with_differents_attributes_4() { } ]); - let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query).await; + let (response, _status_code) = server.search(query).await; assert_json_eq!(expected, response["hits"].clone(), ordered: false); } @@ -974,6 +1017,9 @@ async fn search_with_differents_attributes_4() { // attributesToCrop: [*,overview:10] #[actix_rt::test] async fn search_with_differents_attributes_5() { + let mut server = common::Server::with_uid("movies"); + server.populate_movies().await; + let query = "q=captain&limit=1&attributesToRetrieve=title,producer,director&attributesToCrop=*,overview:10"; let expected = json!([ @@ -990,7 +1036,7 @@ async fn search_with_differents_attributes_5() { } ]); - let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query).await; + let (response, _status_code) = server.search(query).await; assert_json_eq!(expected, response["hits"].clone(), ordered: false); } @@ -1002,6 +1048,9 @@ async fn search_with_differents_attributes_5() { // attributesToHighlight: [title] #[actix_rt::test] async fn search_with_differents_attributes_6() { + let mut server = common::Server::with_uid("movies"); + server.populate_movies().await; + let query = "q=captain&limit=1&attributesToRetrieve=title,producer,director&attributesToCrop=*,overview:10&attributesToHighlight=title"; let expected = json!([ @@ -1018,7 +1067,7 @@ async fn search_with_differents_attributes_6() { } ]); - let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query).await; + let (response, _status_code) = server.search(query).await; assert_json_eq!(expected, response["hits"].clone(), ordered: false); } @@ -1030,6 +1079,9 @@ async fn search_with_differents_attributes_6() { // attributesToHighlight: [*] #[actix_rt::test] async fn search_with_differents_attributes_7() { + let mut server = common::Server::with_uid("movies"); + server.populate_movies().await; + let query = "q=captain&limit=1&attributesToRetrieve=title,producer,director&attributesToCrop=*,overview:10&attributesToHighlight=*"; let expected = json!([ @@ -1046,7 +1098,7 @@ async fn search_with_differents_attributes_7() { } ]); - let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query).await; + let (response, _status_code) = server.search(query).await; assert_json_eq!(expected, response["hits"].clone(), ordered: false); } @@ -1058,6 +1110,9 @@ async fn search_with_differents_attributes_7() { // attributesToHighlight: [*,tagline] #[actix_rt::test] async fn search_with_differents_attributes_8() { + let mut server = common::Server::with_uid("movies"); + server.populate_movies().await; + let query = "q=captain&limit=1&attributesToRetrieve=title,producer,director&attributesToCrop=*,overview:10&attributesToHighlight=*,tagline"; let expected = json!([ @@ -1075,6 +1130,6 @@ async fn search_with_differents_attributes_8() { } ]); - let (response, _status_code) = GLOBAL_SERVER.lock().unwrap().search(query).await; + let (response, _status_code) = server.search(query).await; assert_json_eq!(expected, response["hits"].clone(), ordered: false); }