diff --git a/Cargo.lock b/Cargo.lock index 399b0387e..c84dafd39 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,10 +1,317 @@ # 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-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" +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" +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 = "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" +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 +336,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 +355,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 +372,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 +399,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 +436,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 +494,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 +550,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 +579,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 +603,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 +658,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 +793,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 +824,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" @@ -504,7 +877,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d371106cc88ffdfb1eabd7111e432da544f16f3e2d7bf1dfe8bf575f1df045cd" dependencies = [ "backtrace", - "version_check", + "version_check 0.9.1", +] + +[[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]] @@ -513,6 +908,18 @@ 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 +963,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 +994,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 +1035,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 +1052,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 +1094,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 +1133,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 +1216,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 +1247,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 +1274,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 +1286,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 +1363,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 +1385,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 +1408,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 +1424,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 +1445,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 +1512,20 @@ dependencies = [ name = "meilisearch-http" version = "0.10.1" dependencies = [ + "actix-cors", + "actix-files", + "actix-http", + "actix-rt", + "actix-service", + "actix-web", + "actix-web-macros", "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", @@ -1132,10 +1536,8 @@ dependencies = [ "meilisearch-schema", "meilisearch-tokenizer", "mime", - "once_cell", "pretty-bytes", "rand 0.7.3", - "rayon", "serde", "serde_json", "serde_qs", @@ -1145,7 +1547,7 @@ dependencies = [ "structopt", "sysinfo", "tempdir", - "tide", + "tokio", "ureq", "vergen", "walkdir", @@ -1200,6 +1602,25 @@ 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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa679ff6578b1cddee93d7e82e263b94a575e0bfced07284eb0c037c1d2416a5" +dependencies = [ + "adler32", +] + [[package]] name = "mio" version = "0.6.21" @@ -1266,6 +1687,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" @@ -1296,9 +1727,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 +1775,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 +1860,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 +1888,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" @@ -1511,7 +1936,7 @@ dependencies = [ "proc-macro2", "quote", "syn", - "version_check", + "version_check 0.9.1", ] [[package]] @@ -1524,7 +1949,7 @@ dependencies = [ "quote", "syn", "syn-mid", - "version_check", + "version_check 0.9.1", ] [[package]] @@ -1682,9 +2107,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 +2141,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 +2166,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 +2196,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 +2214,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 +2308,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 +2332,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 +2351,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 +2380,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 +2402,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 +2410,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 +2421,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 +2434,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 +2533,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 +2563,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 +2633,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" @@ -2288,7 +2689,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]] @@ -2306,7 +2707,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 +2736,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", ] @@ -2377,6 +2767,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" @@ -2393,6 +2814,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" @@ -2416,17 +2843,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 +2938,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 +2974,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 +2987,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-http/Cargo.toml b/meilisearch-http/Cargo.toml index b2bac5852..c2a7167d7 100644 --- a/meilisearch-http/Cargo.toml +++ b/meilisearch-http/Cargo.toml @@ -14,7 +14,13 @@ name = "meilisearch" path = "src/main.rs" [dependencies] -async-std = { version = "1.5.0", features = ["attributes"] } +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" @@ -30,25 +36,24 @@ 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" sha2 = "0.8.1" siphasher = "0.3.2" +slice-group-by = "0.2.6" structopt = "0.3.12" sysinfo = "0.12.0" -tide = "0.6.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" [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/data.rs b/meilisearch-http/src/data.rs index 5d05c02dd..cdfdfb80d 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::index_update_callback; const LAST_UPDATE_KEY: &str = "last-update"; @@ -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, @@ -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 a4120b2ee..638726ece 100644 --- a/meilisearch-http/src/error.rs +++ b/meilisearch-http/src/error.rs @@ -1,191 +1,182 @@ -use std::fmt::Display; +use std::fmt; -use http::status::StatusCode; -use log::{error, warn}; -use meilisearch_core::{FstError, HeedError}; -use serde::{Deserialize, Serialize}; -use tide::IntoResponse; -use tide::Response; - -use crate::helpers::meilisearch::Error as SearchError; - -pub type SResult = Result; +use actix_http::ResponseBuilder; +use actix_web as aweb; +use actix_web::http::StatusCode; +use serde_json::json; +#[derive(Debug)] pub enum ResponseError { - Internal(String), - BadRequest(String), - 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, + MissingHeader(String), + NotFound(String), + OpenIndex(String), + FilterParsing(String), + RetrieveDocument(u64, String), + SearchDocuments(String), } impl ResponseError { - pub fn internal(message: impl Display) -> ResponseError { - ResponseError::Internal(message.to_string()) + pub fn internal(err: impl fmt::Display) -> ResponseError { + ResponseError::Internal(err.to_string()) } - pub fn bad_request(message: impl Display) -> ResponseError { - ResponseError::BadRequest(message.to_string()) + pub fn bad_request(err: impl fmt::Display) -> ResponseError { + ResponseError::BadRequest(err.to_string()) } - pub fn invalid_token(message: impl Display) -> ResponseError { - ResponseError::InvalidToken(message.to_string()) + pub fn missing_authorization_header() -> ResponseError { + ResponseError::MissingAuthorizationHeader } - pub fn not_found(message: impl Display) -> ResponseError { - ResponseError::NotFound(message.to_string()) + pub fn invalid_token(err: impl fmt::Display) -> ResponseError { + ResponseError::InvalidToken(err.to_string()) } - pub fn index_not_found(message: impl Display) -> ResponseError { - ResponseError::IndexNotFound(message.to_string()) + pub fn not_found(err: impl fmt::Display) -> ResponseError { + ResponseError::NotFound(err.to_string()) } - pub fn document_not_found(message: impl Display) -> ResponseError { - ResponseError::DocumentNotFound(message.to_string()) + pub fn index_not_found(err: impl fmt::Display) -> ResponseError { + ResponseError::IndexNotFound(err.to_string()) } - pub fn missing_header(message: impl Display) -> ResponseError { - ResponseError::MissingHeader(message.to_string()) + pub fn document_not_found(err: impl fmt::Display) -> ResponseError { + ResponseError::DocumentNotFound(err.to_string()) } - pub fn bad_parameter(name: impl Display, message: impl Display) -> ResponseError { - ResponseError::BadParameter(name.to_string(), message.to_string()) + pub fn missing_header(err: impl fmt::Display) -> ResponseError { + ResponseError::MissingHeader(err.to_string()) } - pub fn open_index(message: impl Display) -> ResponseError { - ResponseError::OpenIndex(message.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 create_index(message: impl Display) -> ResponseError { - ResponseError::CreateIndex(message.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 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::BadParameter(param, err) => write!(f, "Url parameter {} error: {}", param, 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) => 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"), + Self::FilterParsing(err) => write!(f, "parsing error: {}", err), + Self::MissingAuthorizationHeader => f.write_str("You must have an authorization token"), + 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), } } } -#[derive(Serialize, Deserialize)] -struct ErrorMessage { - message: String, +impl aweb::error::ResponseError for ResponseError { + fn error_response(&self) -> aweb::HttpResponse { + ResponseBuilder::new(self.status_code()).json(json!({ + "message": self.to_string(), + })) + } + + fn status_code(&self) -> StatusCode { + match *self { + 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::Internal(_) => StatusCode::INTERNAL_SERVER_ERROR, + Self::Maintenance => StatusCode::SERVICE_UNAVAILABLE, + } + } } -fn error(message: String, status: StatusCode) -> Response { - let message = ErrorMessage { message }; - tide::Response::new(status.as_u16()) - .body_json(&message) - .unwrap() +impl From for ResponseError { + fn from(err: meilisearch_core::HeedError) -> ResponseError { + ResponseError::Internal(err.to_string()) + } } -impl From for ResponseError { - fn from(err: serde_json::Error) -> ResponseError { - ResponseError::internal(err) +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 { - ResponseError::internal(err) - } -} - -impl From for ResponseError { - fn from(err: HeedError) -> ResponseError { - ResponseError::internal(err) - } -} - -impl From for ResponseError { - fn from(err: FstError) -> ResponseError { - ResponseError::internal(err) - } -} - -impl From for ResponseError { - fn from(err: SearchError) -> ResponseError { + use meilisearch_core::pest_error::LineColLocation::*; match err { - SearchError::FilterParsing(s) => ResponseError::FilterParsing(s), - _ => ResponseError::internal(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_core::settings::RankingRuleConversionError) -> ResponseError { - ResponseError::internal(err) +impl From for ResponseError { + fn from(err: meilisearch_schema::Error) -> ResponseError { + ResponseError::Internal(err.to_string()) } } -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")), - } +impl From for ResponseError { + fn from(err: actix_http::Error) -> ResponseError { + ResponseError::Internal(err.to_string()) } } diff --git a/meilisearch-http/src/helpers/authentication.rs b/meilisearch-http/src/helpers/authentication.rs new file mode 100644 index 000000000..f42898683 --- /dev/null +++ b/meilisearch-http/src/helpers/authentication.rs @@ -0,0 +1,103 @@ +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(); + // 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() { + 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/meilisearch.rs b/meilisearch-http/src/helpers/meilisearch.rs index a925562be..6c8046826 100644 --- a/meilisearch-http/src/helpers/meilisearch.rs +++ b/meilisearch-http/src/helpers/meilisearch.rs @@ -1,10 +1,7 @@ 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}; +use std::time::Instant; use indexmap::IndexMap; use log::error; @@ -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; @@ -103,7 +33,6 @@ impl IndexSearchExt for Index { attributes_to_retrieve: None, attributes_to_highlight: None, filters: None, - timeout: Duration::from_millis(30), matches: false, } } @@ -118,7 +47,6 @@ pub struct SearchBuilder<'a> { attributes_to_retrieve: Option>, attributes_to_highlight: Option>, filters: Option, - timeout: Duration, matches: bool, } @@ -159,27 +87,19 @@ 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 } - 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)? { @@ -203,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| { @@ -221,9 +139,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(); @@ -258,8 +175,10 @@ impl<'a> SearchBuilder<'a> { 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))?; + .map_err(|e| ResponseError::retrieve_document(doc.id.0, e))? + .ok_or(ResponseError::internal( + "Impossible to retrieve the document; Corrupted data", + ))?; let mut formatted = document.iter() .filter(|(key, _)| all_formatted.contains(key.as_str())) @@ -320,7 +239,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/helpers/mod.rs b/meilisearch-http/src/helpers/mod.rs index 640453b8a..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..d4f2822ad 100644 --- a/meilisearch-http/src/lib.rs +++ b/meilisearch-http/src/lib.rs @@ -8,3 +8,69 @@ 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())) + .app_data(web::JsonConfig::default().limit(1024 * 1024 * 10)) // Json Limit of 10Mb + .service(routes::load_html) + .service(routes::load_css) + .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) { + 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 0703e65c9..620571bfc 100644 --- a/meilisearch-http/src/main.rs +++ b/meilisearch-http/src/main.rs @@ -1,16 +1,13 @@ use std::{env, thread}; -use async_std::task; +use actix_cors::Cors; +use actix_web::{middleware, HttpServer}; use log::info; use main_error::MainError; -use structopt::StructOpt; -use tide::middleware::{Cors, RequestLogger, Origin}; -use http::header::HeaderValue; - 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::{create_app, index_update_callback}; +use structopt::StructOpt; mod analytics; @@ -18,7 +15,8 @@ mod analytics; #[global_allocator] static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; -pub fn main() -> Result<(), MainError> { +#[actix_rt::main] +async fn main() -> Result<(), MainError> { let opt = Opt::from_args(); match opt.env.as_ref() { @@ -29,7 +27,6 @@ pub fn main() -> Result<(), MainError> { .into(), ); } - env_logger::init(); } "development" => { env_logger::from_env(env_logger::Env::default().default_filter_or("info")).init(); @@ -50,17 +47,21 @@ pub fn main() -> Result<(), MainError> { print_launch_resume(&opt, &data); - let mut app = tide::with_state(data); + HttpServer::new(move || { + create_app(&data) + .wrap( + Cors::new() + .send_wildcard() + .allowed_header("x-meili-api-key") + .finish(), + ) + .wrap(middleware::Logger::default()) + .wrap(middleware::Compress::default()) + }) + .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 +77,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..ad90df8ca 100644 --- a/meilisearch-http/src/routes/document.rs +++ b/meilisearch-http/src/routes/document.rs @@ -1,62 +1,85 @@ use std::collections::{BTreeSet, HashSet}; +use actix_web::{web, HttpResponse}; +use actix_web_macros::{delete, get, post, put}; use indexmap::IndexMap; -use serde::{Deserialize, Serialize}; +use serde::Deserialize; use serde_json::Value; -use tide::{Request, Response}; -use crate::error::{ResponseError, SResult}; -use crate::helpers::tide::RequestExt; -use crate::helpers::tide::ACL::*; +use crate::error::ResponseError; +use crate::helpers::Authentication; +use crate::routes::{IndexParam, IndexUpdateResponse}; use crate::Data; -pub async fn get_document(ctx: Request) -> SResult { - ctx.is_allowed(Public)?; +type Document = IndexMap; - let index = ctx.index()?; - - let original_document_id = ctx.document_id()?; - let document_id = meilisearch_core::serde::compute_document_id(original_document_id.clone()); - - let db = &ctx.state().db; - let reader = db.main_read_txn()?; - - 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)?) +#[derive(Deserialize)] +struct DocumentParam { + index_uid: String, + document_id: String, } -#[derive(Default, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct IndexUpdateResponse { - pub update_id: u64, +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); } -pub async fn delete_document(ctx: Request) -> SResult { - ctx.is_allowed(Private)?; +#[get( + "/indexes/{index_uid}/documents/{document_id}", + wrap = "Authentication::Public" +)] +async fn get_document( + data: web::Data, + path: web::Path, +) -> Result { + let index = data + .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: Document = index + .document(&reader, None, document_id)? + .ok_or(ResponseError::document_not_found(&path.document_id))?; + + Ok(HttpResponse::Ok().json(response)) +} + +#[delete( + "/indexes/{index_uid}/documents/{document_id}", + wrap = "Authentication::Private" +)] +async fn delete_document( + data: web::Data, + path: web::Path, +) -> Result { + let index = data + .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 mut update_writer = data.db.update_write_txn()?; - 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 response_body = IndexUpdateResponse { update_id }; - Ok(tide::Response::new(202).body_json(&response_body)?) + 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, @@ -64,17 +87,21 @@ struct BrowseQuery { attributes_to_retrieve: Option, } -pub async fn get_all_documents(ctx: Request) -> SResult { - ctx.is_allowed(Private)?; +#[get("/indexes/{index_uid}/documents", wrap = "Authentication::Public")] +async fn get_all_documents( + data: web::Data, + path: web::Path, + params: web::Query, +) -> Result { + let index = data + .db + .open_index(&path.index_uid) + .ok_or(ResponseError::index_not_found(&path.index_uid))?; - let index = ctx.index()?; - let query: BrowseQuery = ctx.query().unwrap_or_default(); + let offset = params.offset.unwrap_or(0); + let limit = params.limit.unwrap_or(20); - let offset = query.offset.unwrap_or(0); - let limit = query.limit.unwrap_or(20); - - let db = &ctx.state().db; - let reader = db.main_read_txn()?; + let reader = data.db.main_read_txn()?; let documents_ids: Result, _> = index .documents_fields_counts @@ -83,29 +110,23 @@ pub async fn get_all_documents(ctx: Request) -> SResult { .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?; - let mut response_body = Vec::>::new(); + let attributes: Option> = params + .attributes_to_retrieve + .as_ref() + .map(|a| a.split(',').collect()); - 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); - } + let mut response = Vec::new(); + for document_id in documents_ids { + if let Ok(Some(document)) = + index.document::(&reader, attributes.as_ref(), document_id) + { + response.push(document); } } - Ok(tide::Response::new(200).body_json(&response_body)?) + Ok(HttpResponse::Ok().json(response)) } fn find_primary_key(document: &IndexMap) -> Option { @@ -117,40 +138,45 @@ 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, } -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 = data + .db + .open_index(&path.index_uid) + .ok_or(ResponseError::index_not_found(&path.index_uid))?; - let index = ctx.index()?; + let reader = data.db.main_read_txn()?; - let data: Vec> = - ctx.body_json().await.map_err(ResponseError::bad_request)?; - let query: UpdateDocumentsQuery = ctx.query().unwrap_or_default(); - - 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"))?; + .ok_or(ResponseError::internal("Impossible to retrieve the schema"))?; if schema.primary_key().is_none() { - let id = match query.primary_key { - 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")), - }, + 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 = db.main_write_txn()?; - schema.set_primary_key(&id).map_err(ResponseError::bad_request)?; + let mut writer = data.db.main_write_txn()?; + + schema + .set_primary_key(&id) + .map_err(ResponseError::bad_request)?; index.main.put_schema(&mut writer, &schema)?; writer.commit()?; } @@ -161,38 +187,56 @@ 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 mut update_writer = data.db.update_write_txn()?; let update_id = document_addition.finalize(&mut update_writer)?; update_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 add_or_replace_multiple_documents(ctx: Request) -> SResult { - update_multiple_documents(ctx, false).await +#[post("/indexes/{index_uid}/documents", wrap = "Authentication::Private")] +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", wrap = "Authentication::Private")] +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", + wrap = "Authentication::Private" +)] +async fn delete_documents( + data: web::Data, + path: web::Path, + body: web::Json>, +) -> Result { + let index = data + .db + .open_index(&path.index_uid) + .ok_or(ResponseError::index_not_found(&path.index_uid))?; - let data: Vec = 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()?; + let mut writer = data.db.update_write_txn()?; 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)); @@ -203,21 +247,24 @@ pub async fn delete_multiple_documents(mut ctx: Request) -> SResult) -> SResult { - ctx.is_allowed(Private)?; +#[delete("/indexes/{index_uid}/documents", wrap = "Authentication::Private")] +async fn clear_all_documents( + data: web::Data, + path: web::Path, +) -> Result { + let index = data + .db + .open_index(&path.index_uid) + .ok_or(ResponseError::index_not_found(&path.index_uid))?; - let index = ctx.index()?; - - let db = &ctx.state().db; - let mut writer = db.update_write_txn()?; + let mut writer = data.db.update_write_txn()?; let update_id = index.clear_all(&mut writer)?; + 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/health.rs b/meilisearch-http/src/routes/health.rs index 66a59627a..4f7795e48 100644 --- a/meilisearch-http/src/routes/health.rs +++ b/meilisearch-http/src/routes/health.rs @@ -1,47 +1,47 @@ -use crate::error::{ResponseError, SResult}; -use crate::helpers::tide::RequestExt; -use crate::helpers::tide::ACL::*; -use crate::Data; - +use actix_web::{web, HttpResponse}; +use actix_web_macros::{get, put}; use heed::types::{Str, Unit}; use serde::Deserialize; -use tide::{Request, Response}; + +use crate::error::ResponseError; +use crate::helpers::Authentication; +use crate::Data; 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()?; +pub fn services(cfg: &mut web::ServiceConfig) { + cfg.service(get_health).service(change_healthyness); +} - let common_store = ctx.state().db.common_store(); +#[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(); if let Ok(Some(_)) = common_store.get::<_, Str, Unit>(&reader, UNHEALTHY_KEY) { 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(); +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)?; writer.commit()?; - 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(); +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, &())?; writer.commit()?; - Ok(tide::Response::new(200)) + Ok(HttpResponse::Ok().finish()) } #[derive(Deserialize, Clone)] @@ -49,12 +49,14 @@ 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", wrap = "Authentication::Private")] +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/index.rs b/meilisearch-http/src/routes/index.rs index 5666ff0da..d43967bdd 100644 --- a/meilisearch-http/src/routes/index.rs +++ b/meilisearch-http/src/routes/index.rs @@ -1,16 +1,25 @@ +use actix_web::{web, HttpResponse}; +use actix_web_macros::{delete, get, post, put}; 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 crate::error::{IntoInternalError, ResponseError, SResult}; -use crate::helpers::tide::RequestExt; -use crate::helpers::tide::ACL::*; +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"; @@ -20,24 +29,42 @@ fn generate_uid() -> String { .collect() } -pub async fn list_indexes(ctx: Request) -> SResult { - ctx.is_allowed(Private)?; +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +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", wrap = "Authentication::Private")] +async fn list_indexes(data: web::Data) -> Result { + let reader = data.db.main_read_txn()?; - let db = &ctx.state().db; - let reader = db.main_read_txn()?; + let mut response = Vec::new(); - 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)?.ok_or(ResponseError::internal( + "Impossible to get the name of an index", + ))?; + let created_at = index + .main + .created_at(&reader)? + .ok_or(ResponseError::internal( + "Impossible to get the create date of an index", + ))?; + let updated_at = index + .main + .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) { Ok(Some(schema)) => match schema.primary_key() { @@ -54,7 +81,7 @@ pub async fn list_indexes(ctx: Request) -> SResult { updated_at, primary_key, }; - response_body.push(index_response); + response.push(index_response); } None => error!( "Index {} is referenced in the indexes list but cannot be found", @@ -63,31 +90,36 @@ pub async fn list_indexes(ctx: Request) -> SResult { } } - Ok(tide::Response::new(200).body_json(&response_body)?) + Ok(HttpResponse::Ok().json(response)) } -#[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}", wrap = "Authentication::Private")] +async fn get_index( + data: web::Data, + path: web::Path, +) -> Result { + let index = data + .db + .open_index(&path.index_uid) + .ok_or(ResponseError::index_not_found(&path.index_uid))?; -pub async fn get_index(ctx: Request) -> SResult { - ctx.is_allowed(Private)?; + let reader = data.db.main_read_txn()?; - let index = ctx.index()?; - - 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)?.ok_or(ResponseError::internal( + "Impossible to get the name of an index", + ))?; + let created_at = index + .main + .created_at(&reader)? + .ok_or(ResponseError::internal( + "Impossible to get the create date of an index", + ))?; + let updated_at = index + .main + .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) { Ok(Some(schema)) => match schema.primary_key() { @@ -97,15 +129,13 @@ pub async fn get_index(ctx: Request) -> SResult { _ => None, }; - let response_body = IndexResponse { + Ok(HttpResponse::Ok().json(IndexResponse { name, - uid, + uid: path.index_uid.clone(), created_at, updated_at, primary_key, - }; - - Ok(tide::Response::new(200).body_json(&response_body)?) + })) } #[derive(Debug, Deserialize)] @@ -116,86 +146,74 @@ struct IndexCreateRequest { 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", wrap = "Authentication::Private")] +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", )); } - let db = &ctx.state().db; - - let uid = match body.uid { + 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); } } 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(ResponseError::create_index)?; + + let mut writer = data.db.main_write_txn()?; + + let name = body.name.as_ref().unwrap_or(&uid); + created_index.main.put_name(&mut writer, name)?; - 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()?; + .ok_or(ResponseError::internal("Impossible to read created at"))?; + let updated_at = created_index .main .updated_at(&writer)? - .into_internal_error()?; + .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(&mut writer)? { - schema.set_primary_key(&id).map_err(ResponseError::bad_request)?; + if let Some(mut schema) = created_index.main.schema(&writer)? { + schema + .set_primary_key(&id) + .map_err(ResponseError::bad_request)?; created_index.main.put_schema(&mut writer, &schema)?; } } writer.commit()?; - let response_body = IndexCreateResponse { - name, + Ok(HttpResponse::Created().json(IndexResponse { + name: name.to_string(), 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)] @@ -215,26 +233,25 @@ struct UpdateIndexResponse { primary_key: Option, } -pub async fn update_index(mut ctx: Request) -> SResult { - ctx.is_allowed(Private)?; +#[put("/indexes/{index_uid}", wrap = "Authentication::Private")] +async fn update_index( + data: web::Data, + path: web::Path, + body: web::Json, +) -> Result { + let index = data + .db + .open_index(&path.index_uid) + .ok_or(ResponseError::index_not_found(&path.index_uid))?; - let body = ctx - .body_json::() - .await - .map_err(ResponseError::bad_request)?; + let mut writer = data.db.main_write_txn()?; - let index_uid = ctx.url_param("index")?; - let index = ctx.index()?; - - 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 { + index.main.put_name(&mut writer, name)?; } 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(&writer)? { match schema.primary_key() { Some(_) => { return Err(ResponseError::bad_request( @@ -242,9 +259,7 @@ pub async fn update_index(mut ctx: Request) -> SResult { )); } None => { - schema - .set_primary_key(&id) - .map_err(ResponseError::bad_request)?; + schema.set_primary_key(&id)?; index.main.put_schema(&mut writer, &schema)?; } } @@ -254,10 +269,23 @@ pub async fn update_index(mut ctx: Request) -> SResult { index.main.put_updated_at(&mut writer)?; writer.commit()?; - 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()?; + + 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)? + .ok_or(ResponseError::internal( + "Impossible to get the create date of an index", + ))?; + let updated_at = index + .main + .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) { Ok(Some(schema)) => match schema.primary_key() { @@ -267,86 +295,70 @@ pub async fn update_index(mut ctx: Request) -> SResult { _ => None, }; - let response_body = UpdateIndexResponse { + Ok(HttpResponse::Ok().json(IndexResponse { name, - uid: index_uid, + uid: path.index_uid.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}", wrap = "Authentication::Private")] +async fn delete_index( + data: web::Data, + path: web::Path, +) -> Result { + data.db.delete_index(&path.index_uid)?; - let db = &ctx.state().db; - let reader = db.update_read_txn()?; - - 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) + Ok(HttpResponse::NoContent().finish()) } -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()?; +#[derive(Deserialize)] +struct UpdateParam { + index_uid: String, + update_id: u64, +} + +#[get( + "/indexes/{index_uid}/updates/{update_id}", + wrap = "Authentication::Private" +)] +async fn get_update_status( + data: web::Data, + path: web::Path, +) -> Result { + let index = data + .db + .open_index(&path.index_uid) + .ok_or(ResponseError::index_not_found(&path.index_uid))?; + + let reader = data.db.update_read_txn()?; + + 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 + ))), + } +} + +#[get("/indexes/{index_uid}/updates", wrap = "Authentication::Private")] +async fn get_all_updates_status( + data: web::Data, + path: web::Path, +) -> Result { + let index = data + .db + .open_index(&path.index_uid) + .ok_or(ResponseError::index_not_found(&path.index_uid))?; + + let reader = data.db.update_read_txn()?; + 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); - } - } + + Ok(HttpResponse::Ok().json(response)) } diff --git a/meilisearch-http/src/routes/key.rs b/meilisearch-http/src/routes/key.rs index fe0feacf4..46e59cd6b 100644 --- a/meilisearch-http/src/routes/key.rs +++ b/meilisearch-http/src/routes/key.rs @@ -1,17 +1,26 @@ -use crate::error::SResult; -use crate::helpers::tide::RequestExt; -use crate::helpers::tide::ACL::*; +use actix_web::web; +use actix_web::HttpResponse; +use actix_web_macros::get; +use serde::Serialize; + +use crate::helpers::Authentication; use crate::Data; -use serde_json::json; -use tide::{Request, Response}; -pub async fn list(ctx: Request) -> SResult { - ctx.is_allowed(Admin)?; - - let keys = &ctx.state().api_keys; - - Ok(tide::Response::new(200).body_json(&json!({ - "private": keys.private, - "public": keys.public, - }))?) +pub fn services(cfg: &mut web::ServiceConfig) { + cfg.service(list); +} + +#[derive(Serialize)] +struct KeysResponse { + private: Option, + public: Option, +} + +#[get("/keys", wrap = "Authentication::Admin")] +async fn list(data: web::Data) -> HttpResponse { + let api_keys = data.api_keys.clone(); + 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 1d5942a08..a63e0dc75 100644 --- a/meilisearch-http/src/routes/mod.rs +++ b/meilisearch-http/src/routes/mod.rs @@ -1,7 +1,5 @@ -use crate::data::Data; -use std::future::Future; -use tide::IntoResponse; -use tide::Response; +use actix_web::{get, HttpResponse}; +use serde::{Deserialize, Serialize}; pub mod document; pub mod health; @@ -13,118 +11,33 @@ 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(Deserialize)] +pub struct IndexParam { + index_uid: String, +} + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +pub struct IndexUpdateResponse { + pub update_id: u64, +} + +impl IndexUpdateResponse { + pub fn with_id(update_id: u64) -> Self { + Self { update_id } } } -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/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/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/: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/: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/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))); +#[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()) } diff --git a/meilisearch-http/src/routes/search.rs b/meilisearch-http/src/routes/search.rs index 4d547d268..1189e79a8 100644 --- a/meilisearch-http/src/routes/search.rs +++ b/meilisearch-http/src/routes/search.rs @@ -1,19 +1,21 @@ -use std::collections::HashMap; -use std::collections::HashSet; -use std::time::Duration; +use std::collections::{HashSet, HashMap}; use log::warn; -use meilisearch_core::Index; -use rayon::iter::{IntoParallelIterator, ParallelIterator}; -use serde::{Deserialize, Serialize}; -use tide::{Request, Response}; +use actix_web::web; +use actix_web::HttpResponse; +use actix_web_macros::get; +use serde::Deserialize; -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::IndexSearchExt; +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)] struct SearchQuery { @@ -25,38 +27,39 @@ struct SearchQuery { crop_length: Option, attributes_to_highlight: Option, filters: Option, - timeout_ms: Option, matches: Option, } -pub async fn search_with_url_query(ctx: Request) -> SResult { - ctx.is_allowed(Public)?; +#[get("/indexes/{index_uid}/search", wrap = "Authentication::Public")] +async fn search_with_url_query( + data: web::Data, + path: web::Path, + params: web::Query, +) -> Result { + let index = data + .db + .open_index(&path.index_uid) + .ok_or(ResponseError::index_not_found(&path.index_uid))?; - let index = ctx.index()?; - let db = &ctx.state().db; - let reader = db.main_read_txn()?; + let reader = data.db.main_read_txn()?; let schema = index .main .schema(&reader)? - .ok_or(ResponseError::open_index("No Schema found"))?; + .ok_or(ResponseError::internal("Impossible to retrieve the 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,144 +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 { - 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(); } } - 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)), - }; - - Ok(tide::Response::new(200).body_json(&response).unwrap()) -} - -#[derive(Clone, Deserialize)] -#[serde(rename_all = "camelCase", deny_unknown_fields)] -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")] -struct SearchMultiBodyResponse { - hits: HashMap>, - offset: usize, - hits_per_page: usize, - processing_time_ms: usize, - 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)?; - - 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(); - break; - } - } - - let mut offset = 0; - let mut count = 20; - - if let Some(body_offset) = body.offset { - if let Some(limit) = body.limit { - offset = body_offset; - count = limit; - } - } - - let offset = offset; - let count = count; - let db = &ctx.state().db; - let par_body = body.clone(); - let responses_per_index: Vec> = 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 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 = db.main_read_txn()?; - let response = search_builder.search(&reader)?; - Ok((index_uid, response)) - }) - .collect(); - - let mut hits_map = HashMap::new(); - - 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); - } - } - - let response = SearchMultiBodyResponse { - hits: hits_map, - offset, - hits_per_page: count, - processing_time_ms: max_query_time, - query: body.query, - }; - - Ok(tide::Response::new(200).body_json(&response).unwrap()) + 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 9cdbcb7a3..262af8b84 100644 --- a/meilisearch-http/src/routes/setting.rs +++ b/meilisearch-http/src/routes/setting.rs @@ -1,18 +1,66 @@ +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 tide::{Request, Response}; -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::helpers::Authentication; +use crate::routes::{IndexParam, IndexUpdateResponse}; use crate::Data; -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()?; +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, +) -> Result { + let index = data + .db + .open_index(&path.index_uid) + .ok_or(ResponseError::index_not_found(&path.index_uid))?; + + let mut writer = data.db.update_write_txn()?; + let settings = body + .into_inner() + .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))) +} + +#[get("/indexes/{index_uid}/settings", wrap = "Authentication::Private")] +async fn get_all( + data: web::Data, + path: web::Path, +) -> Result { + let index = data + .db + .open_index(&path.index_uid) + .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()?; @@ -46,14 +94,14 @@ pub async fn get_all(ctx: Request) -> SResult { 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::>() }); @@ -69,30 +117,19 @@ 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; - - 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)?) -} - -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", wrap = "Authentication::Private")] +async fn delete_all( + data: web::Data, + path: web::Path, +) -> Result { + let index = data + .db + .open_index(&path.index_uid) + .ok_or(ResponseError::index_not_found(&path.index_uid))?; + let mut writer = data.db.update_write_txn()?; let settings = SettingsUpdate { ranking_rules: UpdateState::Clear, @@ -106,18 +143,24 @@ pub async fn delete_all(ctx: Request) -> SResult { }; 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))) } -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()?; +#[get( + "/indexes/{index_uid}/settings/ranking-rules", + wrap = "Authentication::Private" +)] +async fn get_rules( + data: web::Data, + path: web::Path, +) -> Result { + let index = data + .db + .open_index(&path.index_uid) + .ok_or(ResponseError::index_not_found(&path.index_uid))?; + let reader = data.db.main_read_txn()?; let ranking_rules = index .main @@ -127,35 +170,49 @@ pub async fn get_rules(ctx: Request) -> SResult { .map(|r| r.to_string()) .collect::>(); - Ok(tide::Response::new(200).body_json(&ranking_rules).unwrap()) + Ok(HttpResponse::Ok().json(ranking_rules)) } -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; +#[post( + "/indexes/{index_uid}/settings/ranking-rules", + wrap = "Authentication::Private" +)] +async fn update_rules( + data: web::Data, + path: web::Path, + body: web::Json>>, +) -> Result { + let index = data + .db + .open_index(&path.index_uid) + .ok_or(ResponseError::index_not_found(&path.index_uid))?; let settings = Settings { - ranking_rules: Some(ranking_rules), + ranking_rules: Some(body.into_inner()), ..Settings::default() }; - let mut writer = db.update_write_txn()?; + 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()?; - 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_rules(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/ranking-rules", + wrap = "Authentication::Private" +)] +async fn delete_rules( + data: web::Data, + path: web::Path, +) -> Result { + let index = data + .db + .open_index(&path.index_uid) + .ok_or(ResponseError::index_not_found(&path.index_uid))?; + let mut writer = data.db.update_write_txn()?; let settings = SettingsUpdate { ranking_rules: UpdateState::Clear, @@ -166,49 +223,67 @@ pub async fn delete_rules(ctx: Request) -> SResult { 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 get_distinct(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/distinct-attribute", + wrap = "Authentication::Private" +)] +async fn get_distinct( + data: web::Data, + path: web::Path, +) -> Result { + let index = data + .db + .open_index(&path.index_uid) + .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(tide::Response::new(200) - .body_json(&distinct_attribute) - .unwrap()) + Ok(HttpResponse::Ok().json(distinct_attribute)) } -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; +#[post( + "/indexes/{index_uid}/settings/distinct-attribute", + wrap = "Authentication::Private" +)] +async fn update_distinct( + data: web::Data, + path: web::Path, + body: web::Json>, +) -> Result { + let index = data + .db + .open_index(&path.index_uid) + .ok_or(ResponseError::index_not_found(&path.index_uid))?; let settings = Settings { - distinct_attribute: Some(distinct_attribute), + distinct_attribute: Some(body.into_inner()), ..Settings::default() }; - let mut writer = db.update_write_txn()?; + 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()?; - 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_distinct(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/distinct-attribute", + wrap = "Authentication::Private" +)] +async fn delete_distinct( + data: web::Data, + path: web::Path, +) -> Result { + let index = data + .db + .open_index(&path.index_uid) + .ok_or(ResponseError::index_not_found(&path.index_uid))?; + let mut writer = data.db.update_write_txn()?; let settings = SettingsUpdate { distinct_attribute: UpdateState::Clear, @@ -219,156 +294,199 @@ pub async fn delete_distinct(ctx: Request) -> SResult { 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 get_searchable(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/searchable-attributes", + wrap = "Authentication::Private" +)] +async fn get_searchable( + data: web::Data, + path: web::Path, +) -> Result { + let index = data + .db + .open_index(&path.index_uid) + .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()); + schema.map(|s| s.indexed_name().iter().map(|i| i.to_string()).collect()); - Ok(tide::Response::new(200) - .body_json(&searchable_attributes) - .unwrap()) + Ok(HttpResponse::Ok().json(searchable_attributes)) } -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; +#[post( + "/indexes/{index_uid}/settings/searchable-attributes", + wrap = "Authentication::Private" +)] +async fn update_searchable( + data: web::Data, + path: web::Path, + body: web::Json>>, +) -> Result { + let index = data + .db + .open_index(&path.index_uid) + .ok_or(ResponseError::index_not_found(&path.index_uid))?; let settings = Settings { - searchable_attributes: Some(searchable_attributes), + searchable_attributes: Some(body.into_inner()), ..Settings::default() }; - let mut writer = db.update_write_txn()?; + 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()?; - 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; +#[delete( + "/indexes/{index_uid}/settings/searchable-attributes", + wrap = "Authentication::Private" +)] +async fn delete_searchable( + data: web::Data, + path: web::Path, +) -> Result { + let index = data + .db + .open_index(&path.index_uid) + .ok_or(ResponseError::index_not_found(&path.index_uid))?; let settings = SettingsUpdate { searchable_attributes: UpdateState::Clear, ..SettingsUpdate::default() }; - let mut writer = db.update_write_txn()?; + let mut writer = data.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)?) + Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } -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()?; +#[get( + "/indexes/{index_uid}/settings/displayed-attributes", + wrap = "Authentication::Private" +)] +async fn get_displayed( + data: web::Data, + path: web::Path, +) -> Result { + let index = data + .db + .open_index(&path.index_uid) + .ok_or(ResponseError::index_not_found(&path.index_uid))?; + let reader = data.db.main_read_txn()?; 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(HttpResponse::Ok().json(displayed_attributes)) } -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; +#[post( + "/indexes/{index_uid}/settings/displayed-attributes", + wrap = "Authentication::Private" +)] +async fn update_displayed( + data: web::Data, + path: web::Path, + body: web::Json>>, +) -> Result { + let index = data + .db + .open_index(&path.index_uid) + .ok_or(ResponseError::index_not_found(&path.index_uid))?; let settings = Settings { - displayed_attributes: Some(displayed_attributes), + displayed_attributes: Some(body.into_inner()), ..Settings::default() }; - let mut writer = db.update_write_txn()?; + 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()?; - 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; +#[delete( + "/indexes/{index_uid}/settings/displayed-attributes", + wrap = "Authentication::Private" +)] +async fn delete_displayed( + data: web::Data, + path: web::Path, +) -> Result { + let index = data + .db + .open_index(&path.index_uid) + .ok_or(ResponseError::index_not_found(&path.index_uid))?; let settings = SettingsUpdate { displayed_attributes: UpdateState::Clear, ..SettingsUpdate::default() }; - let mut writer = db.update_write_txn()?; + let mut writer = data.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)?) + Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } -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()?; +#[get( + "/indexes/{index_uid}/settings/accept-new-fields", + wrap = "Authentication::Private" +)] +async fn get_accept_new_fields( + data: web::Data, + path: web::Path, +) -> Result { + let index = data + .db + .open_index(&path.index_uid) + .ok_or(ResponseError::index_not_found(&path.index_uid))?; + let reader = data.db.main_read_txn()?; let schema = index.main.schema(&reader)?; let accept_new_fields = schema.map(|s| s.accept_new_fields()); - Ok(tide::Response::new(200) - .body_json(&accept_new_fields) - .unwrap()) + Ok(HttpResponse::Ok().json(accept_new_fields)) } -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; +#[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>, +) -> Result { + let index = data + .db + .open_index(&path.index_uid) + .ok_or(ResponseError::index_not_found(&path.index_uid))?; let settings = Settings { - accept_new_fields: Some(accept_new_fields), + accept_new_fields: Some(body.into_inner()), ..Settings::default() }; - let mut writer = db.update_write_txn()?; + 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()?; - 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/stats.rs b/meilisearch-http/src/routes/stats.rs index 16d449c74..92945e588 100644 --- a/meilisearch-http/src/routes/stats.rs +++ b/meilisearch-http/src/routes/stats.rs @@ -1,18 +1,28 @@ use std::collections::HashMap; +use actix_web::web; +use actix_web::HttpResponse; +use actix_web_macros::get; 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::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")] struct IndexStatsResponse { @@ -21,26 +31,35 @@ struct IndexStatsResponse { 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", wrap = "Authentication::Private")] +async fn index_stats( + data: web::Data, + path: web::Path, +) -> Result { + let index = data + .db + .open_index(&path.index_uid) + .ok_or(ResponseError::index_not_found(&path.index_uid))?; - let response = IndexStatsResponse { + let reader = data.db.main_read_txn()?; + + let number_of_documents = index.main.number_of_documents(&reader)?; + + let fields_frequency = index.main.fields_frequency(&reader)?.unwrap_or_default(); + + let update_reader = data.db.update_read_txn()?; + + let is_indexing = + data.is_indexing(&update_reader, &path.index_uid)? + .ok_or(ResponseError::internal( + "Impossible to know if the database is indexing", + ))?; + + Ok(HttpResponse::Ok().json(IndexStatsResponse { number_of_documents, is_indexing, fields_frequency, - }; - Ok(tide::Response::new(200).body_json(&response).unwrap()) + })) } #[derive(Serialize)] @@ -51,29 +70,25 @@ struct StatsResult { indexes: HashMap, } -pub async fn get_stats(ctx: Request) -> SResult { - ctx.is_allowed(Admin)?; - +#[get("/stats", wrap = "Authentication::Private")] +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()?; + let update_reader = data.db.update_read_txn()?; - 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 fields_frequency = index.main.fields_frequency(&reader)?.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)?.ok_or( + ResponseError::internal("Impossible to know if the database is indexing"), + )?; let response = IndexStatsResponse { number_of_documents, @@ -89,22 +104,20 @@ 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) .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)?; - let response = StatsResult { + Ok(HttpResponse::Ok().json(StatsResult { database_size, last_update, indexes: index_list, - }; - - Ok(tide::Response::new(200).body_json(&response).unwrap()) + })) } #[derive(Serialize)] @@ -115,20 +128,18 @@ struct VersionResponse { pkg_version: String, } -pub async fn get_version(ctx: Request) -> SResult { - ctx.is_allowed(Admin)?; - let response = VersionResponse { +#[get("/version", wrap = "Authentication::Private")] +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(), - }; - - Ok(tide::Response::new(200).body_json(&response).unwrap()) + }) } #[derive(Serialize)] #[serde(rename_all = "camelCase")] -pub(crate) struct SysGlobal { +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 { +struct SysProcess { memory: u64, cpu: f32, } @@ -168,7 +179,7 @@ impl SysProcess { #[derive(Serialize)] #[serde(rename_all = "camelCase")] -pub(crate) struct SysInfo { +struct SysInfo { memory_usage: f64, processor_usage: Vec, global: SysGlobal, @@ -186,7 +197,8 @@ impl SysInfo { } } -pub(crate) fn report(pid: Pid) -> SysInfo { +#[get("/sys-info", wrap = "Authentication::Private")] +async fn get_sys_info(data: web::Data) -> HttpResponse { let mut sys = System::new(); let mut info = SysInfo::new(); @@ -200,28 +212,29 @@ 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()) + HttpResponse::Ok().json(info) } #[derive(Serialize)] #[serde(rename_all = "camelCase")] -pub(crate) struct SysGlobalPretty { +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 { +struct SysProcessPretty { memory: String, cpu: String, } @@ -261,7 +274,7 @@ impl SysProcessPretty { #[derive(Serialize)] #[serde(rename_all = "camelCase")] -pub(crate) struct SysInfoPretty { +struct SysInfoPretty { memory_usage: String, processor_usage: Vec, global: SysGlobalPretty, @@ -279,7 +292,8 @@ impl SysInfoPretty { } } -pub(crate) fn report_pretty(pid: Pid) -> SysInfoPretty { +#[get("/sys-info/pretty", wrap = "Authentication::Private")] +async fn get_sys_info_pretty(data: web::Data) -> HttpResponse { let mut sys = System::new(); let mut info = SysInfoPretty::new(); @@ -297,21 +311,25 @@ pub(crate) fn report_pretty(pid: Pid) -> SysInfoPretty { 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(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()) + HttpResponse::Ok().json(info) } diff --git a/meilisearch-http/src/routes/stop_words.rs b/meilisearch-http/src/routes/stop_words.rs index f9d0154a7..21fc9281f 100644 --- a/meilisearch-http/src/routes/stop_words.rs +++ b/meilisearch-http/src/routes/stop_words.rs @@ -1,63 +1,83 @@ +use actix_web::{web, HttpResponse}; +use actix_web_macros::{delete, get, post}; +use meilisearch_core::settings::{SettingsUpdate, UpdateState}; use std::collections::BTreeSet; -use meilisearch_core::settings::{SettingsUpdate, UpdateState}; -use tide::{Request, Response}; - -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::helpers::Authentication; +use crate::routes::{IndexParam, IndexUpdateResponse}; use crate::Data; -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()?; +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 { + let index = data + .db + .open_index(&path.index_uid) + .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(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", + wrap = "Authentication::Private" +)] +async fn update( + data: web::Data, + path: web::Path, + body: web::Json>, +) -> Result { + let index = data + .db + .open_index(&path.index_uid) + .ok_or(ResponseError::index_not_found(&path.index_uid))?; let settings = SettingsUpdate { - stop_words: UpdateState::Update(data), + stop_words: UpdateState::Update(body.into_inner()), ..SettingsUpdate::default() }; + let mut writer = data.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)?) + 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", + wrap = "Authentication::Private" +)] +async fn delete( + data: web::Data, + path: web::Path, +) -> Result { + let index = data + .db + .open_index(&path.index_uid) + .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()?; 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))) } diff --git a/meilisearch-http/src/routes/synonym.rs b/meilisearch-http/src/routes/synonym.rs index 60fca7e98..973f591ab 100644 --- a/meilisearch-http/src/routes/synonym.rs +++ b/meilisearch-http/src/routes/synonym.rs @@ -1,29 +1,39 @@ use std::collections::BTreeMap; +use actix_web::{web, HttpResponse}; +use actix_web_macros::{delete, get, post}; use indexmap::IndexMap; use meilisearch_core::settings::{SettingsUpdate, UpdateState}; -use tide::{Request, Response}; -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::helpers::Authentication; +use crate::routes::{IndexParam, IndexUpdateResponse}; use crate::Data; -pub async fn get(ctx: Request) -> SResult { - ctx.is_allowed(Private)?; - let index = ctx.index()?; +pub fn services(cfg: &mut web::ServiceConfig) { + cfg.service(get).service(update).service(delete); +} - let db = &ctx.state().db; - let reader = db.main_read_txn()?; +#[get( + "/indexes/{index_uid}/settings/synonyms", + wrap = "Authentication::Private" +)] +async fn get( + data: web::Data, + path: web::Path, +) -> Result { + let index = data + .db + .open_index(&path.index_uid) + .ok_or(ResponseError::index_not_found(&path.index_uid))?; + + let reader = data.db.main_read_txn()?; 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())?; @@ -33,50 +43,57 @@ pub async fn get(ctx: Request) -> SResult { } } - 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", + wrap = "Authentication::Private" +)] +async fn update( + data: web::Data, + path: web::Path, + body: web::Json>>, +) -> Result { + let index = data + .db + .open_index(&path.index_uid) + .ok_or(ResponseError::index_not_found(&path.index_uid))?; let settings = SettingsUpdate { - synonyms: UpdateState::Update(data), + synonyms: UpdateState::Update(body.into_inner()), ..SettingsUpdate::default() }; + let mut writer = data.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)?) + 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", + wrap = "Authentication::Private" +)] +async fn delete( + data: web::Data, + path: web::Path, +) -> Result { + let index = data + .db + .open_index(&path.index_uid) + .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()?; 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))) } 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 a223c4757..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,71 +64,71 @@ 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)); // 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"; - 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)); // 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, @@ -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 e0b44b9b5..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,12 +671,12 @@ 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); - assert_eq!(response[0]["status"], "processed"); + assert_eq!(response[0]["status"], "processed"); } diff --git a/meilisearch-http/tests/search.rs b/meilisearch-http/tests/search.rs index faa5d9e9b..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 -#[test] -fn search_with_limit() { +#[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 @@ fn search_with_limit() { } ]); - 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); } @@ -82,8 +77,11 @@ fn search_with_limit() { // q: Captain // limit: 3 // offset: 1 -#[test] -fn search_with_offset() { +#[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 @@ fn search_with_offset() { } ]); - 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); } @@ -149,8 +147,11 @@ 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 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 @@ fn search_with_attribute_to_highlight_wildcard() { } ]); - 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); } @@ -198,8 +199,11 @@ 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 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 @@ fn search_with_attribute_to_highlight_1() { } ]); - 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); } @@ -247,8 +251,11 @@ 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 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 @@ fn search_with_attribute_to_highlight_title_tagline() { } ]); - 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); } @@ -296,8 +303,11 @@ 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 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 @@ fn search_with_attribute_to_highlight_title_overview() { } ]); - 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); } @@ -345,8 +355,11 @@ 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 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 @@ fn search_with_matches() { } ]); - 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); } @@ -392,8 +405,11 @@ fn search_with_matches() { // limit: 1 // attributesToCrop: overview // cropLength: 20 -#[test] -fn search_witch_crop() { +#[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 @@ fn search_witch_crop() { } ]); - 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); } @@ -441,8 +457,11 @@ 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 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 @@ fn search_with_attributes_to_retrieve() { } ]); - 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); } @@ -462,8 +481,11 @@ 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 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 @@ fn search_with_attributes_to_retrieve_wildcard() { } ]); - 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); } @@ -494,8 +516,11 @@ 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 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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); } @@ -744,8 +769,11 @@ 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 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 @@ fn search_with_attributes_to_highlight_and_matches() { } ]); - 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); } @@ -810,8 +838,11 @@ 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 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 @@ fn search_with_attributes_to_highlight_and_matches_and_crop() { } ]); - 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); } @@ -874,8 +905,11 @@ 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 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 @@ fn search_with_differents_attributes() { } ]); - 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); } @@ -899,8 +933,11 @@ 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 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 @@ fn search_with_differents_attributes_2() { } ]); - 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); } @@ -923,8 +960,11 @@ 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 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 @@ fn search_with_differents_attributes_3() { } ]); - 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); } @@ -947,8 +987,11 @@ 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 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 @@ fn search_with_differents_attributes_4() { } ]); - 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); } @@ -972,8 +1015,11 @@ 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 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 @@ fn search_with_differents_attributes_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); } @@ -1000,8 +1046,11 @@ 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 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 @@ fn search_with_differents_attributes_6() { } ]); - 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); } @@ -1028,8 +1077,11 @@ 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 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 @@ fn search_with_differents_attributes_7() { } ]); - 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); } @@ -1056,8 +1108,11 @@ 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 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 @@ fn search_with_differents_attributes_8() { } ]); - 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); } 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 807aa1cd2..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,15 +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 @@ -300,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)); @@ -312,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 @@ -322,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 f203216ff..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,54 +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" - ]); + let body = json!(["asc(title)", "typo"]); - server.update_ranking_rules(body); + server.update_ranking_rules(body).await; // 2 - Add documents @@ -170,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 @@ -180,9 +177,8 @@ 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