From 343760228ed1c6fa176aa2a436e373186572e2eb Mon Sep 17 00:00:00 2001 From: Frank Elsinga Date: Thu, 20 Jun 2024 03:17:22 +0200 Subject: [PATCH] added a testcase --- server/Cargo.lock | 401 +++++++++++++++++++++++++--- server/main-api/Cargo.toml | 1 + server/main-api/src/calendar/mod.rs | 130 ++++++--- 3 files changed, 459 insertions(+), 73 deletions(-) diff --git a/server/Cargo.lock b/server/Cargo.lock index a21d4229b..400113281 100644 --- a/server/Cargo.lock +++ b/server/Cargo.lock @@ -109,7 +109,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" dependencies = [ "quote", - "syn 2.0.61", + "syn 2.0.66", ] [[package]] @@ -223,7 +223,7 @@ dependencies = [ "actix-router", "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.66", ] [[package]] @@ -404,7 +404,7 @@ checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" dependencies = [ "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.66", ] [[package]] @@ -437,7 +437,7 @@ checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.66", ] [[package]] @@ -559,6 +559,56 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bollard" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0aed08d3adb6ebe0eff737115056652670ae290f177759aac19c30456135f94c" +dependencies = [ + "base64 0.22.1", + "bollard-stubs", + "bytes", + "futures-core", + "futures-util", + "hex", + "home", + "http 1.1.0", + "http-body-util", + "hyper 1.3.1", + "hyper-named-pipe", + "hyper-rustls 0.26.0", + "hyper-util", + "hyperlocal-next", + "log", + "pin-project-lite", + "rustls 0.22.4", + "rustls-native-certs", + "rustls-pemfile 2.1.2", + "rustls-pki-types", + "serde", + "serde_derive", + "serde_json", + "serde_repr", + "serde_urlencoded", + "thiserror", + "tokio", + "tokio-util", + "tower-service", + "url", + "winapi", +] + +[[package]] +name = "bollard-stubs" +version = "1.44.0-rc.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "709d9aa1c37abb89d40f19f5d0ad6f0d88cb1581264e571c9350fc5bb89cf1c5" +dependencies = [ + "serde", + "serde_repr", + "serde_with", +] + [[package]] name = "brotli" version = "6.0.0" @@ -637,7 +687,7 @@ dependencies = [ "cached_proc_macro_types", "directories", "futures", - "hashbrown", + "hashbrown 0.14.5", "instant", "once_cell", "rmp-serde", @@ -656,7 +706,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.66", ] [[package]] @@ -871,7 +921,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.61", + "syn 2.0.66", ] [[package]] @@ -882,7 +932,7 @@ checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" dependencies = [ "darling_core", "quote", - "syn 2.0.61", + "syn 2.0.66", ] [[package]] @@ -892,7 +942,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" dependencies = [ "cfg-if", - "hashbrown", + "hashbrown 0.14.5", "lock_api", "once_cell", "parking_lot_core 0.9.10", @@ -965,6 +1015,15 @@ dependencies = [ "dirs-sys", ] +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys", +] + [[package]] name = "dirs-sys" version = "0.4.1" @@ -977,6 +1036,17 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "docker_credential" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31951f49556e34d90ed28342e1df7e1cb7a229c4cab0aecc627b5d91edd41d07" +dependencies = [ + "base64 0.21.7", + "serde", + "serde_json", +] + [[package]] name = "dotenvy" version = "0.15.7" @@ -985,9 +1055,9 @@ checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" [[package]] name = "either" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" +checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" dependencies = [ "serde", ] @@ -1010,7 +1080,7 @@ dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.66", ] [[package]] @@ -1142,6 +1212,21 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -1228,7 +1313,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.66", ] [[package]] @@ -1347,7 +1432,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap", + "indexmap 2.2.6", "slab", "tokio", "tokio-util", @@ -1366,7 +1451,7 @@ dependencies = [ "futures-sink", "futures-util", "http 1.1.0", - "indexmap", + "indexmap 2.2.6", "slab", "tokio", "tokio-util", @@ -1383,6 +1468,12 @@ dependencies = [ "crunchy", ] +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + [[package]] name = "hashbrown" version = "0.14.5" @@ -1399,7 +1490,7 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" dependencies = [ - "hashbrown", + "hashbrown 0.14.5", ] [[package]] @@ -1630,6 +1721,21 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-named-pipe" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73b7d8abf35697b81a825e386fc151e0d503e8cb5fcb93cc8669c376dfd6f278" +dependencies = [ + "hex", + "hyper 1.3.1", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", + "winapi", +] + [[package]] name = "hyper-rustls" version = "0.24.2" @@ -1682,6 +1788,22 @@ dependencies = [ "webpki-roots 0.26.1", ] +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper 1.3.1", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + [[package]] name = "hyper-util" version = "0.1.3" @@ -1702,6 +1824,21 @@ dependencies = [ "tracing", ] +[[package]] +name = "hyperlocal-next" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acf569d43fa9848e510358c07b80f4adf34084ddc28c6a4a651ee8474c070dcc" +dependencies = [ + "hex", + "http-body-util", + "hyper 1.3.1", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", +] + [[package]] name = "iana-time-zone" version = "0.1.60" @@ -1808,6 +1945,17 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44feda355f4159a7c757171a77de25daf6411e217b4cabd03bd6650690468126" +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + [[package]] name = "indexmap" version = "2.2.6" @@ -1815,7 +1963,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.14.5", + "serde", ] [[package]] @@ -1835,7 +1984,7 @@ checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" dependencies = [ "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.66", ] [[package]] @@ -2060,7 +2209,7 @@ dependencies = [ "proc-macro2", "quote", "regex-syntax", - "syn 2.0.61", + "syn 2.0.66", ] [[package]] @@ -2136,7 +2285,7 @@ dependencies = [ "proc-macro2", "quote", "structmeta", - "syn 2.0.61", + "syn 2.0.66", ] [[package]] @@ -2221,6 +2370,23 @@ dependencies = [ "typenum", ] +[[package]] +name = "native-tls" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "navigatum-main-api" version = "1.0.0" @@ -2255,6 +2421,7 @@ dependencies = [ "sqlx", "structured-logger", "tempfile", + "testcontainers-modules", "time", "tokio", "unicode-truncate", @@ -2358,7 +2525,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.66", ] [[package]] @@ -2483,12 +2650,50 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "openssl" +version = "0.10.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" +dependencies = [ + "bitflags 2.5.0", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + [[package]] name = "openssl-probe" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +[[package]] +name = "openssl-sys" +version = "0.9.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "option-ext" version = "0.2.0" @@ -2552,6 +2757,31 @@ dependencies = [ "windows-targets 0.52.5", ] +[[package]] +name = "parse-display" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "914a1c2265c98e2446911282c6ac86d8524f495792c38c5bd884f80499c7538a" +dependencies = [ + "parse-display-derive", + "regex", + "regex-syntax", +] + +[[package]] +name = "parse-display-derive" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ae7800a4c974efd12df917266338e79a7a74415173caf7e70aa0a0707345281" +dependencies = [ + "proc-macro2", + "quote", + "regex", + "regex-syntax", + "structmeta", + "syn 2.0.66", +] + [[package]] name = "paste" version = "1.0.15" @@ -2600,7 +2830,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.66", ] [[package]] @@ -2685,9 +2915,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.82" +version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" +checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" dependencies = [ "unicode-ident", ] @@ -2708,7 +2938,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8021cf59c8ec9c432cfc2526ac6b8aa508ecaf29cd415f271b8406c1b851c3fd" dependencies = [ "quote", - "syn 2.0.61", + "syn 2.0.66", ] [[package]] @@ -3075,11 +3305,13 @@ dependencies = [ "http-body-util", "hyper 1.3.1", "hyper-rustls 0.27.2", + "hyper-tls", "hyper-util", "ipnet", "js-sys", "log", "mime", + "native-tls", "once_cell", "percent-encoding", "pin-project-lite", @@ -3092,6 +3324,7 @@ dependencies = [ "serde_urlencoded", "sync_wrapper 1.0.1", "tokio", + "tokio-native-tls", "tokio-rustls 0.26.0", "tokio-util", "tower-service", @@ -3408,7 +3641,7 @@ checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.66", ] [[package]] @@ -3441,6 +3674,17 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_repr" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + [[package]] name = "serde_spanned" version = "0.6.5" @@ -3462,13 +3706,43 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_with" +version = "3.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ad483d2ab0149d5a5ebcd9972a3852711e0153d863bf5a5d0391d28883c4a20" +dependencies = [ + "base64 0.22.1", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.2.6", + "serde", + "serde_derive", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65569b702f41443e8bc8bbb1c5779bd0450bbe723b56198980e80ec45780bce2" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.66", +] + [[package]] name = "serde_yaml" version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap", + "indexmap 2.2.6", "itoa", "ryu", "serde", @@ -3605,7 +3879,7 @@ dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.66", ] [[package]] @@ -3698,7 +3972,7 @@ dependencies = [ "futures-util", "hashlink", "hex", - "indexmap", + "indexmap 2.2.6", "log", "memchr", "once_cell", @@ -3896,7 +4170,7 @@ dependencies = [ "proc-macro2", "quote", "structmeta-derive", - "syn 2.0.61", + "syn 2.0.66", ] [[package]] @@ -3907,7 +4181,7 @@ checksum = "152a0b65a590ff6c3da95cabe2353ee04e6167c896b28e3b14478c2636c922fc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.66", ] [[package]] @@ -4020,9 +4294,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.61" +version = "2.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c993ed8ccba56ae856363b1845da7266a7cb78e1d146c8a32d54b45a8b831fc9" +checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" dependencies = [ "proc-macro2", "quote", @@ -4093,6 +4367,43 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "testcontainers" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2c9b71635ab25d4f789a86678d114a4f390467c8b93fd7feeaf7c443732a511" +dependencies = [ + "async-trait", + "bollard", + "bollard-stubs", + "bytes", + "dirs", + "docker_credential", + "either", + "futures", + "log", + "memchr", + "parse-display", + "reqwest 0.12.5", + "serde", + "serde_json", + "serde_with", + "thiserror", + "tokio", + "tokio-stream", + "tokio-util", + "url", +] + +[[package]] +name = "testcontainers-modules" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "322fe829905e734f38a73be07e200a25713fe4baa65074277af7b30dbc4ea487" +dependencies = [ + "testcontainers", +] + [[package]] name = "thiserror" version = "1.0.60" @@ -4110,7 +4421,7 @@ checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524" dependencies = [ "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.66", ] [[package]] @@ -4197,7 +4508,17 @@ checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.66", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", ] [[package]] @@ -4283,7 +4604,7 @@ version = "0.22.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef" dependencies = [ - "indexmap", + "indexmap 2.2.6", "serde", "serde_spanned", "toml_datetime", @@ -4356,7 +4677,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.66", ] [[package]] @@ -4588,7 +4909,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.66", "wasm-bindgen-shared", ] @@ -4622,7 +4943,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.66", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -4935,7 +5256,7 @@ checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.66", ] [[package]] diff --git a/server/main-api/Cargo.toml b/server/main-api/Cargo.toml index 375fda053..86233fa34 100644 --- a/server/main-api/Cargo.toml +++ b/server/main-api/Cargo.toml @@ -66,6 +66,7 @@ actix-governor = { version = "0.5.0", features = ["logger"] } tempfile = "3.10.1" base64 = "0.22.1" time = "0.3.36" +testcontainers-modules = { version = "0.6.1", features = ["postgres"] } [dev-dependencies] pretty_assertions = "1.4.0" diff --git a/server/main-api/src/calendar/mod.rs b/server/main-api/src/calendar/mod.rs index 785800ac9..b25aa038f 100644 --- a/server/main-api/src/calendar/mod.rs +++ b/server/main-api/src/calendar/mod.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use actix_web::{get, web, HttpResponse}; use chrono::{DateTime, Utc}; use log::error; -use serde::Deserialize; +use serde::{Deserialize, Serialize}; use sqlx::PgPool; use crate::calendar::models::{CalendarLocation, Event, LocationEvents}; @@ -11,8 +11,8 @@ mod connectum; mod models; pub mod refresh; -#[derive(Deserialize, Debug)] -pub struct QueryArguments { +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct Arguments { ids: Vec, /// eg. 2039-01-19T03:14:07+1 start_after: DateTime, @@ -20,69 +20,135 @@ pub struct QueryArguments { end_before: DateTime, } +impl Arguments { + fn validate_ids(&self) -> Result, HttpResponse> { + let ids = self.ids.clone().into_iter().map(|s| s.replace(|c: char| c.is_whitespace() || c.is_control(), "")).collect::>(); + if ids.len() > 10 { + return Err(HttpResponse::BadRequest().body("Too many ids to query. We suspect that users don't need this. If you need this limit increased, please send us a message")); + }; + if ids.is_empty() { + return Err(HttpResponse::BadRequest().body("No id requested")); + }; + Ok(ids) + } +} + #[get("/api/calendar")] pub async fn calendar_handler( - web::Query(args): web::Query, + web::Json(args): web::Json, data: web::Data, ) -> HttpResponse { - let ids = args.ids.into_iter().map(|s| s.replace(|c: char| c.is_whitespace() || c.is_control(), "")).collect::>(); - if ids.len() > 10 { - return HttpResponse::BadRequest() - .body("Too many ids to query. We suspect that users don't need this. If you need this limit increased, please send us a message"); - }; - if ids.is_empty() { - return HttpResponse::BadRequest() - .body("No id requested"); + let ids = match args.validate_ids() { + Ok(ids) => ids, + Err(e) => return e, }; let locations = match get_locations(&data.db, &ids).await { Ok(l) => l, Err(e) => return e }; - if let Err(e) = validate_locations(&ids,&locations){ + if let Err(e) = validate_locations(&ids, &locations) { return e; } match get_from_db(&data.db, &locations, &args.start_after, &args.end_before).await { Ok(events) => HttpResponse::Ok().json(events), Err(e) => { error!("could not get entries from the db for {ids:?} because {e:?}"); - HttpResponse::InternalServerError() - .body("could not get calendar entries, please try again later") + HttpResponse::InternalServerError().body("could not get calendar entries, please try again later") + } + } +} + +#[cfg(test)] +mod tests { + use actix_web::App; + use actix_web::test; + use actix_web::http::header::ContentType; + use sqlx::postgres::PgPoolOptions; + use crate::{AppData}; + use testcontainers_modules::{postgres, testcontainers::runners::AsyncRunner}; + use super::*; + use pretty_assertions::assert_eq; + + #[actix_web::test] + async fn test_index_get() { + // setup postgres + let postgres_instance = postgres::Postgres::default().start().await.unwrap(); + let connection_string = format!( + "postgres://postgres:postgres@{}:{}/postgres", + postgres_instance.get_host().await.unwrap(), + postgres_instance.get_host_port_ipv4(5432).await.unwrap() + ); + let pool = PgPoolOptions::new().connect(&connection_string).await.unwrap(); + // set up the http service/api/calendar + let app = test::init_service( + App::new().app_data(web::Data::new(AppData { db: pool.clone() })).service(calendar_handler) + ).await; + // -- send requests and assert response -- + { + // missing required query parameters + let req = test::TestRequest::get().uri("/api/calendar").insert_header(ContentType::json()).to_request(); + let (_, resp) = test::call_service(&app, req).await.into_parts(); + run_testcase(resp, 400, "Json deserialize error: EOF while parsing a value at line 1 column 0").await; + } + { + // missing required query parameters + let args = Arguments { + end_before: Utc::now(), + start_after: Utc::now(), + ids: vec![], + }; + let req = test::TestRequest::get().uri("/api/calendar").set_json(args).insert_header(ContentType::json()).to_request(); + let (_, resp) = test::call_service(&app, req).await.into_parts(); + run_testcase(resp, 400, "No id requested").await; } + { + // way too many parameters + let args = Arguments { + end_before: Utc::now(), + start_after: Utc::now(), + ids: (0..10_000).into_iter().map(|i| i.to_string()).collect(), + }; + let req = test::TestRequest::get().uri("/api/calendar").set_json(args).insert_header(ContentType::json()).to_request(); + let (_, resp) = test::call_service(&app, req).await.into_parts(); + run_testcase(resp, 400, "Too many ids to query. We suspect that users don't need this. If you need this limit increased, please send us a message").await; + } + } + + async fn run_testcase(resp: actix_web::HttpResponse, expected_status: u16, expected_body: &str) { + assert_eq!(resp.status().as_u16(), expected_status); + let body_box = resp.into_body(); + let body_bytes = actix_web::body::to_bytes(body_box).await.unwrap(); + let body_text = String::from_utf8(body_bytes.into_iter().collect()).unwrap(); + assert_eq!(body_text, expected_body); } } -fn validate_locations(ids: &[String],locations:&[CalendarLocation])->Result<(),HttpResponse>{ - for id in ids{ - if !locations.iter().any(|l|&l.key==id) { - return Err(HttpResponse::BadRequest() - .body("Requested id {id} does not exist")); + +fn validate_locations(ids: &[String], locations: &[CalendarLocation]) -> Result<(), HttpResponse> { + for id in ids { + if !locations.iter().any(|l| &l.key == id) { + return Err(HttpResponse::BadRequest().body("Requested id {id} does not exist")); } } assert_eq!(locations.len(), ids.len()); for loc in locations { if loc.last_calendar_scrape_at.is_none() { - return Err(HttpResponse::ServiceUnavailable() - .body(format!("Room {key}/{url:?} calendar entry is currently in the process of being scraped, please try again later", key = loc.key, url = loc.calendar_url))); + return Err(HttpResponse::ServiceUnavailable().body(format!("Room {key}/{url:?} calendar entry is currently in the process of being scraped, please try again later", key = loc.key, url = loc.calendar_url))); }; } for loc in locations { if loc.calendar_url.is_none() { - return Err(HttpResponse::NotFound() - .content_type("text/plain") - .body(format!("Room {key}/{url:?} does not have a calendar", key = loc.key, url = loc.calendar_url))); + return Err(HttpResponse::NotFound().content_type("text/plain").body(format!("Room {key}/{url:?} does not have a calendar", key = loc.key, url = loc.calendar_url))); }; } Ok(()) } async fn get_locations(pool: &PgPool, ids: &[String]) -> Result, HttpResponse> { - match sqlx::query_as!(CalendarLocation, "SELECT key,name,last_calendar_scrape_at,calendar_url,type,type_common_name FROM de WHERE key = ANY($1::text[])", ids) - .fetch_all(pool) - .await { + match sqlx::query_as!(CalendarLocation, "SELECT key,name,last_calendar_scrape_at,calendar_url,type,type_common_name FROM de WHERE key = ANY($1::text[])", ids).fetch_all(pool).await { Err(e) => { error!("could not refetch due to {e:?}"); - Err(HttpResponse::InternalServerError() - .body("could not get calendar entries, please try again later")) + Err(HttpResponse::InternalServerError().body("could not get calendar entries, please try again later")) } Ok(locations) => Ok(locations), } @@ -99,9 +165,7 @@ async fn get_from_db( let events = sqlx::query_as!(Event, r#"SELECT id,room_code,start_at,end_at,stp_title_de,stp_title_en,stp_type,entry_type AS "entry_type!:crate::calendar::models::EventType",detailed_entry_type FROM calendar WHERE room_code = $1 AND start_at >= $2 AND end_at <= $3"#, - location.key, start_after, end_before) - .fetch_all(pool) - .await?; + location.key, start_after, end_before).fetch_all(pool).await?; located_events.insert( location.key.clone(), LocationEvents {