diff --git a/Cargo.lock b/Cargo.lock index 3b135a73..729011ee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -240,7 +240,7 @@ dependencies = [ "proc-macro2", "quote", "strum", - "syn 2.0.92", + "syn 2.0.96", "thiserror 1.0.69", ] @@ -287,7 +287,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.92", + "syn 2.0.96", ] [[package]] @@ -298,13 +298,13 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] name = "async-trait" -version = "0.1.83" +version = "0.1.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" +checksum = "3f934833b4b7233644e5848f235df3f57ed8c80f1528a26c3dfa13d2147fa056" dependencies = [ "proc-macro2", "quote", - "syn 2.0.92", + "syn 2.0.96", ] [[package]] @@ -318,18 +318,6 @@ dependencies = [ "rustc_version", ] -[[package]] -name = "auto_enums" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "459b77b7e855f875fd15f101064825cd79eb83185a961d66e6298560126facfb" -dependencies = [ - "derive_utils", - "proc-macro2", - "quote", - "syn 2.0.92", -] - [[package]] name = "autocfg" version = "1.4.0" @@ -348,8 +336,6 @@ dependencies = [ "email_address", "handlebars", "jsonwebtoken", - "juniper", - "juniper_axum", "lettre", "rand", "rust-i18n", @@ -367,14 +353,13 @@ dependencies = [ [[package]] name = "axum" -version = "0.7.9" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" +checksum = "6d6fd624c75e18b3b4c6b9caf42b1afe24437daaee904069137d8bab077be8b8" dependencies = [ - "async-trait", "axum-core", - "base64 0.22.1", "bytes", + "form_urlencoded", "futures-util", "http", "http-body", @@ -393,10 +378,8 @@ dependencies = [ "serde_json", "serde_path_to_error", "serde_urlencoded", - "sha1", "sync_wrapper", "tokio", - "tokio-tungstenite 0.24.0", "tower", "tower-layer", "tower-service", @@ -405,11 +388,10 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.4.5" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" +checksum = "df1362f362fd16024ae199c1970ce98f9661bf5ef94b9808fee734bc3698b733" dependencies = [ - "async-trait", "bytes", "futures-util", "http", @@ -426,21 +408,19 @@ dependencies = [ [[package]] name = "axum-extra" -version = "0.9.6" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c794b30c904f0a1c2fb7740f7df7f7972dfaa14ef6f57cb6178dc63e5dca2f04" +checksum = "460fc6f625a1f7705c6cf62d0d070794e94668988b1c38111baeec177c715f7b" dependencies = [ "axum", "axum-core", "bytes", "cookie", - "fastrand", "futures-util", "http", "http-body", "http-body-util", "mime", - "multer", "pin-project-lite", "serde", "tower", @@ -465,9 +445,12 @@ dependencies = [ [[package]] name = "base62" -version = "2.0.3" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48fa474cf7492f9a299ba6019fb99ec673e1739556d48e8a90eabaea282ef0e4" +checksum = "3d2c393c12f2661ef3f22426d26f95bcfa13d730abab7554502f427562b72f37" +dependencies = [ + "rustversion", +] [[package]] name = "base64" @@ -515,7 +498,7 @@ version = "0.69.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "cexpr", "clang-sys", "itertools 0.12.1", @@ -526,7 +509,7 @@ dependencies = [ "regex", "rustc-hash 1.1.0", "shlex", - "syn 2.0.92", + "syn 2.0.96", ] [[package]] @@ -552,9 +535,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "1be3f42a67d6d345ecd59f675f3f012d6974981560836e938c22b424b85ce1be" [[package]] name = "bitvec" @@ -629,14 +612,14 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.92", + "syn 2.0.96", ] [[package]] name = "bstr" -version = "1.11.1" +version = "1.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "786a307d683a5bf92e6fd5fd69a7eb613751668d1d8d67d802846dfe367c62c8" +checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0" dependencies = [ "memchr", "serde", @@ -713,9 +696,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.6" +version = "1.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d6dbb628b8f8555f86d0323c2eb39e3ec81901f4b83e091db8a6a76d316a333" +checksum = "c8293772165d9345bdaaa39b45b2109591e63fe5e6fbc23c6ff930a048aa310b" dependencies = [ "jobserver", "libc", @@ -1000,7 +983,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.92", + "syn 2.0.96", ] [[package]] @@ -1011,7 +994,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.92", + "syn 2.0.96", ] [[package]] @@ -1044,14 +1027,34 @@ dependencies = [ ] [[package]] -name = "derive_utils" -version = "0.14.2" +name = "derive_builder" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65f152f4b8559c4da5d574bafc7af85454d706b4c5fe8b530d508cacbb6807ea" +checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" +dependencies = [ + "darling", "proc-macro2", "quote", - "syn 2.0.92", + "syn 2.0.96", +] + +[[package]] +name = "derive_builder_macro" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" +dependencies = [ + "derive_builder_core", + "syn 2.0.96", ] [[package]] @@ -1100,7 +1103,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.92", + "syn 2.0.96", ] [[package]] @@ -1201,9 +1204,9 @@ dependencies = [ [[package]] name = "event-listener" -version = "5.3.1" +version = "5.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" +checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae" dependencies = [ "concurrent-queue", "parking", @@ -1359,9 +1362,9 @@ checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-lite" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cef40d21ae2c515b51041df9ed313ed21e572df340ea58a922a0aefe7e8891a1" +checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532" dependencies = [ "fastrand", "futures-core", @@ -1378,7 +1381,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.92", + "syn 2.0.96", ] [[package]] @@ -1497,9 +1500,9 @@ checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "glob" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" [[package]] name = "globset" @@ -1537,17 +1540,18 @@ dependencies = [ [[package]] name = "handlebars" -version = "6.2.0" +version = "6.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd4ccde012831f9a071a637b0d4e31df31c0f6c525784b35ae76a9ac6bc1e315" +checksum = "3d6b224b95c1e668ac0270325ad563b2eef1469fbbb8959bc7c692c844b813d9" dependencies = [ + "derive_builder", "log", "num-order", "pest", "pest_derive", "serde", "serde_json", - "thiserror 1.0.69", + "thiserror 2.0.11", ] [[package]] @@ -1654,7 +1658,7 @@ dependencies = [ "markup5ever", "proc-macro2", "quote", - "syn 2.0.92", + "syn 2.0.96", ] [[package]] @@ -1910,7 +1914,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.92", + "syn 2.0.96", ] [[package]] @@ -2069,72 +2073,6 @@ dependencies = [ "simple_asn1", ] -[[package]] -name = "juniper" -version = "0.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "943306315b1a7a03d27af9dfb0c288d9f4da8830c17df4bceb7d50a47da0982c" -dependencies = [ - "async-trait", - "auto_enums", - "fnv", - "futures", - "indexmap 2.7.0", - "juniper_codegen", - "serde", - "smartstring", - "static_assertions", -] - -[[package]] -name = "juniper_axum" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed3b21b9af313a2967572c8d4b8875c53fc8062e10768470de4748c16ce7b992" -dependencies = [ - "axum", - "bytes", - "futures", - "juniper", - "juniper_graphql_ws", - "serde", - "serde_json", -] - -[[package]] -name = "juniper_codegen" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "760dbe46660494d469023d661e8d268f413b2cb68c999975dcc237407096a693" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.92", - "url", -] - -[[package]] -name = "juniper_graphql_ws" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "709eb11c716072f5c9fcbfa705dd684bd3c070943102f9fc56ccb812a36ba017" -dependencies = [ - "juniper", - "juniper_subscriptions", - "serde", - "tokio", -] - -[[package]] -name = "juniper_subscriptions" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6208a839bd4ca2131924a238311d088d6604ea267c0917903392bad7b70a92c" -dependencies = [ - "futures", - "juniper", -] - [[package]] name = "lalrpop" version = "0.20.2" @@ -2243,7 +2181,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "libc", ] @@ -2271,9 +2209,9 @@ checksum = "64804cc6a5042d4f05379909ba25b503ec04e2c082151d62122d5dcaa274b961" [[package]] name = "libz-sys" -version = "1.1.20" +version = "1.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472" +checksum = "df9b68e50e6e0b26f672573834882eb57759f6db9b3be2ea3c35c91188bb4eaa" dependencies = [ "cc", "pkg-config", @@ -2294,9 +2232,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "litemap" @@ -2376,9 +2314,9 @@ dependencies = [ [[package]] name = "matchit" -version = "0.7.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" [[package]] name = "matrixmultiply" @@ -2426,7 +2364,7 @@ checksum = "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.92", + "syn 2.0.96", ] [[package]] @@ -2707,7 +2645,7 @@ version = "0.10.68" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "cfg-if", "foreign-types", "libc", @@ -2724,7 +2662,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.92", + "syn 2.0.96", ] [[package]] @@ -2838,7 +2776,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc" dependencies = [ "memchr", - "thiserror 2.0.9", + "thiserror 2.0.11", "ucd-trie", ] @@ -2862,7 +2800,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.92", + "syn 2.0.96", ] [[package]] @@ -2898,22 +2836,22 @@ dependencies = [ [[package]] name = "phf" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" dependencies = [ "phf_macros", - "phf_shared 0.11.2", + "phf_shared 0.11.3", ] [[package]] name = "phf_codegen" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a" +checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" dependencies = [ - "phf_generator 0.11.2", - "phf_shared 0.11.2", + "phf_generator 0.11.3", + "phf_shared 0.11.3", ] [[package]] @@ -2928,25 +2866,25 @@ dependencies = [ [[package]] name = "phf_generator" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ - "phf_shared 0.11.2", + "phf_shared 0.11.3", "rand", ] [[package]] name = "phf_macros" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" +checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" dependencies = [ - "phf_generator 0.11.2", - "phf_shared 0.11.2", + "phf_generator 0.11.3", + "phf_shared 0.11.3", "proc-macro2", "quote", - "syn 2.0.92", + "syn 2.0.96", "unicase", ] @@ -2961,11 +2899,11 @@ dependencies = [ [[package]] name = "phf_shared" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" dependencies = [ - "siphasher 0.3.11", + "siphasher 1.0.1", "unicase", ] @@ -2977,9 +2915,9 @@ checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" [[package]] name = "pin-project-lite" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pin-utils" @@ -3025,9 +2963,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.92" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" dependencies = [ "unicode-ident", ] @@ -3104,7 +3042,7 @@ dependencies = [ "rustc-hash 2.1.0", "rustls", "socket2", - "thiserror 2.0.9", + "thiserror 2.0.11", "tokio", "tracing", ] @@ -3123,7 +3061,7 @@ dependencies = [ "rustls", "rustls-pki-types", "slab", - "thiserror 2.0.9", + "thiserror 2.0.11", "tinyvec", "tracing", "web-time", @@ -3243,7 +3181,7 @@ version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", ] [[package]] @@ -3274,7 +3212,7 @@ checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.92", + "syn 2.0.96", ] [[package]] @@ -3332,9 +3270,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.12.11" +version = "0.12.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe060fe50f524be480214aba758c71f99f90ee8c83c5a36b5e9e1d568eb4eb3" +checksum = "43e734407157c3c2034e0258f5e4473ddb361b1e85f95a66690d67264d7cd1da" dependencies = [ "base64 0.22.1", "bytes", @@ -3399,7 +3337,7 @@ checksum = "5f0ec466e5d8dca9965eb6871879677bef5590cf7525ad96cae14376efb75073" dependencies = [ "proc-macro2", "quote", - "syn 2.0.92", + "syn 2.0.96", ] [[package]] @@ -3480,9 +3418,9 @@ dependencies = [ [[package]] name = "roaring" -version = "0.10.9" +version = "0.10.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41589aba99537475bf697f2118357cad1c31590c5a1b9f6d9fc4ad6d07503661" +checksum = "a652edd001c53df0b3f96a36a8dc93fce6866988efc16808235653c6bcac8bf2" dependencies = [ "bytemuck", "byteorder", @@ -3544,7 +3482,7 @@ dependencies = [ "serde", "serde_json", "serde_yml", - "syn 2.0.92", + "syn 2.0.96", ] [[package]] @@ -3634,11 +3572,11 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.42" +version = "0.38.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" +checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "errno", "libc", "linux-raw-sys", @@ -3647,9 +3585,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.20" +version = "0.23.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5065c3f250cbd332cd894be57c40fa52387247659b14a2d6041d121547903b1b" +checksum = "8f287924602bf649d949c63dc8ac8b235fa5387d394020705b80c4eb597ce5b8" dependencies = [ "log", "once_cell", @@ -3758,7 +3696,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "core-foundation", "core-foundation-sys", "libc", @@ -3767,9 +3705,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.13.0" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1863fd3768cd83c56a7f60faa4dc0d403f1b6df0a38c3c25f44b7894e45370d5" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" dependencies = [ "core-foundation-sys", "libc", @@ -3816,14 +3754,14 @@ checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.92", + "syn 2.0.96", ] [[package]] name = "serde_json" -version = "1.0.134" +version = "1.0.135" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d" +checksum = "2b0d7ba2887406110130a978386c4e1befb98c674b4fba677954e4db976630d9" dependencies = [ "indexmap 2.7.0", "itoa", @@ -3890,7 +3828,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.92", + "syn 2.0.96", ] [[package]] @@ -4001,17 +3939,6 @@ version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" -[[package]] -name = "smartstring" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb72c633efbaa2dd666986505016c32c3044395ceaf881518399d2f4127ee29" -dependencies = [ - "autocfg", - "static_assertions", - "version_check", -] - [[package]] name = "smol_str" version = "0.2.2" @@ -4096,12 +4023,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - [[package]] name = "static_assertions_next" version = "1.1.2" @@ -4171,7 +4092,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.92", + "syn 2.0.96", ] [[package]] @@ -4210,7 +4131,7 @@ dependencies = [ "surrealdb-core", "thiserror 1.0.69", "tokio", - "tokio-tungstenite 0.23.1", + "tokio-tungstenite", "tokio-util", "tracing", "trice", @@ -4355,9 +4276,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.92" +version = "2.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ae51629bf965c5c098cc9e87908a3df5301051a9e087d6f9bef5c9771ed126" +checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" dependencies = [ "proc-macro2", "quote", @@ -4381,7 +4302,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.92", + "syn 2.0.96", ] [[package]] @@ -4392,12 +4313,13 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.14.0" +version = "3.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" +checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704" dependencies = [ "cfg-if", "fastrand", + "getrandom", "once_cell", "rustix", "windows-sys 0.59.0", @@ -4436,11 +4358,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.9" +version = "2.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc" +checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" dependencies = [ - "thiserror-impl 2.0.9", + "thiserror-impl 2.0.11", ] [[package]] @@ -4451,18 +4373,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.92", + "syn 2.0.96", ] [[package]] name = "thiserror-impl" -version = "2.0.9" +version = "2.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4" +checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.92", + "syn 2.0.96", ] [[package]] @@ -4542,9 +4464,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.42.0" +version = "1.43.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" +checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" dependencies = [ "backtrace", "bytes", @@ -4560,13 +4482,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.92", + "syn 2.0.96", ] [[package]] @@ -4601,22 +4523,10 @@ dependencies = [ "rustls-pki-types", "tokio", "tokio-rustls", - "tungstenite 0.23.0", + "tungstenite", "webpki-roots", ] -[[package]] -name = "tokio-tungstenite" -version = "0.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edc5f74e248dc973e0dbb7b74c7e0d6fcc301c694ff50049504004ef4d0cdcd9" -dependencies = [ - "futures-util", - "log", - "tokio", - "tungstenite 0.24.0", -] - [[package]] name = "tokio-util" version = "0.7.13" @@ -4673,7 +4583,7 @@ checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ "indexmap 2.7.0", "toml_datetime", - "winnow 0.6.20", + "winnow 0.6.24", ] [[package]] @@ -4698,7 +4608,7 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "403fa3b783d4b626a8ad51d766ab03cb6d2dbfc46b1c5d4448395e6628dc9697" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "bytes", "futures-util", "http", @@ -4749,7 +4659,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.92", + "syn 2.0.96", ] [[package]] @@ -4840,24 +4750,6 @@ dependencies = [ "utf-8", ] -[[package]] -name = "tungstenite" -version = "0.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18e5b8366ee7a95b16d32197d0b2604b43a0be89dc5fac9f8e96ccafbaedda8a" -dependencies = [ - "byteorder", - "bytes", - "data-encoding", - "http", - "httparse", - "log", - "rand", - "sha1", - "thiserror 1.0.69", - "utf-8", -] - [[package]] name = "typenum" version = "1.17.0" @@ -4872,11 +4764,10 @@ checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" [[package]] name = "ulid" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04f903f293d11f31c0c29e4148f6dc0d033a7f80cebc0282bea147611667d289" +checksum = "f294bff79170ed1c5633812aff1e565c35d993a36e757f9bc0accf5eec4e6045" dependencies = [ - "getrandom", "rand", "serde", "web-time", @@ -4974,9 +4865,9 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "utoipa" -version = "5.3.0" +version = "5.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68e76d357bc95c7d0939c92c04c9269871a8470eea39cb1f0231eeadb0c47d0f" +checksum = "435c6f69ef38c9017b4b4eea965dfb91e71e53d869e896db40d1cf2441dd75c0" dependencies = [ "indexmap 2.7.0", "serde", @@ -4986,20 +4877,20 @@ dependencies = [ [[package]] name = "utoipa-gen" -version = "5.3.0" +version = "5.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "564b03f8044ad6806bdc0d635e88be24967e785eef096df6b2636d2cc1e05d4b" +checksum = "a77d306bc75294fd52f3e99b13ece67c02c1a2789190a6f31d32f736624326f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.92", + "syn 2.0.96", ] [[package]] name = "uuid" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" +checksum = "b913a3b5fe84142e269d63cc62b64319ccaf89b748fc31fe025177f767a756c4" dependencies = [ "getrandom", "serde", @@ -5076,7 +4967,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.92", + "syn 2.0.96", "wasm-bindgen-shared", ] @@ -5111,7 +5002,7 @@ checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.92", + "syn 2.0.96", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -5350,9 +5241,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.20" +version = "0.6.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +checksum = "c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a" dependencies = [ "memchr", ] @@ -5417,7 +5308,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.92", + "syn 2.0.96", "synstructure", ] @@ -5439,7 +5330,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.92", + "syn 2.0.96", ] [[package]] @@ -5459,7 +5350,7 @@ checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ "proc-macro2", "quote", - "syn 2.0.92", + "syn 2.0.96", "synstructure", ] @@ -5488,7 +5379,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.92", + "syn 2.0.96", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 98224b23..f4d3747e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,28 +6,26 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -axum = { version = "0.7.9", features = ["multipart"] } -serde = { version = "1.0.216", features = ["derive"] } -tokio = { version = "1.42.0", features = ["full"] } +axum = { version = "0.8.1", features = ["multipart"] } +serde = { version = "1.0.217", features = ["derive"] } +tokio = { version = "1.43.0", features = ["full"] } tracing = "0.1.41" tracing-subscriber = { version = "0.3.19", features = ["env-filter"] } tower-http = { version = "0.6.2", features = ["fs", "cors"] } dotenvy = "0.15.7" -axum-extra = { version = "0.9.6", features = ["cookie", "cookie-signed"] } +axum-extra = { version = "0.10.0", features = ["cookie", "cookie-signed"] } argon2 = "0.5.3" rand = "0.8.5" urlencoding = "2.1.3" -serde_json = "1.0.134" +serde_json = "1.0.135" surrealdb = { version = "2.1.4", features = ["kv-rocksdb", "kv-mem"] } jsonwebtoken = "9.3.0" chrono = { version = "0.4.39", features = [] } email_address = "0.2.9" rust-i18n = "3.1.2" lettre = { version = "0.11.11", features = ["tokio1-native-tls"] } -handlebars = "6.2.0" -utoipa = "5.3.0" -juniper = "0.16.1" -juniper_axum = { version = "0.1.1", features = ["subscriptions"] } +handlebars = "6.3.0" +utoipa = "5.3.1" [dev-dependencies] tower = { version = "0.5.2", features = ["util"] } diff --git a/react-admin/src/App.tsx b/react-admin/src/App.tsx index 1b7f69e1..57b977fe 100644 --- a/react-admin/src/App.tsx +++ b/react-admin/src/App.tsx @@ -24,10 +24,15 @@ import AvoRedApiTesting from "./pages/setting/AvoRedApiTesting"; import SettingPage from "./pages/setting/SettingPage"; import NotFoundPage from "./pages/NotFoundPage"; import ChangePassword from './pages/admin-user/ChangePassword'; -import { ModelTablePage } from "./pages/models/ModelTablePage"; import {ModelCreatePage} from "./pages/models/ModelCreatePage"; import { ModelEditPage } from "./pages/models/ModelEditPage"; import {ComponentTablePage} from "./pages/component/ComponentTablePage"; +import {CollectionTable} from "./pages/collection/CollectionTable"; +import {CollectionEdit} from "./pages/collection/CollectionEdit"; +import {CollectionCreate} from "./pages/collection/CollectionCreate"; +import {ContentTable} from "./pages/content/ContentTable"; +import {ContentCreate} from "./pages/content/ContentCreate"; +import {ContentEdit} from "./pages/content/ContentEdit"; function App() { return ( @@ -51,9 +56,16 @@ function App() { } /> } /> } /> - } /> + + } /> + } /> + } /> + } /> } /> + } /> + } /> + } /> } /> } /> } /> diff --git a/react-admin/src/layouts/partials/AppSidebar.tsx b/react-admin/src/layouts/partials/AppSidebar.tsx index f9e0336e..8bbfecac 100644 --- a/react-admin/src/layouts/partials/AppSidebar.tsx +++ b/react-admin/src/layouts/partials/AppSidebar.tsx @@ -1,7 +1,14 @@ import {Link, Outlet} from "react-router-dom"; -import {Menu} from "@headlessui/react"; +import {Menu, MenuButton, MenuItem, MenuItems} from "@headlessui/react"; import {useTranslation} from "react-i18next"; -import {ChevronDownIcon, FilmIcon, RocketLaunchIcon, CpuChipIcon, DeviceTabletIcon} from "@heroicons/react/24/solid"; +import { + ChevronDownIcon, + FilmIcon, + RocketLaunchIcon, + CpuChipIcon, + DeviceTabletIcon, + CircleStackIcon +} from "@heroicons/react/24/solid"; import {useContext} from "react"; import {ThemeContext} from "../../context/ThemeContext"; @@ -41,6 +48,15 @@ function AppSidebar() {
{t("sidebar.page")}
+ +
+ +
+
{t("collections")}
+
-
{t("model")}
+
{t("content")}
@@ -79,7 +95,7 @@ function AppSidebar() { -
@@ -88,11 +104,11 @@ function AppSidebar() {
- + - - - {({active}) => ( + + + {() => ( {t("sidebar.admin_user")}
)} - - - {({active}) => ( + + + {() => ( {t("sidebar.role")} )} - - - {({active}) => ( + + + {() => ( )} - - + +
diff --git a/react-admin/src/locales/en.json b/react-admin/src/locales/en.json index 219daad9..c0f7b309 100644 --- a/react-admin/src/locales/en.json +++ b/react-admin/src/locales/en.json @@ -67,6 +67,7 @@ "table": "Table", "permissions": "Permissions", "page": "Page", + "collections": "Collections", "role": "Role", "general": "General", "is_super_admin": "Is super admin", @@ -90,8 +91,10 @@ "loading": "Loading...", "component_information": "Component information", "page_information": "Page Information", - "model": "Model", + "content": "Content", + "content_field": "content field", "model_information": "Model Information", + "collection_information": "Collection Information", "components": "Components", "field_type": "Field type", "upload_asset": "Upload asset", diff --git a/react-admin/src/pages/collection/CollectionCreate.tsx b/react-admin/src/pages/collection/CollectionCreate.tsx new file mode 100644 index 00000000..44ab1fdc --- /dev/null +++ b/react-admin/src/pages/collection/CollectionCreate.tsx @@ -0,0 +1,227 @@ +import {Link} from "react-router-dom"; +import InputField from "../../components/InputField"; +import {useTranslation} from "react-i18next"; +import {Controller, useFieldArray, useForm} from "react-hook-form"; +import {joiResolver} from "@hookform/resolvers/joi"; +import ErrorMessage from "../../components/ErrorMessage"; +import { + CollectionFieldDataType, CollectionFieldFieldType, + SavableCollectionType +} from "../../types/collection/CreatableCollectionType"; +import {useStoreCollection} from "./hooks/useStoreCollection"; +import {useCollectionCreateSchema} from "./schemas/CollectionCreateSchema"; +import React, {useState} from "react"; +import slug from "slug"; +import AvoRedButton, {ButtonType} from "../../components/AvoRedButton"; +import _ from "lodash"; +import {CollectionFieldModal} from "./CollectionFieldModal"; +import {Cog8ToothIcon, TrashIcon} from "@heroicons/react/24/solid"; + +export const CollectionCreate = () => { + const [t] = useTranslation("global") + const {mutate, error} = useStoreCollection() + const [isCollectionFieldModalOpen, setIsCollectionFieldModalOpen] = useState(false); + const [currentIndex, setCurrentIndex] = useState(0); + + const { + register, + handleSubmit, + setValue, + formState: {errors}, + getValues, + control, + trigger + } = useForm({ + resolver: joiResolver(useCollectionCreateSchema(), {allowUnknown: true}), + }) + + const { fields, append, remove } = useFieldArray({ + control, + name: "collection_fields", //rename fields + }); + + const submitHandler = ((data: SavableCollectionType) => { + console.log(data) + // mutate(data) + }) + + const onNameChange = (e: React.KeyboardEvent) => { + setValue('identifier', slug(e.currentTarget.value || '')) + } + + const deleteCollectionFieldOnClick = (e: any, index: number) => { + e.preventDefault(); + remove(index); + setCurrentIndex(0); + }; + + const addFieldButtonOnClick = (async (e: React.MouseEvent, max_index: number) => { + e.preventDefault() + append({ + name: '', + identifier: '', + data_type: CollectionFieldDataType.TEXT, + field_type: CollectionFieldFieldType.TEXT, + }) + await trigger("collection_fields"); + setCurrentIndex(max_index); + setIsCollectionFieldModalOpen(true) + }) + + + return ( + <> +
+
+
+

+ {t("collection_information")} +

+ +
+ + {_.size(fields) > 0 ? ( + + ) : ( + <> + )} + +
+ onNameChange(e)} + autoFocus={true} + /> + +
+
+ + +
+ + {fields.map((field, index) => { + return ( +
+ { + return ( + <> +
+
+
+
+
+ + {collection_field.value.name} + + + ({collection_field.value.identifier}) + +
+
+
+ +
+
+ deleteCollectionFieldOnClick(e, index) + } + className="ml-3" + > + +
+
+
+ + + + +
+
+
+ + ); + }} + control={control} + /> +
+ ); + })} + + + {/* todo uncomment below to enabled the add field button */} + {/*
*/} + {/* ) => addFieldButtonOnClick(e, fields.length)}*/} + {/* type={ButtonType.button}/>*/} + {/*
*/} + +
+ + + {t("cancel")} + +
+ +
+
+
+ + ); +}; diff --git a/react-admin/src/pages/collection/CollectionEdit.tsx b/react-admin/src/pages/collection/CollectionEdit.tsx new file mode 100644 index 00000000..e3e1bb54 --- /dev/null +++ b/react-admin/src/pages/collection/CollectionEdit.tsx @@ -0,0 +1,135 @@ +import InputField from "../../components/InputField"; +import {Link, useParams} from "react-router-dom"; +import {useTranslation} from "react-i18next"; +import {useState} from "react"; +import {useForm} from "react-hook-form"; +import {PutCollectionIdentifierType} from "../../types/collection/PutCollectionIdentifierType"; +import {joiResolver} from "@hookform/resolvers/joi"; +import EditableCollectionType from "../../types/collection/EditableCollectionType"; +import {useGetCollection} from "./hooks/useGetCollection"; +import {useCollectionPutSchema} from "./schemas/CollectionPutSchema"; +import {useCollectionEditSchema} from "./schemas/CollectionEditSchema"; +import {usePutCollectionIdentifier} from "./hooks/usePutCollectionIdentifier"; +import {useUpdateCollection} from "./hooks/useUpdateCollection"; + +export const CollectionEdit = (() => { + const params = useParams(); + const collection_id = params.collection_id ?? '' + const {mutate} = useUpdateCollection(collection_id); + const [t] = useTranslation("global") + const {data} = useGetCollection(collection_id) + const [isEditableIdentifier, setIsEditableIdentifier] = useState(true) + const values = data?.data.data + + + const { + register: putCollectionRegister, + getValues: getCollectionIdentifierValue + } = useForm({ + resolver: joiResolver(useCollectionPutSchema(), {allowUnknown: true}), + values: { + identifier: data?.data.data.identifier + } + }); + + const { + register, + handleSubmit, + formState: {errors}, + } = useForm({ + resolver: joiResolver(useCollectionEditSchema(), {allowUnknown: true}), + values + }) + + const {mutate: putCollectionIdentifierMutate} = usePutCollectionIdentifier(collection_id) + + + const editableIdentifierOnClick = (() => { + setIsEditableIdentifier(false) + }) + const saveIdentifierOnClick = (() => { + putCollectionIdentifierMutate({identifier: getCollectionIdentifierValue('identifier')}) + setIsEditableIdentifier(true) + console.log("teststes") + }) + + const cancelIdentifierOnClick = (() => { + setIsEditableIdentifier(true) + }) + + const submitHandler = ((data: EditableCollectionType) => { + mutate(data) + }) + return ( + <> +
+
+
+

+ {t("collection_information")} +

+ +
+
+ +
+
+ +
+ {isEditableIdentifier ? ( + <> + + {t("edit_identifier")} + + + ) : ( + <> + + + + )} +
+
+ +
+ + + {t("cancel")} + +
+
+
+
+
+ + ) +}) \ No newline at end of file diff --git a/react-admin/src/pages/collection/CollectionFieldModal.tsx b/react-admin/src/pages/collection/CollectionFieldModal.tsx new file mode 100644 index 00000000..5f8d1920 --- /dev/null +++ b/react-admin/src/pages/collection/CollectionFieldModal.tsx @@ -0,0 +1,125 @@ +import { + UseFormGetValues, + UseFormRegister, + UseFormSetValue, + UseFormTrigger, +} from "react-hook-form"; +import AvoredModal from "../../components/AvoredModal"; +import AvoRedButton from "../../components/AvoRedButton"; +import { useTranslation } from "react-i18next"; +import InputField from "../../components/InputField"; +import React from "react"; +import slug from "slug"; +import { + CollectionFieldDataType, + SavableCollectionType, CollectionFieldFieldType +} from "../../types/collection/CreatableCollectionType"; + +type CollectionFieldProps = { + register: UseFormRegister; + currentIndex: number; + getValues: UseFormGetValues; + setValue: UseFormSetValue; + trigger: UseFormTrigger; + setIsOpen: React.Dispatch>; + isOpen: boolean; +}; + +export const CollectionFieldModal = ({ + register, + currentIndex, + getValues, + setValue, + trigger, + setIsOpen, + isOpen, +}: CollectionFieldProps) => { + const [t] = useTranslation("global"); + + const onContentFieldChange = async ( + index: number, + field_type: CollectionFieldFieldType, + data_type: CollectionFieldDataType + ) => { + setValue(`collection_fields.${index}.field_type`, field_type); + setValue(`collection_fields.${index}.data_type`, data_type); + await trigger(`collection_fields.${index}`); + }; + + const contentFieldNameInputChange = ((e: React.KeyboardEvent, index: number) => { + e.stopPropagation(); + + setValue(`collection_fields.${index}.identifier`, slug(e.currentTarget.value)); + }) + + return ( + setIsOpen(false)} + modal_body={ +
+
+
+
+ ) => contentFieldNameInputChange(e, currentIndex)} + register={register(`collection_fields.${currentIndex}.name`)} + /> +
+
+ +
+ + {/*
{renderFieldData(currentIndex)}
*/} +
+ +
+
+
+ onContentFieldChange( + currentIndex, + CollectionFieldFieldType.TEXT, + CollectionFieldDataType.TEXT + ) + } + className={`${getValues(`collection_fields.${currentIndex}.field_type`) === CollectionFieldFieldType.TEXT ? "bg-primary-200" : "bg-gray-300"} + ring-1 ring-gray-300 hover:cursor-pointer hover:ring-primary-300 p-3 rounded`} + > + {t("text_field")} +
+
+
+
+
+
+
+
+ setIsOpen(false)} + className="bg-primary-500" + label={t("create_content_field")} + /> +
+
+ setIsOpen(false)} + label={t("cancel")} + /> +
+
+
+
+ } + modal_header={t("collection_field")} + isOpen={isOpen} + >
+ ); +}; diff --git a/react-admin/src/pages/collection/CollectionTable.tsx b/react-admin/src/pages/collection/CollectionTable.tsx new file mode 100644 index 00000000..21c35f7b --- /dev/null +++ b/react-admin/src/pages/collection/CollectionTable.tsx @@ -0,0 +1,131 @@ +import HasPermission from "../../components/HasPermission" +import {Link} from "react-router-dom" +import {useTranslation} from "react-i18next" +import {useState} from "react" +import {createColumnHelper, getCoreRowModel, SortingState, useReactTable} from "@tanstack/react-table" +import AvoRedTable from "../../components/AvoRedTable" +import {getFormattedDate} from "../../lib/common" +import _ from "lodash" +import {useCollectionTable} from "./hooks/useCollectionTable"; +import {CollectionType} from "../../types/collection/CollectionType"; + +export const CollectionTable = (() => { + const [t] = useTranslation("global") + const [sorting, setSorting] = useState([]); + const [pagination, setPagination] = useState({ + pageIndex: 0, //initial page index + pageSize: 10, //default page size + }); + + const customSorting = ((sorting: any) => { + setSorting(sorting) + }) + + const collection_api_table_response = useCollectionTable({ + order: sorting.map((s) => `${s.id}:${s.desc ? 'DESC' : 'ASC'}`).join(','), + page: pagination.pageIndex + }); + const customPagination = (async (pagination: any) => { + setPagination(pagination) + }) + const collections: Array = _.get(collection_api_table_response, 'data.data.data', []) + + const columnHelper = createColumnHelper() + const columns = [ + columnHelper.accessor('id', { + cell: info => info.getValue(), + header: t("id") + }), + columnHelper.accessor('name', { + cell: info => info.getValue(), + header: t("name") + }), + columnHelper.accessor('identifier', { + cell: info => info.getValue(), + header: t("identifier") + }), + columnHelper.accessor('created_at', { + id: "created_at", + cell: info => getFormattedDate(info.getValue()), + header: t("created_at") + }), + columnHelper.accessor('created_by', { + cell: info => info.getValue(), + header: t("created_by") + }), + columnHelper.accessor('updated_at', { + cell: info => getFormattedDate(info.getValue()), + header: t("updated_at") + }), + columnHelper.accessor('updated_by', { + cell: info => info.getValue(), + header: t("updated_by") + }), + columnHelper.accessor('action', { + cell: info => { + return ( + + + {t("edit")} + + + ) + }, + enableSorting: false, + header: t("action"), + enableHiding: false + }), + ] + const table = useReactTable({ + data: collections, + columns, + getCoreRowModel: getCoreRowModel(), + rowCount: collection_api_table_response.data?.data.pagination.total, + onPaginationChange: customPagination, + manualPagination: true, + initialState: { + pagination, + columnVisibility: { + created_at: false, + created_by: false + } + }, + manualSorting: true, + onSortingChange: customSorting, + state: { + sorting, + pagination + }, + }) + + return ( + <> +
+
+
+ {t("collection")} +
+
+ + + {t("create")} + + +
+
+ +
+ + + +
+
+ + ) +}) \ No newline at end of file diff --git a/react-admin/src/pages/collection/hooks/useCollectionTable.ts b/react-admin/src/pages/collection/hooks/useCollectionTable.ts new file mode 100644 index 00000000..be64ae28 --- /dev/null +++ b/react-admin/src/pages/collection/hooks/useCollectionTable.ts @@ -0,0 +1,35 @@ +import {useQuery} from '@tanstack/react-query' +import { useAxios } from '../../../hooks/useAxios' +import _ from 'lodash' +import {useNavigate} from 'react-router-dom' +import PaginateType from "../../../types/misc/PaginateType"; + +export const useCollectionTable = (query: PaginateType) => { + let params: URLSearchParams = new URLSearchParams(); + if (query.page && query.page > 0) { + params.append("page", query.page.toString()) + } + if (query.order && query.order !== "") { + params.append("order", query.order) + } + let query_string = ""; + if (params.toString() !== "") { + query_string = "?" + params.toString() + } + + const client = useAxios(); + const redirect = useNavigate(); + return useQuery({ + queryKey: ['collection-table', query], + queryFn: (async () => { + try { + return await client.get("/collection" + query_string) + } catch (error) { + if (_.get(error, 'response.status') === 401) { + localStorage.removeItem('AUTH_TOKEN') + redirect("/admin/login") + } + } + }) + }) +} \ No newline at end of file diff --git a/react-admin/src/pages/collection/hooks/useGetCollection.ts b/react-admin/src/pages/collection/hooks/useGetCollection.ts new file mode 100644 index 00000000..0945e131 --- /dev/null +++ b/react-admin/src/pages/collection/hooks/useGetCollection.ts @@ -0,0 +1,23 @@ +import {useQuery} from '@tanstack/react-query' +import { useAxios } from '../../../hooks/useAxios' +import _ from 'lodash' +import {useNavigate} from 'react-router-dom' + +export const useGetCollection = (collection_id: string) => { + const client = useAxios() + const redirect = useNavigate() + + return useQuery({ + queryKey: ['collection', collection_id], + queryFn: (async () => { + try { + return await client.get("/collection/" + collection_id) + } catch (error) { + if (_.get(error, 'response.status') === 401) { + localStorage.removeItem('AUTH_TOKEN') + redirect("/admin/login") + } + } + }) + }) +} \ No newline at end of file diff --git a/react-admin/src/pages/collection/hooks/usePutCollectionIdentifier.ts b/react-admin/src/pages/collection/hooks/usePutCollectionIdentifier.ts new file mode 100644 index 00000000..a7a6bf87 --- /dev/null +++ b/react-admin/src/pages/collection/hooks/usePutCollectionIdentifier.ts @@ -0,0 +1,21 @@ +import {useMutation} from '@tanstack/react-query' +import { useAxios } from '../../../hooks/useAxios' +import _ from 'lodash' +import {useNavigate} from 'react-router-dom' +import {PutCollectionIdentifierType} from "../../../types/collection/PutCollectionIdentifierType"; + +export const usePutCollectionIdentifier = (collection_id: string) => { + const client = useAxios(); + const redirect = useNavigate(); + return useMutation({ + mutationFn: async (data: PutCollectionIdentifierType) => { + const url = '/put-collection-identifier/' + collection_id; + return await client.put(url , JSON.stringify(data)); + }, + onSuccess: (res) => { + if (_.get(res, 'data.status') === true) { + redirect("/admin/collection-edit/" + collection_id) + } + } + }) +} \ No newline at end of file diff --git a/react-admin/src/pages/collection/hooks/useStoreCollection.ts b/react-admin/src/pages/collection/hooks/useStoreCollection.ts new file mode 100644 index 00000000..607e851e --- /dev/null +++ b/react-admin/src/pages/collection/hooks/useStoreCollection.ts @@ -0,0 +1,20 @@ +import {useMutation} from '@tanstack/react-query' +import { useAxios } from '../../../hooks/useAxios' +import _ from 'lodash' +import {useNavigate} from 'react-router-dom' +import {SavableCollectionType} from "../../../types/collection/CreatableCollectionType"; + +export const useStoreCollection = () => { + const client = useAxios(); + const redirect = useNavigate(); + return useMutation({ + mutationFn: async (data: SavableCollectionType) => { + return await client.post('/collection', JSON.stringify(data)); + }, + onSuccess: (res) => { + if (_.get(res, 'data.status') === true) { + redirect("/admin/collections") + } + } + }) +} \ No newline at end of file diff --git a/react-admin/src/pages/collection/hooks/useUpdateCollection.ts b/react-admin/src/pages/collection/hooks/useUpdateCollection.ts new file mode 100644 index 00000000..3abb6cdf --- /dev/null +++ b/react-admin/src/pages/collection/hooks/useUpdateCollection.ts @@ -0,0 +1,21 @@ +import {useMutation} from '@tanstack/react-query' +import { useAxios } from '../../../hooks/useAxios' +import _ from 'lodash' +import {useNavigate} from 'react-router-dom' +import IEditableModel from "../../../types/model/IEditableModel"; + +export const useUpdateCollection = (role_id: string) => { + const client = useAxios(); + const redirect = useNavigate(); + return useMutation({ + mutationFn: async (data: IEditableModel) => { + const url = '/collection/' + role_id; + return await client.put(url , JSON.stringify(data)); + }, + onSuccess: (res) => { + if (_.get(res, 'data.status') === true) { + redirect("/admin/collection") + } + } + }) +} \ No newline at end of file diff --git a/react-admin/src/pages/collection/schemas/CollectionCreateSchema.ts b/react-admin/src/pages/collection/schemas/CollectionCreateSchema.ts new file mode 100644 index 00000000..2eb1ea8e --- /dev/null +++ b/react-admin/src/pages/collection/schemas/CollectionCreateSchema.ts @@ -0,0 +1,15 @@ +import Joi from 'joi'; +import {useTranslation} from "react-i18next"; + +export const useCollectionCreateSchema = (() => { + + const [t] = useTranslation("global") + return Joi.object({ + name : Joi.string().required().messages({ + 'string.empty': t("empty_message", {attribute: t("name")}), + }), + identifier : Joi.string().required().messages({ + 'string.empty': t("empty_message", {attribute: t("identifier")}), + }) + }); +}) diff --git a/react-admin/src/pages/collection/schemas/CollectionEditSchema.ts b/react-admin/src/pages/collection/schemas/CollectionEditSchema.ts new file mode 100644 index 00000000..765e1f34 --- /dev/null +++ b/react-admin/src/pages/collection/schemas/CollectionEditSchema.ts @@ -0,0 +1,12 @@ +import Joi from 'joi'; +import {useTranslation} from "react-i18next"; + +export const useCollectionEditSchema = (() => { + + const [t] = useTranslation("global") + return Joi.object({ + name : Joi.string().required().messages({ + 'string.empty': t("empty_message", {attribute: t("name")}), + }) + }); +}) diff --git a/react-admin/src/pages/collection/schemas/CollectionPutSchema.ts b/react-admin/src/pages/collection/schemas/CollectionPutSchema.ts new file mode 100644 index 00000000..b19d9406 --- /dev/null +++ b/react-admin/src/pages/collection/schemas/CollectionPutSchema.ts @@ -0,0 +1,12 @@ +import Joi from 'joi'; +import {useTranslation} from "react-i18next"; + +export const useCollectionPutSchema = (() => { + + const [t] = useTranslation("global") + return Joi.object({ + identifier : Joi.string().required().messages({ + 'string.empty': t("empty_message", {attribute: t("identifier")}), + }) + }); +}) diff --git a/react-admin/src/pages/content/ContentCreate.tsx b/react-admin/src/pages/content/ContentCreate.tsx new file mode 100644 index 00000000..72357637 --- /dev/null +++ b/react-admin/src/pages/content/ContentCreate.tsx @@ -0,0 +1,249 @@ +import {useTranslation} from "react-i18next"; +import {ContentSidebar} from "./ContentSidebar"; +import {Link, useSearchParams} from "react-router-dom"; +import InputField from "../../components/InputField"; +import ErrorMessage from "../../components/ErrorMessage"; +import React, { useState } from "react"; +import { + AvoRedContentDataType, + AvoRedContentFieldType, + SaveContentFieldType, + SaveContentType +} from "../../types/content/ContentType"; +import slug from "slug"; +import {Controller, useFieldArray, useForm} from "react-hook-form"; +import {joiResolver} from "@hookform/resolvers/joi"; +import {useContentCreateSchema} from "./schemas/useContentCreateSchema"; +import {useStoreContent} from "./hooks/useStoreContent"; +import AvoRedButton, { ButtonType } from "../../components/AvoRedButton"; +import _ from 'lodash'; +import { ContentFieldModal } from "./ContentFieldModal"; +import {Cog8ToothIcon, TrashIcon} from "@heroicons/react/24/solid"; + +export const ContentCreate = (() => { + const [t] = useTranslation("global") + const [isContentFieldModalOpen, setIsContentFieldModalOpen] = useState(false); + const [currentIndex, setCurrentIndex] = useState(0); + const [searchParams] = useSearchParams() + const collectionType: string = searchParams.get("type") as string + const {mutate, error} = useStoreContent() + + const submitHandler = (async (data: SaveContentType) => { + mutate(data) + }) + + const { + register, + handleSubmit, + setValue, + getValues, + formState: {errors}, + control, + trigger + } = useForm({ + resolver: joiResolver(useContentCreateSchema(), {allowUnknown: true}) + }) + + const { fields, append, remove } = useFieldArray({ + control, + name: "content_fields", //rename fields + }); + + const deleteContentFieldOnClick = (e: any, index: number) => { + e.preventDefault(); + remove(index); + setCurrentIndex(0); + }; + + const onNameChange = (e: React.KeyboardEvent) => { + setValue('identifier', slug(e.currentTarget.value || '')) + } + + const addFieldButtonOnClick = (async (e: React.MouseEvent, max_index: number) => { + e.preventDefault() + append({ + name: '', + identifier: '', + data_type: AvoRedContentDataType.TEXT, + field_type: AvoRedContentFieldType.TEXT, + field_content: { + text_value: { + text_value: "" + } + } + }) + await trigger("content_fields"); + setCurrentIndex(max_index); + setIsContentFieldModalOpen(true) + }) + + const renderField = (field: SaveContentFieldType, index: number) => { + switch (field.field_type) { + case AvoRedContentFieldType.TEXT: + return ( +
+ +
+ ); + } + + } + + return ( +
+
+ +
+
+
+ {_.size(fields) > 0 ? ( + + ) : ( + <> + )} + +
+ + onNameChange(e)} + autoFocus={true} + /> + +
+
+ + +
+ + {fields.map((field, index) => { + return ( +
+ { + return ( + <> +
+
+
+
+
+ + {field.value.name} + + + ({field.value.identifier}) + +
+
+
+ +
+
+ deleteContentFieldOnClick(e, index) + } + className="ml-3" + > + +
+
+
+ + + + {renderField(field.value, index)} +
+
+
+ + ) + }} + control={control} + /> + +
+ ) + })} + +
+ ) => addFieldButtonOnClick(e, fields.length)} + type={ButtonType.button}/> +
+ + +
+ + + {t("cancel")} + +
+ +
+
+ + ) +}) diff --git a/react-admin/src/pages/content/ContentEdit.tsx b/react-admin/src/pages/content/ContentEdit.tsx new file mode 100644 index 00000000..48de8178 --- /dev/null +++ b/react-admin/src/pages/content/ContentEdit.tsx @@ -0,0 +1,288 @@ +import {useTranslation} from "react-i18next" +import {ContentSidebar} from "./ContentSidebar" +import {Link, useParams, useSearchParams} from "react-router-dom" +import InputField from "../../components/InputField" +import ErrorMessage from "../../components/ErrorMessage" +import React, { useState } from "react" +import { + AvoRedContentDataType, + AvoRedContentFieldType, + SaveContentFieldType, + SaveContentType +} from "../../types/content/ContentType" +import {Controller, useFieldArray, useForm} from "react-hook-form" +import {joiResolver} from "@hookform/resolvers/joi" +import AvoRedButton, { ButtonType } from "../../components/AvoRedButton" +import _ from 'lodash' +import { ContentFieldModal } from "./ContentFieldModal" +import {Cog8ToothIcon, TrashIcon} from "@heroicons/react/24/solid" +import {useContentEditSchema} from "./schemas/useContentEditSchema" +import {useGetContent} from "./hooks/useGetContent"; +import {useUpdateContent} from "./hooks/useUpdateContent"; +import {usePutContentIdentifier} from "./hooks/usePutContentIdentifier"; + +export const ContentEdit = (() => { + const [t] = useTranslation("global") + const [isContentFieldModalOpen, setIsContentFieldModalOpen] = useState(false); + const [isEditableIdentifier, setIsEditableIdentifier] = useState(true); + const [currentIndex, setCurrentIndex] = useState(0); + const [searchParams] = useSearchParams() + const collectionType: string = searchParams.get("type") as string + const params = useParams() + + const {mutate, error} = useUpdateContent(params.content_id ?? "", collectionType) + + const { data } = useGetContent(params.content_id ?? "", collectionType) + const values = data?.data.data + + const submitHandler = (async (data: SaveContentType) => { + mutate(data) + }) + + const { + register, + handleSubmit, + setValue, + getValues, + formState: {errors}, + control, + trigger + } = useForm({ + resolver: joiResolver(useContentEditSchema(), {allowUnknown: true}), + values + }) + + const { fields, append, remove } = useFieldArray({ + control, + name: "content_fields", //rename fields + }); + + const { mutate: putContentIdentifierMutate } = usePutContentIdentifier( + params.content_id ?? "", collectionType + ); + + + const editableIdentifierOnClick = () => { + setIsEditableIdentifier(false); + }; + const saveIdentifierOnClick = () => { + putContentIdentifierMutate({ + identifier: getValues("identifier"), + }); + setIsEditableIdentifier(true); + }; + + const cancelIdentifierOnClick = () => { + setIsEditableIdentifier(true); + }; + + const deleteContentFieldOnClick = (e: any, index: number) => { + e.preventDefault(); + remove(index); + setCurrentIndex(0); + }; + + const addFieldButtonOnClick = (async (e: React.MouseEvent, max_index: number) => { + e.preventDefault() + append({ + name: '', + identifier: '', + data_type: AvoRedContentDataType.TEXT, + field_type: AvoRedContentFieldType.TEXT, + field_content: { + text_value: { + text_value: "" + } + } + }) + await trigger("content_fields"); + setCurrentIndex(max_index); + setIsContentFieldModalOpen(true) + }) + + const renderField = (field: SaveContentFieldType, index: number) => { + switch (field.field_type) { + case AvoRedContentFieldType.TEXT: + return ( +
+ +
+ ); + } + + } + + return ( +
+
+ +
+
+
+ {_.size(fields) > 0 ? ( + + ) : ( + <> + )} + + +
+ +
+ +
+ +
+ {isEditableIdentifier ? ( + <> + + {t("edit_identifier")} + + + ) : ( + <> + + + + )} +
+
+ + {fields.map((field, index) => { + return ( +
+ { + return ( + <> +
+
+
+
+
+ + {field.value.name} + + + ({field.value.identifier}) + +
+
+
+ +
+
+ deleteContentFieldOnClick(e, index) + } + className="ml-3" + > + +
+
+
+ + + + {renderField(field.value, index)} +
+
+
+ + ) + }} + control={control} + /> + +
+ ) + })} + +
+ ) => addFieldButtonOnClick(e, fields.length)} + type={ButtonType.button}/> +
+
+ + + {t("cancel")} + +
+ +
+
+ + ) +}) diff --git a/react-admin/src/pages/content/ContentFieldModal.tsx b/react-admin/src/pages/content/ContentFieldModal.tsx new file mode 100644 index 00000000..2e0f0d19 --- /dev/null +++ b/react-admin/src/pages/content/ContentFieldModal.tsx @@ -0,0 +1,128 @@ +import { + UseFormGetValues, + UseFormRegister, + UseFormSetValue, + UseFormTrigger, +} from "react-hook-form"; +import { + AvoRedContentDataType, + AvoRedContentFieldType, + SaveContentType, +} from "../../types/content/ContentType"; +import AvoredModal from "../../components/AvoredModal"; +import AvoRedButton from "../../components/AvoRedButton"; +import { useTranslation } from "react-i18next"; +import InputField from "../../components/InputField"; +import React from "react"; +import slug from "slug"; + +type ContentFieldProps = { + register: UseFormRegister; + currentIndex: number; + getValues: UseFormGetValues; + setValue: UseFormSetValue; + trigger: UseFormTrigger; + setIsOpen: React.Dispatch>; + isOpen: boolean; + collectionType: string; +}; + +export const ContentFieldModal = ({ + register, + currentIndex, + getValues, + setValue, + trigger, + setIsOpen, + isOpen, + collectionType, +}: ContentFieldProps) => { + const [t] = useTranslation("global"); + + const onContentFieldChange = async ( + index: number, + field_type: AvoRedContentFieldType, + data_type: AvoRedContentDataType + ) => { + setValue(`content_fields.${index}.field_type`, field_type); + setValue(`content_fields.${index}.data_type`, data_type); + await trigger(`content_fields.${index}`); + }; + + const contentFieldNameInputChange = ((e: React.KeyboardEvent, index: number) => { + e.stopPropagation(); + + setValue(`content_fields.${index}.identifier`, slug(e.currentTarget.value)); + }) + + return ( + setIsOpen(false)} + modal_body={ +
+
+
+
+ ) => contentFieldNameInputChange(e, currentIndex)} + register={register(`content_fields.${currentIndex}.name`)} + /> +
+
+ +
+ + {/*
{renderFieldData(currentIndex)}
*/} +
+ +
+
+
+ onContentFieldChange( + currentIndex, + AvoRedContentFieldType.TEXT, + AvoRedContentDataType.TEXT + ) + } + className={`${getValues(`content_fields.${currentIndex}.field_type`) === AvoRedContentFieldType.TEXT ? "bg-primary-200" : "bg-gray-300"} + ring-1 ring-gray-300 hover:cursor-pointer hover:ring-primary-300 p-3 rounded`} + > + {t("text_field")} +
+
+
+
+
+
+
+
+ setIsOpen(false)} + className="bg-primary-500" + label={t("create_content_field")} + /> +
+
+ setIsOpen(false)} + label={t("cancel")} + /> +
+
+
+
+ } + modal_header={`${t(collectionType)} ${t("content_field")}`} + isOpen={isOpen} + >
+ ); +}; diff --git a/react-admin/src/pages/content/ContentSidebar.tsx b/react-admin/src/pages/content/ContentSidebar.tsx new file mode 100644 index 00000000..be8aaf2e --- /dev/null +++ b/react-admin/src/pages/content/ContentSidebar.tsx @@ -0,0 +1,37 @@ +import {Link, useSearchParams} from "react-router-dom"; +import {useTranslation} from "react-i18next"; +import {useCollectionAll} from "./hooks/useCollectionAll"; +import {CollectionType} from "../../types/collection/CollectionType"; +import _ from 'lodash'; + +export const ContentSidebar = (() => { + const [t] = useTranslation("global"); + const [searchParams] = useSearchParams() + const collections_api_response = useCollectionAll() + const collections: Array = _.get(collections_api_response, 'data.data.data', []) + return ( + <> +
+ {t("collections")} +
+ +
    + {collections.map((collection: CollectionType) => { + return ( +
  • + + {collection.name} + +
  • + ) + })} + + +
+ + ) +}) \ No newline at end of file diff --git a/react-admin/src/pages/content/ContentTable.tsx b/react-admin/src/pages/content/ContentTable.tsx new file mode 100644 index 00000000..aeba42b0 --- /dev/null +++ b/react-admin/src/pages/content/ContentTable.tsx @@ -0,0 +1,167 @@ +import {useTranslation} from "react-i18next"; +import {ContentSidebar} from "./ContentSidebar"; +import {Link, useSearchParams} from "react-router-dom"; +import HasPermission from "../../components/HasPermission"; +import _ from 'lodash'; +import {ContentModel} from "../../types/content/ContentType"; +import {useContentTable} from "./hooks/useContentTable"; +import {useState} from "react"; +import {createColumnHelper, getCoreRowModel, SortingState, useReactTable} from "@tanstack/react-table"; +import {getFormattedDate} from "../../lib/common"; +import AvoRedTable from "../../components/AvoRedTable"; + +export const ContentTable = (() => { + const [t] = useTranslation("global"); + const [searchParams] = useSearchParams() + const collectionType = searchParams.get("type") + + const [pagination, setPagination] = useState({ + pageIndex: 0, //initial page index + pageSize: 10, //default page size + }); + const [sorting, setSorting] = useState([]); + const content_api_table_response = useContentTable({ + order: sorting.map((s) => `${s.id}:${s.desc ? "DESC" : "ASC"}`).join(","), + page: pagination.pageIndex + }, collectionType); + + const contents: Array = _.get( + content_api_table_response, + "data.data.data.data", + [], + ); + + + const customSorting = (sorting: any) => { + setSorting(sorting); + }; + + const customPagination = (async (pagination: any) => { + setPagination(pagination) + }) + + const columnHelper = createColumnHelper(); + const columns = [ + columnHelper.accessor("id", { + cell: (info) => info.getValue(), + header: t("id"), + }), + columnHelper.accessor("name", { + cell: (info) => info.getValue(), + header: t("name"), + }), + columnHelper.accessor("identifier", { + cell: (info) => info.getValue(), + header: t("identifier"), + }), + columnHelper.accessor("created_at", { + id: "created_at", + cell: (info) => getFormattedDate(info.getValue()), + header: t("created_at"), + }), + columnHelper.accessor("created_by", { + cell: (info) => info.getValue(), + header: t("created_by"), + }), + columnHelper.accessor("updated_at", { + cell: (info) => getFormattedDate(info.getValue()), + header: t("updated_at"), + }), + columnHelper.accessor("updated_by", { + cell: (info) => info.getValue(), + header: t("updated_by"), + }), + columnHelper.accessor("action", { + cell: (info) => { + return ( + + + {t("edit")} + + {/*
*/} + {/* onDeleteSelect(e, info.row.original)}*/} + {/* >*/} + {/* {t("delete")}*/} + {/* */} + {/*
*/} +
+ ); + }, + enableSorting: false, + header: t("action"), + enableHiding: false, + }), + ]; + + + const table = useReactTable({ + data: contents, + columns, + getCoreRowModel: getCoreRowModel(), + rowCount: _.get(content_api_table_response, '.data.data.data.pagination.total', 10), + onPaginationChange: customPagination, + manualPagination: true, + initialState: { + columnVisibility: { + created_at: false, + created_by: false, + }, + pagination + }, + manualSorting: true, + onSortingChange: customSorting, + state: { + sorting, + pagination + }, + }); + + + return ( +
+
+ +
+
+ {collectionType ? + <> +
+
+ {t("collection type table title")} +
+
+ + + {t("create")} + + +
+
+
+ + + +
+ + + : + <> +
+ {t("please select the type on left hand side")} +
+ + } +
+
+ + ) +}) \ No newline at end of file diff --git a/react-admin/src/pages/content/hooks/useCollectionAll.ts b/react-admin/src/pages/content/hooks/useCollectionAll.ts new file mode 100644 index 00000000..93149d23 --- /dev/null +++ b/react-admin/src/pages/content/hooks/useCollectionAll.ts @@ -0,0 +1,25 @@ +import {useQuery} from '@tanstack/react-query' +import { useAxios } from '../../../hooks/useAxios' +import _ from 'lodash' +import {useNavigate} from 'react-router-dom' + +export const useCollectionAll = () => { + + const client = useAxios(); + const redirect = useNavigate(); + return useQuery({ + queryKey: ['collection-all'], + queryFn: (async () => { + try { + return await client.get("/collection-all") + } catch (error) { + if (_.get(error, 'response.status') === 401) { + localStorage.removeItem('AUTH_TOKEN') + redirect("/admin/login") + } + + console.error(error) + } + }) + }) +} \ No newline at end of file diff --git a/react-admin/src/pages/content/hooks/useContentTable.ts b/react-admin/src/pages/content/hooks/useContentTable.ts new file mode 100644 index 00000000..0af8c61f --- /dev/null +++ b/react-admin/src/pages/content/hooks/useContentTable.ts @@ -0,0 +1,41 @@ +import {useQuery} from '@tanstack/react-query' +import { useAxios } from '../../../hooks/useAxios' +import _ from 'lodash' +import {useNavigate} from 'react-router-dom' +import PaginateType from "../../../types/misc/PaginateType"; + +export const useContentTable = (query: PaginateType, collectionType: string | null) => { + let params: URLSearchParams = new URLSearchParams(); + if (query.page && query.page > 0) { + params.append("page", query.page.toString()) + } + if (query.order && query.order !== "") { + params.append("order", query.order) + } + let query_string = ""; + if (params.toString() !== "") { + query_string = "?" + params.toString() + } + + const client = useAxios(); + const redirect = useNavigate(); + return useQuery({ + queryKey: ['content-table', query, collectionType], + queryFn: (async () => { + // early exit + if (_.isEmpty(collectionType)) { + return { data: undefined } + } + + try { + + return await client.get(`/content/${collectionType?.trim()}${query_string}`) + } catch (error) { + if (_.get(error, 'response.status') === 401) { + localStorage.removeItem('AUTH_TOKEN') + redirect("/admin/login") + } + } + }) + }) +} diff --git a/react-admin/src/pages/content/hooks/useGetContent.ts b/react-admin/src/pages/content/hooks/useGetContent.ts new file mode 100644 index 00000000..bf521398 --- /dev/null +++ b/react-admin/src/pages/content/hooks/useGetContent.ts @@ -0,0 +1,23 @@ +import {useQuery} from '@tanstack/react-query' +import { useAxios } from '../../../hooks/useAxios' +import _ from 'lodash' +import {useNavigate} from 'react-router-dom' + +export const useGetContent = (content_id: string, collection_type: string) => { + const client = useAxios() + const redirect = useNavigate() + + return useQuery({ + queryKey: ['content', content_id], + queryFn: (async () => { + try { + return await client.get(`/content/${collection_type}/${content_id}`) + } catch (error) { + if (_.get(error, 'response.status') === 401) { + localStorage.removeItem('AUTH_TOKEN') + redirect("/admin/login") + } + } + }) + }) +} \ No newline at end of file diff --git a/react-admin/src/pages/content/hooks/usePutContentIdentifier.ts b/react-admin/src/pages/content/hooks/usePutContentIdentifier.ts new file mode 100644 index 00000000..b802b93c --- /dev/null +++ b/react-admin/src/pages/content/hooks/usePutContentIdentifier.ts @@ -0,0 +1,21 @@ +import {useMutation} from '@tanstack/react-query' +import { useAxios } from '../../../hooks/useAxios' +import _ from 'lodash' +import {useNavigate} from 'react-router-dom' +import {PutRoleIdentifierType} from "../../../types/role/PutRoleIdentifierType"; + +export const usePutContentIdentifier = (content_id: string, collectionType: string) => { + const client = useAxios(); + const redirect = useNavigate(); + return useMutation({ + mutationFn: async (data: PutRoleIdentifierType) => { + const url = `/put-content-identifier/${collectionType}/${content_id}`; + return await client.put(url , JSON.stringify(data)); + }, + onSuccess: (res) => { + if (_.get(res, 'data.status') === true) { + redirect("/admin/content-edit/" + content_id + "?type=" + collectionType) + } + } + }) +} \ No newline at end of file diff --git a/react-admin/src/pages/content/hooks/useStoreContent.ts b/react-admin/src/pages/content/hooks/useStoreContent.ts new file mode 100644 index 00000000..2541a80e --- /dev/null +++ b/react-admin/src/pages/content/hooks/useStoreContent.ts @@ -0,0 +1,21 @@ +import {useMutation} from '@tanstack/react-query' +import { useAxios } from '../../../hooks/useAxios' +import _ from 'lodash' +import {useNavigate} from 'react-router-dom' +import {SaveContentType} from "../../../types/content/ContentType"; + +export const useStoreContent = () => { + const client = useAxios(); + const redirect = useNavigate(); + return useMutation({ + mutationFn: async (data: SaveContentType) => { + const url = `/content`; + return await client.post(url , JSON.stringify(data)); + }, + onSuccess: (res) => { + if (_.get(res, 'data.status') === true) { + redirect("/admin/content") + } + } + }) +} \ No newline at end of file diff --git a/react-admin/src/pages/content/hooks/useUpdateContent.ts b/react-admin/src/pages/content/hooks/useUpdateContent.ts new file mode 100644 index 00000000..f7529075 --- /dev/null +++ b/react-admin/src/pages/content/hooks/useUpdateContent.ts @@ -0,0 +1,21 @@ +import {useMutation} from '@tanstack/react-query' +import { useAxios } from '../../../hooks/useAxios' +import _ from 'lodash' +import {useNavigate} from 'react-router-dom' +import {SaveContentType} from "../../../types/content/ContentType" + +export const useUpdateContent = (content_id: string, collection_type: string) => { + const client = useAxios(); + const redirect = useNavigate(); + return useMutation({ + mutationFn: async (data: SaveContentType) => { + const url = `/content/${collection_type}/${content_id}`; + return await client.put(url , JSON.stringify(data)); + }, + onSuccess: (res) => { + if (_.get(res, 'data.status') === true) { + redirect("/admin/content?type=" + collection_type) + } + } + }) +} \ No newline at end of file diff --git a/react-admin/src/pages/content/schemas/useContentCreateSchema.ts b/react-admin/src/pages/content/schemas/useContentCreateSchema.ts new file mode 100644 index 00000000..16a0caba --- /dev/null +++ b/react-admin/src/pages/content/schemas/useContentCreateSchema.ts @@ -0,0 +1,15 @@ +import Joi from 'joi'; +import {useTranslation} from "react-i18next"; + +export const useContentCreateSchema = (() => { + + const [t] = useTranslation("global") + return Joi.object({ + name : Joi.string().required().messages({ + 'string.empty': t("empty_message", {attribute: t("name")}), + }), + identifier : Joi.string().required().messages({ + 'string.empty': t("empty_message", {attribute: t("identifier")}), + }) + }); +}) diff --git a/react-admin/src/pages/content/schemas/useContentEditSchema.ts b/react-admin/src/pages/content/schemas/useContentEditSchema.ts new file mode 100644 index 00000000..f346ff57 --- /dev/null +++ b/react-admin/src/pages/content/schemas/useContentEditSchema.ts @@ -0,0 +1,14 @@ +import Joi from 'joi'; +import {useTranslation} from "react-i18next"; + +export const useContentEditSchema = (() => { + const [t] = useTranslation("global") + return Joi.object({ + name : Joi.string().required().messages({ + 'string.empty': t("empty_message", {attribute: t("name")}), + }), + identifier : Joi.string().required().messages({ + 'string.empty': t("empty_message", {attribute: t("identifier")}), + }), + }) +}) \ No newline at end of file diff --git a/react-admin/src/pages/models/ModelCreatePage.tsx b/react-admin/src/pages/models/ModelCreatePage.tsx index 4c41ac19..0bbd590c 100644 --- a/react-admin/src/pages/models/ModelCreatePage.tsx +++ b/react-admin/src/pages/models/ModelCreatePage.tsx @@ -1,24 +1,40 @@ import {Link} from "react-router-dom"; import InputField from "../../components/InputField"; import {useTranslation} from "react-i18next"; -import {useForm} from "react-hook-form"; +import {Controller, useFieldArray, useForm} from "react-hook-form"; import {joiResolver} from "@hookform/resolvers/joi"; import ErrorMessage from "../../components/ErrorMessage"; import {CreatableModelType} from "../../types/model/CreatableModelType"; import {useStoreModel} from "./hooks/useStoreModel"; import {useModelCreateSchema} from "./schemas/ModelCreateSchema"; +import {ModelSidebar} from "./ModelSidebar"; export const ModelCreatePage = () => { const [t] = useTranslation("global") const {mutate, error} = useStoreModel() const { + control, register, handleSubmit, formState: {errors}, + trigger, } = useForm({ resolver: joiResolver(useModelCreateSchema(), {allowUnknown: true}), }) + const { fields, append, remove } = useFieldArray({ + control, + name: "model_fields", //rename fields + }); + + const addModelAddOnClick = (async (e: React.MouseEvent) => { + e.preventDefault() + append({ + name: "" + }) + await trigger("model_fields") + }) + const submitHandler = ((data: CreatableModelType) => { mutate(data) }) @@ -30,50 +46,83 @@ export const ModelCreatePage = () => {

{t("model_information")}

- -
-
- - +
+
+
-
- - -
-
- - - {t("cancel")} - + +
+ +
+ + +
+
+ + +
+ {fields.map((field, index) => { + return ( +
+
+ + +
+
+ ) + })} + +
+ + +
+
+ + + {t("cancel")} + +
+
- +
diff --git a/react-admin/src/pages/models/ModelNew.tsx b/react-admin/src/pages/models/ModelNew.tsx new file mode 100644 index 00000000..e6efdd78 --- /dev/null +++ b/react-admin/src/pages/models/ModelNew.tsx @@ -0,0 +1,45 @@ +import {useTranslation} from "react-i18next"; +import {ModelSidebar} from "./ModelSidebar"; +import {Link, useSearchParams} from "react-router-dom"; +import HasPermission from "../../components/HasPermission"; + +export const ModelNew = (() => { + + const [t] = useTranslation("global") + const [searchParams] = useSearchParams() + const modelType = searchParams.get("type") + + + return ( +
+
+
+ +
+
+ {modelType ? +
+
+ {t("model type table title")} +
+
+ + + {t("create")} + + +
+
+ : + <> + {t('please select a model from sidebar to see the models entries.')} + + } +
+
+
+ ) +}) \ No newline at end of file diff --git a/react-admin/src/pages/models/ModelSidebar.tsx b/react-admin/src/pages/models/ModelSidebar.tsx new file mode 100644 index 00000000..a0fc5e67 --- /dev/null +++ b/react-admin/src/pages/models/ModelSidebar.tsx @@ -0,0 +1,33 @@ +import {useTranslation} from "react-i18next"; +import {useModelAll} from "./hooks/useModelAll"; +import {ModelType} from "../../types/model/ModelType"; +import _ from 'lodash'; +import {Link, useSearchParams} from "react-router-dom"; + +export const ModelSidebar = (() => { + const [t] = useTranslation("global") + const [searchParams] = useSearchParams() + const models_api_response = useModelAll() + + const models: Array = _.get(models_api_response, 'data.data.data', []) + return ( + <> +
+ {t("models")} +
+
+ {models.map((model: ModelType) => { + return ( + + {model.name} + + ) + })} +
+ + ) +}) \ No newline at end of file diff --git a/react-admin/src/pages/models/hooks/useModelAll.ts b/react-admin/src/pages/models/hooks/useModelAll.ts new file mode 100644 index 00000000..c4616bc7 --- /dev/null +++ b/react-admin/src/pages/models/hooks/useModelAll.ts @@ -0,0 +1,25 @@ +import {useQuery} from '@tanstack/react-query' +import { useAxios } from '../../../hooks/useAxios' +import _ from 'lodash' +import {useNavigate} from 'react-router-dom' + +export const useModelAll = () => { + + const client = useAxios(); + const redirect = useNavigate(); + return useQuery({ + queryKey: ['model-all'], + queryFn: (async () => { + try { + return await client.get("/model-all") + } catch (error) { + if (_.get(error, 'response.status') === 401) { + localStorage.removeItem('AUTH_TOKEN') + redirect("/admin/login") + } + + console.error(error) + } + }) + }) +} \ No newline at end of file diff --git a/react-admin/src/pages/models/hooks/useModelTable.ts b/react-admin/src/pages/models/hooks/useModelTable.ts index 467c7334..2b2fe44a 100644 --- a/react-admin/src/pages/models/hooks/useModelTable.ts +++ b/react-admin/src/pages/models/hooks/useModelTable.ts @@ -29,6 +29,8 @@ export const useModelTable = (query: PaginateType) => { localStorage.removeItem('AUTH_TOKEN') redirect("/admin/login") } + + console.error(error) } }) }) diff --git a/react-admin/src/pages/page/PageCreate.tsx b/react-admin/src/pages/page/PageCreate.tsx index f1d51dee..b0c57f1d 100644 --- a/react-admin/src/pages/page/PageCreate.tsx +++ b/react-admin/src/pages/page/PageCreate.tsx @@ -119,6 +119,7 @@ function PageCreate() { > ); + case AvoRedPageFieldType.TextEditor: return (
@@ -137,6 +138,7 @@ function PageCreate() {
); + case AvoRedPageFieldType.Radio: return (
@@ -167,7 +169,7 @@ function PageCreate() {
); - case AvoRedPageFieldType.Checkbox: + case AvoRedPageFieldType.Checkbox: return (
); + case AvoRedPageFieldType.SELECT: return (
@@ -217,6 +220,7 @@ function PageCreate() {
); + case AvoRedPageFieldType.TEXT: return (
@@ -227,6 +231,7 @@ function PageCreate() { />
); + case AvoRedPageFieldType.SingleImage: return (
@@ -264,6 +269,7 @@ function PageCreate() {
); + default: return (
@@ -285,7 +291,6 @@ function PageCreate() { e.preventDefault(); e.stopPropagation(); openSingleAssetModal() - console.log("test") } const openSingleAssetModal = () => { setisSingleAssetModalOpen(true); @@ -308,8 +313,8 @@ function PageCreate() { }) // template start here return ( - <> -
+ <> +
@@ -383,11 +388,13 @@ function PageCreate() {
- {page_field.value.name} + + {page_field.value.name} + - ({page_field.value.identifier}) - + ({page_field.value.identifier}) +
@@ -479,8 +486,8 @@ function PageCreate() {
- - + + ); } diff --git a/react-admin/src/pages/page/PageEdit.tsx b/react-admin/src/pages/page/PageEdit.tsx index bcdf4858..c9bde927 100644 --- a/react-admin/src/pages/page/PageEdit.tsx +++ b/react-admin/src/pages/page/PageEdit.tsx @@ -36,10 +36,9 @@ function PageEdit() { const [isSingleAssetModalOpen, setisSingleAssetModalOpen] = useState(false); const [currentIndex, setCurrentIndex] = useState(0); const backend_url = import.meta.env.VITE_AVORED_BACKEND_BASE_URL; - const params = useParams(); - const [t] = useTranslation("global"); - const [isEditableIdentifier, setIsEditableIdentifier] = - useState(true); + const params = useParams() + const [t] = useTranslation("global") + const [isEditableIdentifier, setIsEditableIdentifier] = useState(true); const { mutate } = useUpdatePage(params.page_id ?? ""); const { data } = useGetPage(params.page_id ?? ""); @@ -330,12 +329,14 @@ function PageEdit() { setValue(`page_fields.${index}.field_content.text_value.text_value`, selectedAsset.new_path); closeSingleAssetModal(); } + const singleImageButtonOnClick = (e: React.MouseEvent) => { e.preventDefault(); e.stopPropagation(); openSingleAssetModal() console.log("test") } + const openSingleAssetModal = () => { setisSingleAssetModalOpen(true); }; diff --git a/react-admin/src/pages/page/PageFieldModal.tsx b/react-admin/src/pages/page/PageFieldModal.tsx index 8f6194b7..5a9e44d6 100644 --- a/react-admin/src/pages/page/PageFieldModal.tsx +++ b/react-admin/src/pages/page/PageFieldModal.tsx @@ -1,50 +1,57 @@ import AvoredModal from "../../components/AvoredModal"; import InputField from "../../components/InputField"; import { useTranslation } from "react-i18next"; -import { AvoRedPageDataType, AvoRedPageFieldType } from "../../types/page/IPageModel"; +import { + AvoRedPageDataType, + AvoRedPageFieldType, +} from "../../types/page/IPageModel"; import { AvoRedPageFieldCheckboxFieldDataOptions, AvoRedPageFieldData, AvoRedPageFieldRadioFieldDataOptions, AvoRedPageFieldSelectFieldDataOptions, SaveFieldType, - SavePageType + SavePageType, } from "../../types/page/CreatablePageType"; import { MinusIcon, PlusIcon } from "@heroicons/react/24/solid"; import AvoRedButton from "../../components/AvoRedButton"; -import { UseFormGetValues, UseFormRegister, UseFormSetValue, UseFormTrigger } from "react-hook-form"; +import { + UseFormGetValues, + UseFormRegister, + UseFormSetValue, + UseFormTrigger, +} from "react-hook-form"; import _ from "lodash"; type PageFieldProps = { - register: UseFormRegister - currentIndex: number - getValues: UseFormGetValues - setValue: UseFormSetValue - trigger: UseFormTrigger - setIsOpen: React.Dispatch> - isOpen: boolean -} - -export const PageFieldModal = (({ + register: UseFormRegister; + currentIndex: number; + getValues: UseFormGetValues; + setValue: UseFormSetValue; + trigger: UseFormTrigger; + setIsOpen: React.Dispatch>; + isOpen: boolean; +}; + +export const PageFieldModal = ({ register, currentIndex, getValues, setValue, trigger, setIsOpen, - isOpen + isOpen, }: PageFieldProps) => { - - const [t] = useTranslation("global"); + const [t] = useTranslation("global"); const radioOptionLabelOnChange = async ( - e: any, - field_index: number, - option_index: number, + e: any, + field_index: number, + option_index: number ) => { setValue( - `page_fields.${field_index}.field_data.radio_field_options.${option_index}.label`, - e.target.value, + `page_fields.${field_index}.field_data.radio_field_options.${option_index}.label`, + e.target.value ); await trigger("page_fields"); }; @@ -52,23 +59,23 @@ export const PageFieldModal = (({ const checkboxOptionLabelOnChange = async ( e: any, field_index: number, - option_index: number, -) => { - setValue( + option_index: number + ) => { + setValue( `page_fields.${field_index}.field_data.checkbox_field_options.${option_index}.label`, - e.target.value, - ); - await trigger("page_fields"); -}; - + e.target.value + ); + await trigger("page_fields"); + }; + const optionLabelOnChange = async ( e: any, field_index: number, - option_index: number, + option_index: number ) => { setValue( `page_fields.${field_index}.field_data.select_field_options.${option_index}.label`, - e.target.value, + e.target.value ); await trigger("page_fields"); }; @@ -76,24 +83,23 @@ export const PageFieldModal = (({ const optionValueOnChange = async ( e: any, field_index: number, - option_index: number, + option_index: number ) => { setValue( `page_fields.${field_index}.field_data.select_field_options.${option_index}.value`, - e.target.value, + e.target.value ); await trigger("page_fields"); }; - const radioOptionValueOnChange = async ( - e: any, - field_index: number, - option_index: number, + e: any, + field_index: number, + option_index: number ) => { setValue( - `page_fields.${field_index}.field_data.radio_field_options.${option_index}.value`, - e.target.value, + `page_fields.${field_index}.field_data.radio_field_options.${option_index}.value`, + e.target.value ); await trigger("page_fields"); }; @@ -101,24 +107,21 @@ export const PageFieldModal = (({ const checkboxOptionValueOnChange = async ( e: any, field_index: number, - option_index: number, -) => { - setValue( + option_index: number + ) => { + setValue( `page_fields.${field_index}.field_data.checkbox_field_options.${option_index}.value`, - e.target.value, - ); - await trigger("page_fields"); -}; + e.target.value + ); + await trigger("page_fields"); + }; - const optionAddOnClick = async ( e: React.MouseEvent, - field_index: number, + field_index: number ) => { e.preventDefault(); - const page_field: SaveFieldType = getValues( - `page_fields.${field_index}`, - ); + const page_field: SaveFieldType = getValues(`page_fields.${field_index}`); const empty_option: AvoRedPageFieldSelectFieldDataOptions = { label: "", value: "", @@ -130,13 +133,11 @@ export const PageFieldModal = (({ }; const radioOptionAddOnClick = async ( - e: React.MouseEvent, - field_index: number, + e: React.MouseEvent, + field_index: number ) => { e.preventDefault(); - const page_field: SaveFieldType = getValues( - `page_fields.${field_index}`, - ); + const page_field: SaveFieldType = getValues(`page_fields.${field_index}`); const empty_option: AvoRedPageFieldRadioFieldDataOptions = { label: "", value: "", @@ -149,45 +150,39 @@ export const PageFieldModal = (({ const checkboxOptionAddOnClick = async ( e: React.MouseEvent, - field_index: number, -) => { - e.preventDefault(); - const page_field: SaveFieldType = getValues( - `page_fields.${field_index}`, - ); - const empty_option: AvoRedPageFieldCheckboxFieldDataOptions = { - label: "", - value: "", - }; + field_index: number + ) => { + e.preventDefault(); + const page_field: SaveFieldType = getValues(`page_fields.${field_index}`); + const empty_option: AvoRedPageFieldCheckboxFieldDataOptions = { + label: "", + value: "", + }; - page_field.field_data?.checkbox_field_options?.push(empty_option); + page_field.field_data?.checkbox_field_options?.push(empty_option); - await trigger("page_fields"); -}; + await trigger("page_fields"); + }; const optionRemoveOnClick = async ( e: React.MouseEvent, field_index: number, - option_index: number, + option_index: number ) => { e.preventDefault(); - const page_field: SaveFieldType = getValues( - `page_fields.${field_index}`, - ); + const page_field: SaveFieldType = getValues(`page_fields.${field_index}`); page_field.field_data?.select_field_options?.splice(option_index, 1); await trigger(`page_fields.${field_index}`); }; const radioOptionRemoveOnClick = async ( - e: React.MouseEvent, - field_index: number, - option_index: number, + e: React.MouseEvent, + field_index: number, + option_index: number ) => { e.preventDefault(); - const page_field: SaveFieldType = getValues( - `page_fields.${field_index}`, - ); + const page_field: SaveFieldType = getValues(`page_fields.${field_index}`); page_field.field_data?.radio_field_options?.splice(option_index, 1); await trigger(`page_fields.${field_index}`); @@ -196,44 +191,64 @@ export const PageFieldModal = (({ const checkboxOptionRemoveOnClick = async ( e: React.MouseEvent, field_index: number, - option_index: number, -) => { - e.preventDefault(); - const page_field: SaveFieldType = getValues( - `page_fields.${field_index}`, - ); - page_field.field_data?.checkbox_field_options?.splice(option_index, 1); + option_index: number + ) => { + e.preventDefault(); + const page_field: SaveFieldType = getValues(`page_fields.${field_index}`); + page_field.field_data?.checkbox_field_options?.splice(option_index, 1); - await trigger(`page_fields.${field_index}`); -}; + await trigger(`page_fields.${field_index}`); + }; const renderFieldData = (current_index: number) => { - const page_field: SaveFieldType = getValues( - `page_fields.${current_index}`, - ); - - - switch (page_field.field_type) { - case AvoRedPageFieldType.SELECT: - return ( - <> - {_.get(page_field, 'field_data.select_field_options', []).map( - (option, option_index) => { - return ( -
-
-
-
+ const page_field: SaveFieldType = getValues(`page_fields.${current_index}`); + + switch (page_field.field_type) { + case AvoRedPageFieldType.SELECT: + return ( + <> + {_.get(page_field, "field_data.select_field_options", []).map( + (option, option_index) => { + return ( +
+
+
+
+ + optionLabelOnChange( + e, + current_index, + option_index + ) + } + placeholder={t("label")} + className="appearance-none rounded-md ring-1 ring-gray-400 + relative border-0 block w-full px-3 py-2 placeholder-gray-500 text-gray-900 + active::ring-primary-500 + focus:ring-primary-500 focus:outline-none focus:z-10 + disabled:bg-gray-200 disabled:opacity-70 + sm:text-sm " + /> +
+
+
+
+
- optionLabelOnChange( + optionValueOnChange( e, current_index, - option_index, + option_index ) } - placeholder={t("label")} + placeholder={t("value")} className="appearance-none rounded-md ring-1 ring-gray-400 relative border-0 block w-full px-3 py-2 placeholder-gray-500 text-gray-900 active::ring-primary-500 @@ -242,483 +257,471 @@ export const PageFieldModal = (({ sm:text-sm " />
-
-
-
-
- - optionValueOnChange( - e, - current_index, - option_index, - ) - } - placeholder={t("value")} - className="appearance-none rounded-md ring-1 ring-gray-400 - relative border-0 block w-full px-3 py-2 placeholder-gray-500 text-gray-900 - active::ring-primary-500 - focus:ring-primary-500 focus:outline-none focus:z-10 - disabled:bg-gray-200 disabled:opacity-70 - sm:text-sm " - /> -
-
- {_.size(getValues( - `page_fields.${current_index}.field_data.select_field_options`, - )) === - option_index + 1 ? ( - <> - - - ) : ( - <> - - - )} -
+
+ {_.size( + getValues( + `page_fields.${current_index}.field_data.select_field_options` + ) + ) === + option_index + 1 ? ( + <> + + + ) : ( + <> + + + )}
- ); - }, - )} - - ); - case AvoRedPageFieldType.Radio: - return ( - <> - {_.get(page_field, 'field_data.radio_field_options', []).map( - (option, option_index) => { - return ( -
-
-
-
- - radioOptionLabelOnChange( - e, - current_index, - option_index, - ) - } - placeholder={t("label")} - className="appearance-none rounded-md ring-1 ring-gray-400 +
+ ); + } + )} + + ); + case AvoRedPageFieldType.Radio: + return ( + <> + {_.get(page_field, "field_data.radio_field_options", []).map( + (option, option_index) => { + return ( +
+
+
+
+ + radioOptionLabelOnChange( + e, + current_index, + option_index + ) + } + placeholder={t("label")} + className="appearance-none rounded-md ring-1 ring-gray-400 relative border-0 block w-full px-3 py-2 placeholder-gray-500 text-gray-900 active::ring-primary-500 focus:ring-primary-500 focus:outline-none focus:z-10 disabled:bg-gray-200 disabled:opacity-70 sm:text-sm " - /> -
-
-
-
-
- - radioOptionValueOnChange( - e, - current_index, - option_index, - ) - } - placeholder={t("value")} - className="appearance-none rounded-md ring-1 ring-gray-400 + /> +
+
+
+
+
+ + radioOptionValueOnChange( + e, + current_index, + option_index + ) + } + placeholder={t("value")} + className="appearance-none rounded-md ring-1 ring-gray-400 relative border-0 block w-full px-3 py-2 placeholder-gray-500 text-gray-900 active::ring-primary-500 focus:ring-primary-500 focus:outline-none focus:z-10 disabled:bg-gray-200 disabled:opacity-70 sm:text-sm " - /> -
-
- {_.size(getValues( - `page_fields.${current_index}.field_data.radio_field_options`, - )) === - option_index + 1 ? ( - <> - - - ) : ( - <> - - - )} -
-
-
-
+ />
- ); - }, - )} - - ); - - case AvoRedPageFieldType.Checkbox: - return ( - <> - {_.get(page_field, 'field_data.checkbox_field_options', []).map( - (option, option_index) => { - return ( -
-
-
-
- - checkboxOptionLabelOnChange( - e, - current_index, - option_index, - ) - } - placeholder={t("label")} - className="appearance-none rounded-md ring-1 ring-gray-400 +
+ {_.size( + getValues( + `page_fields.${current_index}.field_data.radio_field_options` + ) + ) === + option_index + 1 ? ( + <> + + + ) : ( + <> + + + )} +
+
+
+
+
+ ); + } + )} + + ); + + case AvoRedPageFieldType.Checkbox: + return ( + <> + {_.get(page_field, "field_data.checkbox_field_options", []).map( + (option, option_index) => { + return ( +
+
+
+
+ + checkboxOptionLabelOnChange( + e, + current_index, + option_index + ) + } + placeholder={t("label")} + className="appearance-none rounded-md ring-1 ring-gray-400 relative border-0 block w-full px-3 py-2 placeholder-gray-500 text-gray-900 active::ring-primary-500 focus:ring-primary-500 focus:outline-none focus:z-10 disabled:bg-gray-200 disabled:opacity-70 sm:text-sm " - /> -
-
-
-
-
- - checkboxOptionValueOnChange( - e, - current_index, - option_index, - ) - } - placeholder={t("value")} - className="appearance-none rounded-md ring-1 ring-gray-400 + /> +
+
+
+
+
+ + checkboxOptionValueOnChange( + e, + current_index, + option_index + ) + } + placeholder={t("value")} + className="appearance-none rounded-md ring-1 ring-gray-400 relative border-0 block w-full px-3 py-2 placeholder-gray-500 text-gray-900 active::ring-primary-500 focus:ring-primary-500 focus:outline-none focus:z-10 disabled:bg-gray-200 disabled:opacity-70 sm:text-sm " - /> -
-
- {_.size(getValues( - `page_fields.${current_index}.field_data.checkbox_field_options`, - )) === - option_index + 1 ? ( - <> - - - ) : ( - <> - - - )} -
-
-
-
-
- ); - }, - )} - - ); - - - default: - return <>; - } - }; + /> +
+
+ {_.size( + getValues( + `page_fields.${current_index}.field_data.checkbox_field_options` + ) + ) === + option_index + 1 ? ( + <> + + + ) : ( + <> + + + )} +
+
+
+
+
+ ); + } + )} + + ); + + default: + return <>; + } + }; - const onPageFieldChange = async ( - index: number, - field_type: AvoRedPageFieldType, - data_type: AvoRedPageDataType, - ) => { - setValue(`page_fields.${index}.field_type`, field_type); - setValue(`page_fields.${index}.data_type`, data_type); - - if (field_type === AvoRedPageFieldType.SELECT) { - setValue(`page_fields.${index}.field_data.select_field_options`, []); - } - - switch (field_type) { - case AvoRedPageFieldType.SELECT: - const empty_option: AvoRedPageFieldSelectFieldDataOptions = { - label: "", - value: "", - }; - - const options: AvoRedPageFieldData = { - select_field_options: [], - }; - if (typeof options.select_field_options == "undefined") { - options.select_field_options = []; - } - options.select_field_options.push(empty_option); - setValue(`page_fields.${index}.field_data`, options); - - break; - case AvoRedPageFieldType.Radio: - const radio_empty_option: AvoRedPageFieldRadioFieldDataOptions = { - label: "", - value: "", - }; - - const radio_field_data: AvoRedPageFieldData = { - radio_field_options: [], - }; - if (typeof radio_field_data.radio_field_options == "undefined") { - radio_field_data.radio_field_options = []; - } - radio_field_data.radio_field_options.push(radio_empty_option); - setValue(`page_fields.${index}.field_data`, radio_field_data); - - break; - case AvoRedPageFieldType.Checkbox: - const checkbox_empty_option: AvoRedPageFieldCheckboxFieldDataOptions = { - label: "", - value: "", - }; - - const existing_content = getValues(`page_fields.${index}.field_content`); - - // delete the property as it has beed added as part of default values - delete existing_content.text_value; - delete existing_content.integer_value; - setValue(`page_fields.${index}.field_content`, existing_content); - - - const checkbox_field_data: AvoRedPageFieldData = { - checkbox_field_options: [], - }; - if (typeof checkbox_field_data.checkbox_field_options == "undefined") { - checkbox_field_data.checkbox_field_options = []; - } - checkbox_field_data.checkbox_field_options.push(checkbox_empty_option); - setValue(`page_fields.${index}.field_data`, checkbox_field_data); - - break; - default: - break; - } - - await trigger(`page_fields.${index}`); - }; - - return ( - setIsOpen(false)} - modal_body={ -
-
-
-
- -
-
- -
+ const onPageFieldChange = async ( + index: number, + field_type: AvoRedPageFieldType, + data_type: AvoRedPageDataType + ) => { + setValue(`page_fields.${index}.field_type`, field_type); + setValue(`page_fields.${index}.data_type`, data_type); + + if (field_type === AvoRedPageFieldType.SELECT) { + setValue(`page_fields.${index}.field_data.select_field_options`, []); + } + + switch (field_type) { + case AvoRedPageFieldType.SELECT: + const empty_option: AvoRedPageFieldSelectFieldDataOptions = { + label: "", + value: "", + }; + + const options: AvoRedPageFieldData = { + select_field_options: [], + }; + if (typeof options.select_field_options == "undefined") { + options.select_field_options = []; + } + options.select_field_options.push(empty_option); + setValue(`page_fields.${index}.field_data`, options); + + break; + case AvoRedPageFieldType.Radio: + const radio_empty_option: AvoRedPageFieldRadioFieldDataOptions = { + label: "", + value: "", + }; + + const radio_field_data: AvoRedPageFieldData = { + radio_field_options: [], + }; + if (typeof radio_field_data.radio_field_options == "undefined") { + radio_field_data.radio_field_options = []; + } + radio_field_data.radio_field_options.push(radio_empty_option); + setValue(`page_fields.${index}.field_data`, radio_field_data); + + break; + case AvoRedPageFieldType.Checkbox: + const checkbox_empty_option: AvoRedPageFieldCheckboxFieldDataOptions = { + label: "", + value: "", + }; + + const existing_content = getValues( + `page_fields.${index}.field_content` + ); + + // delete the property as it has beed added as part of default values + delete existing_content.text_value; + delete existing_content.integer_value; + setValue(`page_fields.${index}.field_content`, existing_content); + + const checkbox_field_data: AvoRedPageFieldData = { + checkbox_field_options: [], + }; + if (typeof checkbox_field_data.checkbox_field_options == "undefined") { + checkbox_field_data.checkbox_field_options = []; + } + checkbox_field_data.checkbox_field_options.push(checkbox_empty_option); + setValue(`page_fields.${index}.field_data`, checkbox_field_data); -
{renderFieldData(currentIndex)}
+ break; + default: + break; + } + + await trigger(`page_fields.${index}`); + }; + + return ( + setIsOpen(false)} + modal_body={ +
+
+
+
+
-
-
-
- onPageFieldChange( - currentIndex, - AvoRedPageFieldType.TEXT, - AvoRedPageDataType.TEXT, - ) - } - className={`${getValues(`page_fields.${currentIndex}.field_type`) === AvoRedPageFieldType.TEXT ? "bg-primary-200" : "bg-gray-300"} +
+ +
+ +
{renderFieldData(currentIndex)}
+
+
+
+
+ onPageFieldChange( + currentIndex, + AvoRedPageFieldType.TEXT, + AvoRedPageDataType.TEXT + ) + } + className={`${getValues(`page_fields.${currentIndex}.field_type`) === AvoRedPageFieldType.TEXT ? "bg-primary-200" : "bg-gray-300"} ring-1 ring-gray-300 hover:cursor-pointer hover:ring-primary-300 p-3 rounded`} - > - {t("text_field")} -
-
- onPageFieldChange( - currentIndex, - AvoRedPageFieldType.TEXTAREA, - AvoRedPageDataType.TEXT, - ) - } - className={`${getValues(`page_fields.${currentIndex}.field_type`) === AvoRedPageFieldType.TEXTAREA ? "bg-primary-200" : "bg-gray-300"} + > + {t("text_field")} +
+
+ onPageFieldChange( + currentIndex, + AvoRedPageFieldType.TEXTAREA, + AvoRedPageDataType.TEXT + ) + } + className={`${getValues(`page_fields.${currentIndex}.field_type`) === AvoRedPageFieldType.TEXTAREA ? "bg-primary-200" : "bg-gray-300"} ring-1 mt-2 ring-gray-300 hover:cursor-pointer hover:ring-primary-300 p-3 rounded`} - > - {t("textarea_field")} -
-
- onPageFieldChange( - currentIndex, - AvoRedPageFieldType.SELECT, - AvoRedPageDataType.TEXT, - ) - } - className={`${getValues(`page_fields.${currentIndex}.field_type`) === AvoRedPageFieldType.SELECT ? "bg-primary-200" : "bg-gray-300"} + > + {t("textarea_field")} +
+
+ onPageFieldChange( + currentIndex, + AvoRedPageFieldType.SELECT, + AvoRedPageDataType.TEXT + ) + } + className={`${getValues(`page_fields.${currentIndex}.field_type`) === AvoRedPageFieldType.SELECT ? "bg-primary-200" : "bg-gray-300"} ring-1 mt-2 ring-gray-300 hover:cursor-pointer hover:ring-primary-300 p-3 rounded`} - > - {t("select_field")} -
+ > + {t("select_field")} +
-
- onPageFieldChange( - currentIndex, - AvoRedPageFieldType.TextEditor, - AvoRedPageDataType.TEXT, - ) - } - className={`${getValues(`page_fields.${currentIndex}.field_type`) === AvoRedPageFieldType.TextEditor ? "bg-primary-200" : "bg-gray-300"} +
+ onPageFieldChange( + currentIndex, + AvoRedPageFieldType.TextEditor, + AvoRedPageDataType.TEXT + ) + } + className={`${getValues(`page_fields.${currentIndex}.field_type`) === AvoRedPageFieldType.TextEditor ? "bg-primary-200" : "bg-gray-300"} ring-1 mt-2 ring-gray-300 hover:cursor-pointer hover:ring-primary-300 p-3 rounded`} - > - {t("text_editor_field")} -
+ > + {t("text_editor_field")} +
-
- onPageFieldChange( - currentIndex, - AvoRedPageFieldType.Radio, - AvoRedPageDataType.TEXT, - ) - } - className={`${getValues(`page_fields.${currentIndex}.field_type`) === AvoRedPageFieldType.Radio ? "bg-primary-200" : "bg-gray-300"} +
+ onPageFieldChange( + currentIndex, + AvoRedPageFieldType.Radio, + AvoRedPageDataType.TEXT + ) + } + className={`${getValues(`page_fields.${currentIndex}.field_type`) === AvoRedPageFieldType.Radio ? "bg-primary-200" : "bg-gray-300"} ring-1 mt-2 ring-gray-300 hover:cursor-pointer hover:ring-primary-300 p-3 rounded`} - > - {t("radio_field")} -
+ > + {t("radio_field")} +
-
- onPageFieldChange( - currentIndex, - AvoRedPageFieldType.Checkbox, - AvoRedPageDataType.Array_Text, - ) - } - className={`${getValues(`page_fields.${currentIndex}.field_type`) === AvoRedPageFieldType.Checkbox ? "bg-primary-200" : "bg-gray-300"} +
+ onPageFieldChange( + currentIndex, + AvoRedPageFieldType.Checkbox, + AvoRedPageDataType.Array_Text + ) + } + className={`${getValues(`page_fields.${currentIndex}.field_type`) === AvoRedPageFieldType.Checkbox ? "bg-primary-200" : "bg-gray-300"} ring-1 mt-2 ring-gray-300 hover:cursor-pointer hover:ring-primary-300 p-3 rounded`} - > - {t("checkbox_field")} -
+ > + {t("checkbox_field")} +
-
- onPageFieldChange( - currentIndex, - AvoRedPageFieldType.SingleImage, - AvoRedPageDataType.TEXT, - ) - } - className={`${getValues(`page_fields.${currentIndex}.field_type`) === AvoRedPageFieldType.SingleImage ? "bg-primary-200" : "bg-gray-300"} +
+ onPageFieldChange( + currentIndex, + AvoRedPageFieldType.SingleImage, + AvoRedPageDataType.TEXT + ) + } + className={`${getValues(`page_fields.${currentIndex}.field_type`) === AvoRedPageFieldType.SingleImage ? "bg-primary-200" : "bg-gray-300"} ring-1 mt-2 ring-gray-300 hover:cursor-pointer hover:ring-primary-300 p-3 rounded`} - > - {t("single_image_field")} -
+ > + {t("single_image_field")}
-
-
-
-
- setIsOpen(false)} - className="bg-primary-500" - label={t("create_page_field")} - /> -
-
- setIsOpen(false)} - label={t("cancel")} - /> -
+
+
+
+
+
+ setIsOpen(false)} + className="bg-primary-500" + label={t("create_page_field")} + /> +
+
+ setIsOpen(false)} + label={t("cancel")} + />
- } - modal_header={`Page Field`} - isOpen={isOpen} - > - ); -}) +
+ } + modal_header={`Page Field`} + isOpen={isOpen} + > + ); +}; diff --git a/react-admin/src/types/collection/CollectionType.ts b/react-admin/src/types/collection/CollectionType.ts new file mode 100644 index 00000000..a42df2ce --- /dev/null +++ b/react-admin/src/types/collection/CollectionType.ts @@ -0,0 +1,10 @@ +export type CollectionType = { + id: string; + name: string; + identifier: string; + created_at: string; + updated_at: string; + created_by: string; + updated_by: string; + action?: string; +} \ No newline at end of file diff --git a/react-admin/src/types/collection/CreatableCollectionType.ts b/react-admin/src/types/collection/CreatableCollectionType.ts new file mode 100644 index 00000000..2501ed96 --- /dev/null +++ b/react-admin/src/types/collection/CreatableCollectionType.ts @@ -0,0 +1,21 @@ + +export type SavableCollectionType = { + name: string; + identifier: string; + collection_fields: Array +} + +export type SaveCollectionFieldType = { + name: string; + identifier: string; + data_type: CollectionFieldDataType; + field_type: CollectionFieldFieldType; +} + +export enum CollectionFieldDataType { + TEXT = "TEXT", +} + +export enum CollectionFieldFieldType { + TEXT = "Text", +} \ No newline at end of file diff --git a/react-admin/src/types/collection/EditableCollectionType.ts b/react-admin/src/types/collection/EditableCollectionType.ts new file mode 100644 index 00000000..ae4b2889 --- /dev/null +++ b/react-admin/src/types/collection/EditableCollectionType.ts @@ -0,0 +1,4 @@ +export default interface EditableCollectionType { + id: string; + name: string; +} \ No newline at end of file diff --git a/react-admin/src/types/collection/PutCollectionIdentifierType.ts b/react-admin/src/types/collection/PutCollectionIdentifierType.ts new file mode 100644 index 00000000..c2a365e4 --- /dev/null +++ b/react-admin/src/types/collection/PutCollectionIdentifierType.ts @@ -0,0 +1,3 @@ +export type PutCollectionIdentifierType = { + identifier: String; +} \ No newline at end of file diff --git a/react-admin/src/types/content/ContentType.ts b/react-admin/src/types/content/ContentType.ts new file mode 100644 index 00000000..089fc4db --- /dev/null +++ b/react-admin/src/types/content/ContentType.ts @@ -0,0 +1,52 @@ +export type ContentModel = { + id: string; + name: string; + identifier: string; + created_at: string; + created_by: string; + updated_at: string; + updated_by: string; + action: string; +} + + +export type SaveContentType = { + name: string; + content_type: string; + identifier: string; + content_fields: Array; +} + +export type SaveContentFieldType = { + name: string; + identifier: string; + data_type: AvoRedContentDataType; + field_type: AvoRedContentFieldType; + field_content: AvoRedContentFieldContent; + // field_data?: AvoRedContentFieldData, +} + + +export enum AvoRedContentDataType { + TEXT = "TEXT", + // INT = "INT", + // Array_Text = "Array_Text" +} + +export type AvoRedContentFieldContent = { + text_value?: ContentTextContent, +} + +export type ContentTextContent = { + text_value: string; +} + +export enum AvoRedContentFieldType { + TEXT = "Text", + // TEXTAREA = "Textarea", + // SELECT = "Select", + // TextEditor = "TextEditor", + // Radio = "Radio", + // Checkbox = "Checkbox", + // SingleImage = "SingleImage" + } diff --git a/react-admin/src/types/model/CreatableModelType.ts b/react-admin/src/types/model/CreatableModelType.ts index 572da6a8..4435b4fa 100644 --- a/react-admin/src/types/model/CreatableModelType.ts +++ b/react-admin/src/types/model/CreatableModelType.ts @@ -1,4 +1,9 @@ export type CreatableModelType = { name: string; identifier: string; + model_fields: Array +} + +export type SaveModelFieldType = { + name: string; } \ No newline at end of file diff --git a/src/api/handlers/admin_user/admin_user_forgot_password_api_handler.rs b/src/api/handlers/admin_user/admin_user_forgot_password_api_handler.rs index d0ae0138..b69e2657 100644 --- a/src/api/handlers/admin_user/admin_user_forgot_password_api_handler.rs +++ b/src/api/handlers/admin_user/admin_user_forgot_password_api_handler.rs @@ -1,30 +1,33 @@ -use std::sync::Arc; -use axum::extract::State; -use axum::Json; -use serde::Serialize; use crate::api::handlers::admin_user::request::admin_user_forgot_password_request::AdminUserForgotPasswordRequest; use crate::avored_state::AvoRedState; use crate::error::{Error, Result}; use crate::models::validation_error::ErrorResponse; use crate::responses::ApiResponse; +use axum::extract::State; +use axum::Json; +use serde::Serialize; +use std::sync::Arc; #[derive(Serialize, Default)] pub struct ForgotPasswordViewModel { - pub link: String + pub link: String, } -pub async fn admin_user_forgot_password_api_handler ( +pub async fn admin_user_forgot_password_api_handler( state: State>, Json(payload): Json, ) -> Result>> { - println!("->> {:<12} - admin_user_forgot_password_api_handler", "HANDLER"); + println!( + "->> {:<12} - admin_user_forgot_password_api_handler", + "HANDLER" + ); let error_messages = payload.validate()?; if !error_messages.is_empty() { let error_response = ErrorResponse { status: false, - errors: error_messages + errors: error_messages, }; return Err(Error::BadRequest(error_response)); diff --git a/src/api/handlers/admin_user/admin_user_login_api_handler.rs b/src/api/handlers/admin_user/admin_user_login_api_handler.rs index b95ec8ed..1ca09941 100644 --- a/src/api/handlers/admin_user/admin_user_login_api_handler.rs +++ b/src/api/handlers/admin_user/admin_user_login_api_handler.rs @@ -1,4 +1,9 @@ -use std::sync::Arc; +use crate::api::handlers::admin_user::request::authenticate_admin_user_request::AuthenticateAdminUserRequest; +use crate::avored_state::AvoRedState; +use crate::error::{Error, Result}; +use crate::models::admin_user_model::AdminUserModel; +use crate::models::token_claim_model::TokenClaims; +use crate::models::validation_error::ErrorResponse; use axum::extract::State; use axum::http::{header, Response}; use axum::Json; @@ -6,14 +11,8 @@ use axum_extra::extract::cookie::{Cookie, SameSite}; use jsonwebtoken::{encode, EncodingKey, Header}; use serde::{Deserialize, Serialize}; use serde_json::json; +use std::sync::Arc; use utoipa::ToSchema; -use crate::api::handlers::admin_user::request::authenticate_admin_user_request::AuthenticateAdminUserRequest; -use crate::avored_state::AvoRedState; -use crate::error::{Error, Result}; -use crate::models::admin_user_model::AdminUserModel; -use crate::models::token_claim_model::TokenClaims; -use crate::models::validation_error::ErrorResponse; - /// Login Admin User /// @@ -37,7 +36,7 @@ pub async fn admin_user_login_api_handler( if !error_messages.is_empty() { let error_response = ErrorResponse { status: false, - errors: error_messages + errors: error_messages, }; return Err(Error::BadRequest(error_response)); @@ -50,10 +49,7 @@ pub async fn admin_user_login_api_handler( let is_password_match: bool = state .admin_user_service - .compare_password( - payload.password.clone(), - admin_user_model.password.clone() - )?; + .compare_password(payload.password.clone(), admin_user_model.password.clone())?; if !is_password_match { return Err(Error::Authentication); @@ -65,7 +61,7 @@ pub async fn admin_user_login_api_handler( let claims: TokenClaims = TokenClaims { sub: admin_user_model.clone().id, name: admin_user_model.clone().full_name, - email:admin_user_model.clone().email, + email: admin_user_model.clone().email, admin_user_model: admin_user_model.clone(), exp, iat, @@ -88,7 +84,7 @@ pub async fn admin_user_login_api_handler( let response_data = LoginResponseData { status: true, data: token, - admin_user: admin_user_model + admin_user: admin_user_model, }; Ok(Json(response_data)) @@ -98,22 +94,19 @@ pub async fn admin_user_login_api_handler( pub struct LoginResponseData { pub status: bool, pub data: String, - pub admin_user: AdminUserModel + pub admin_user: AdminUserModel, } - #[cfg(test)] mod tests { + use crate::api::rest_api_routes::tests::{get_axum_app, send_post_request}; + use crate::error::Result; use axum::body::Body; use axum::http::StatusCode; use tower::ServiceExt; - use crate::api::rest_api_routes::tests::{ get_axum_app, send_post_request}; - use crate::error::Result; - #[tokio::test] - async fn test_admin_user_login_api_handler() -> Result<()> - { + async fn test_admin_user_login_api_handler() -> Result<()> { let (app, _state) = get_axum_app().await.unwrap(); //@todo do a post request to a setup // then do a post request with username and password @@ -125,7 +118,10 @@ mod tests { }"#, ); - let response = app.oneshot(send_post_request("/api/setup", payload)).await.unwrap(); + let response = app + .oneshot(send_post_request("/api/setup", payload)) + .await + .unwrap(); assert_eq!(response.status(), StatusCode::OK); let res_b = response.into_body(); diff --git a/src/api/handlers/admin_user/admin_user_reset_password_api_handler.rs b/src/api/handlers/admin_user/admin_user_reset_password_api_handler.rs index ad33d720..5e613405 100644 --- a/src/api/handlers/admin_user/admin_user_reset_password_api_handler.rs +++ b/src/api/handlers/admin_user/admin_user_reset_password_api_handler.rs @@ -1,30 +1,33 @@ -use std::sync::Arc; -use axum::extract::State; -use axum::Json; -use serde::Serialize; use crate::api::handlers::admin_user::request::admin_user_reset_password_request::AdminUserResetPasswordRequest; use crate::avored_state::AvoRedState; use crate::error::{Error, Result}; use crate::models::validation_error::ErrorResponse; use crate::responses::ApiResponse; +use axum::extract::State; +use axum::Json; +use serde::Serialize; +use std::sync::Arc; #[derive(Serialize, Default)] pub struct ForgotPasswordViewModel { - pub link: String + pub link: String, } pub async fn admin_user_reset_password_api_handler( state: State>, Json(payload): Json, ) -> Result>> { - println!("->> {:<12} - admin_user_reset_password_api_handler", "HANDLER"); + println!( + "->> {:<12} - admin_user_reset_password_api_handler", + "HANDLER" + ); let error_messages = payload.validate(&state).await?; if !error_messages.is_empty() { let error_response = ErrorResponse { status: false, - errors: error_messages + errors: error_messages, }; return Err(Error::BadRequest(error_response)); } @@ -40,7 +43,7 @@ pub async fn admin_user_reset_password_api_handler( let mut response_data = ApiResponse { status: false, - data: false + data: false, }; if update_password_status { @@ -51,15 +54,14 @@ pub async fn admin_user_reset_password_api_handler( response_data = ApiResponse { status: true, - data: expired_status + data: expired_status, }; } Ok(Json(response_data)) } - #[derive(Serialize)] pub struct ResponseData { - status: bool + status: bool, } diff --git a/src/api/handlers/admin_user/admin_user_table_api_handler.rs b/src/api/handlers/admin_user/admin_user_table_api_handler.rs index ff322277..8e086496 100644 --- a/src/api/handlers/admin_user/admin_user_table_api_handler.rs +++ b/src/api/handlers/admin_user/admin_user_table_api_handler.rs @@ -1,11 +1,11 @@ -use std::sync::Arc; -use axum::extract::{Query, State}; -use axum::{Extension, Json}; use crate::api::handlers::page::request::page_table_request::PageTableRequest; use crate::avored_state::AvoRedState; use crate::error::{Error, Result}; use crate::models::admin_user_model::AdminUserPagination; use crate::models::token_claim_model::LoggedInUser; +use axum::extract::{Query, State}; +use axum::{Extension, Json}; +use std::sync::Arc; pub async fn admin_user_table_api_handler( state: State>, @@ -26,7 +26,8 @@ pub async fn admin_user_table_api_handler( let order = query_param.order.unwrap_or(String::from("")); let admin_user_pagination = state .admin_user_service - .paginate(&state.db, current_page, order).await?; + .paginate(&state.db, current_page, order) + .await?; Ok(Json(admin_user_pagination)) } diff --git a/src/api/handlers/admin_user/change_password_api_handler.rs b/src/api/handlers/admin_user/change_password_api_handler.rs index 63ec2ce5..da0052ff 100644 --- a/src/api/handlers/admin_user/change_password_api_handler.rs +++ b/src/api/handlers/admin_user/change_password_api_handler.rs @@ -1,17 +1,17 @@ -use std::sync::Arc; -use argon2::{Argon2, PasswordHasher}; -use argon2::password_hash::SaltString; -use axum::extract::{ State}; -use axum::{Extension, Json}; -use rust_i18n::t; use crate::api::handlers::admin_user::request::change_password_request::ChangePasswordRequest; use crate::avored_state::AvoRedState; use crate::error::{Error, Result}; use crate::models::token_claim_model::LoggedInUser; use crate::models::validation_error::{ErrorMessage, ErrorResponse}; use crate::responses::ApiResponse; +use argon2::password_hash::SaltString; +use argon2::{Argon2, PasswordHasher}; +use axum::extract::State; +use axum::{Extension, Json}; +use rust_i18n::t; +use std::sync::Arc; -pub async fn change_password_api_handler ( +pub async fn change_password_api_handler( Extension(logged_in_user): Extension, state: State>, Json(payload): Json, @@ -20,17 +20,15 @@ pub async fn change_password_api_handler ( let mut error_messages = payload.validate()?; - let is_password_match: bool = state - .admin_user_service - .compare_password( - payload.current_password.clone(), - logged_in_user.admin_user_model.password - )?; + let is_password_match: bool = state.admin_user_service.compare_password( + payload.current_password.clone(), + logged_in_user.admin_user_model.password, + )?; if !is_password_match { let error_message = ErrorMessage { key: String::from("password"), - message: t!("password_match_error").to_string() + message: t!("password_match_error").to_string(), }; error_messages.push(error_message); } @@ -38,7 +36,7 @@ pub async fn change_password_api_handler ( if !error_messages.is_empty() { let error_response = ErrorResponse { status: false, - errors: error_messages + errors: error_messages, }; return Err(Error::BadRequest(error_response)); @@ -60,7 +58,7 @@ pub async fn change_password_api_handler ( let response_data = ApiResponse { status: true, - data: update_password_status + data: update_password_status, }; Ok(Json(response_data)) diff --git a/src/api/handlers/admin_user/fetch_admin_user_api_handler.rs b/src/api/handlers/admin_user/fetch_admin_user_api_handler.rs index 6181d5a7..6a842e55 100644 --- a/src/api/handlers/admin_user/fetch_admin_user_api_handler.rs +++ b/src/api/handlers/admin_user/fetch_admin_user_api_handler.rs @@ -1,18 +1,20 @@ +use crate::{avored_state::AvoRedState, error::Result}; use std::sync::Arc; -use crate::{ - avored_state::AvoRedState, error::Result -}; -use axum::{Extension, extract::{Path as AxumPath, State}, Json, response::IntoResponse}; -use serde::Serialize; use crate::error::Error; use crate::models::admin_user_model::AdminUserModel; use crate::models::token_claim_model::LoggedInUser; +use axum::{ + extract::{Path as AxumPath, State}, + response::IntoResponse, + Extension, Json, +}; +use serde::Serialize; pub async fn fetch_admin_user_api_handler( Extension(logged_in_user): Extension, AxumPath(admin_user_id): AxumPath, - state: State> + state: State>, ) -> Result { println!("->> {:<12} - fetch_admin_user_api_handler", "HANDLER"); @@ -30,15 +32,14 @@ pub async fn fetch_admin_user_api_handler( .await?; let response = FetchAdminUserResponse { status: true, - admin_user_model + admin_user_model, }; Ok(Json(response)) } - #[derive(Serialize, Debug)] pub struct FetchAdminUserResponse { pub status: bool, - pub admin_user_model: AdminUserModel -} \ No newline at end of file + pub admin_user_model: AdminUserModel, +} diff --git a/src/api/handlers/admin_user/logged_in_user_api_handler.rs b/src/api/handlers/admin_user/logged_in_user_api_handler.rs index 0e8f2e9f..7c749395 100644 --- a/src/api/handlers/admin_user/logged_in_user_api_handler.rs +++ b/src/api/handlers/admin_user/logged_in_user_api_handler.rs @@ -1,7 +1,7 @@ use crate::error::Result; -use axum::{Extension, Json, response::IntoResponse}; use crate::models::token_claim_model::LoggedInUser; use crate::responses::ApiResponse; +use axum::{response::IntoResponse, Extension, Json}; pub async fn logged_in_user_api_handler( Extension(logged_in_user): Extension, @@ -10,7 +10,7 @@ pub async fn logged_in_user_api_handler( let response = ApiResponse { status: true, - data: logged_in_user + data: logged_in_user, }; Ok(Json(response)) diff --git a/src/api/handlers/admin_user/mod.rs b/src/api/handlers/admin_user/mod.rs index b19ab865..7dd747d8 100644 --- a/src/api/handlers/admin_user/mod.rs +++ b/src/api/handlers/admin_user/mod.rs @@ -1,10 +1,10 @@ +pub mod admin_user_forgot_password_api_handler; pub mod admin_user_login_api_handler; +pub mod admin_user_reset_password_api_handler; pub mod admin_user_table_api_handler; -pub mod store_admin_user_api_handler; -pub mod update_admin_user_api_handler; +pub mod change_password_api_handler; pub mod fetch_admin_user_api_handler; pub mod logged_in_user_api_handler; -pub mod admin_user_forgot_password_api_handler; -pub mod admin_user_reset_password_api_handler; -pub mod change_password_api_handler; -pub mod request; \ No newline at end of file +pub mod request; +pub mod store_admin_user_api_handler; +pub mod update_admin_user_api_handler; diff --git a/src/api/handlers/admin_user/request/admin_user_forgot_password_request.rs b/src/api/handlers/admin_user/request/admin_user_forgot_password_request.rs index e8601ebf..037a55b8 100644 --- a/src/api/handlers/admin_user/request/admin_user_forgot_password_request.rs +++ b/src/api/handlers/admin_user/request/admin_user_forgot_password_request.rs @@ -1,8 +1,8 @@ -use rust_i18n::t; -use serde::Deserialize; use crate::error::Result; use crate::models::validation_error::ErrorMessage; use crate::models::validation_error::Validate; +use rust_i18n::t; +use serde::Deserialize; #[derive(Deserialize, Debug, Clone)] pub struct AdminUserForgotPasswordRequest { @@ -16,7 +16,7 @@ impl AdminUserForgotPasswordRequest { if !self.email.required()? { let error_message = ErrorMessage { key: String::from("email"), - message: t!("validation_required", attribute = t!("email")).to_string() + message: t!("validation_required", attribute = t!("email")).to_string(), }; errors.push(error_message); @@ -25,7 +25,7 @@ impl AdminUserForgotPasswordRequest { if !self.email.validate_email()? { let error_message = ErrorMessage { key: String::from("email"), - message: t!("email_address_not_valid").to_string() + message: t!("email_address_not_valid").to_string(), }; errors.push(error_message); diff --git a/src/api/handlers/admin_user/request/admin_user_reset_password_request.rs b/src/api/handlers/admin_user/request/admin_user_reset_password_request.rs index 2519b5ba..c1a96de9 100644 --- a/src/api/handlers/admin_user/request/admin_user_reset_password_request.rs +++ b/src/api/handlers/admin_user/request/admin_user_reset_password_request.rs @@ -1,15 +1,15 @@ -use rust_i18n::t; -use serde::Deserialize; use crate::avored_state::AvoRedState; use crate::models::password_rest_model::PasswordResetTokenStatus; use crate::models::validation_error::{ErrorMessage, Validate}; +use rust_i18n::t; +use serde::Deserialize; #[derive(Deserialize, Debug, Clone)] pub struct AdminUserResetPasswordRequest { pub email: String, pub password: String, pub confirm_password: String, - pub token: String + pub token: String, } impl AdminUserResetPasswordRequest { @@ -19,7 +19,7 @@ impl AdminUserResetPasswordRequest { if !self.email.required()? { let error_message = ErrorMessage { key: String::from("email"), - message: t!("validation_required", attribute = t!("email")).to_string() + message: t!("validation_required", attribute = t!("email")).to_string(), }; errors.push(error_message); @@ -28,7 +28,7 @@ impl AdminUserResetPasswordRequest { if !self.email.validate_email()? { let error_message = ErrorMessage { key: String::from("email"), - message: t!("email_address_not_valid").to_string() + message: t!("email_address_not_valid").to_string(), }; errors.push(error_message); @@ -36,7 +36,7 @@ impl AdminUserResetPasswordRequest { if !self.password.required()? { let error_message = ErrorMessage { key: String::from("password"), - message: t!("validation_required", attribute = t!("password")).to_string() + message: t!("validation_required", attribute = t!("password")).to_string(), }; errors.push(error_message); @@ -44,7 +44,7 @@ impl AdminUserResetPasswordRequest { if !self.confirm_password.required()? { let error_message = ErrorMessage { key: String::from("confirm_password"), - message: t!("validation_required", attribute = t!("confirm_password")).to_string() + message: t!("validation_required", attribute = t!("confirm_password")).to_string(), }; errors.push(error_message); @@ -53,7 +53,7 @@ impl AdminUserResetPasswordRequest { if self.password != self.confirm_password { let error_message = ErrorMessage { key: String::from("password"), - message: t!("password_did_not_match_confirmation_password").to_string() + message: t!("password_did_not_match_confirmation_password").to_string(), }; errors.push(error_message); @@ -62,7 +62,7 @@ impl AdminUserResetPasswordRequest { if !self.token.required()? { let error_message = ErrorMessage { key: String::from("token"), - message: t!("validation_required", attribute = t!("token")).to_string() + message: t!("validation_required", attribute = t!("token")).to_string(), }; errors.push(error_message); @@ -76,11 +76,11 @@ impl AdminUserResetPasswordRequest { if password_reset_model.status == PasswordResetTokenStatus::Expire { let error_message = ErrorMessage { key: String::from("token"), - message: t!("password_reset_token_expire").to_string() + message: t!("password_reset_token_expire").to_string(), }; errors.push(error_message); } Ok(errors) } -} \ No newline at end of file +} diff --git a/src/api/handlers/admin_user/request/authenticate_admin_user_request.rs b/src/api/handlers/admin_user/request/authenticate_admin_user_request.rs index 9114d19f..414ffc16 100644 --- a/src/api/handlers/admin_user/request/authenticate_admin_user_request.rs +++ b/src/api/handlers/admin_user/request/authenticate_admin_user_request.rs @@ -1,7 +1,7 @@ +use crate::models::validation_error::{ErrorMessage, Validate}; use rust_i18n::t; use serde::Deserialize; use utoipa::ToSchema; -use crate::models::validation_error::{ErrorMessage, Validate}; #[derive(Deserialize, Debug, Clone, ToSchema)] pub struct AuthenticateAdminUserRequest { @@ -16,7 +16,7 @@ impl AuthenticateAdminUserRequest { if !self.email.required()? { let error_message = ErrorMessage { key: String::from("email"), - message: t!("validation_required", attribute = t!("email")).to_string() + message: t!("validation_required", attribute = t!("email")).to_string(), }; errors.push(error_message); @@ -25,7 +25,7 @@ impl AuthenticateAdminUserRequest { if !self.email.validate_email()? { let error_message = ErrorMessage { key: String::from("email"), - message: t!("email_address_not_valid").to_string() + message: t!("email_address_not_valid").to_string(), }; errors.push(error_message); @@ -34,7 +34,7 @@ impl AuthenticateAdminUserRequest { if !self.password.required()? { let error_message = ErrorMessage { key: String::from("password"), - message: t!("validation_required", attribute = t!("password")).to_string() + message: t!("validation_required", attribute = t!("password")).to_string(), }; errors.push(error_message); @@ -42,4 +42,4 @@ impl AuthenticateAdminUserRequest { Ok(errors) } -} \ No newline at end of file +} diff --git a/src/api/handlers/admin_user/request/change_password_request.rs b/src/api/handlers/admin_user/request/change_password_request.rs index b894f809..3b3b490f 100644 --- a/src/api/handlers/admin_user/request/change_password_request.rs +++ b/src/api/handlers/admin_user/request/change_password_request.rs @@ -1,6 +1,6 @@ +use crate::models::validation_error::{ErrorMessage, Validate}; use rust_i18n::t; use serde::Deserialize; -use crate::models::validation_error::{ErrorMessage, Validate}; #[derive(Deserialize, Debug, Clone)] pub struct ChangePasswordRequest { @@ -16,7 +16,7 @@ impl ChangePasswordRequest { if !self.current_password.required()? { let error_message = ErrorMessage { key: String::from("current_password"), - message: t!("validation_required", attribute = t!("current_password")).to_string() + message: t!("validation_required", attribute = t!("current_password")).to_string(), }; errors.push(error_message); @@ -25,7 +25,7 @@ impl ChangePasswordRequest { if !self.password.required()? { let error_message = ErrorMessage { key: String::from("password"), - message: t!("validation_required", attribute = t!("password")).to_string() + message: t!("validation_required", attribute = t!("password")).to_string(), }; errors.push(error_message); @@ -34,7 +34,7 @@ impl ChangePasswordRequest { if !self.confirm_password.required()? { let error_message = ErrorMessage { key: String::from("confirm_password"), - message: t!("validation_required", attribute = t!("confirm_password")).to_string() + message: t!("validation_required", attribute = t!("confirm_password")).to_string(), }; errors.push(error_message); @@ -43,7 +43,7 @@ impl ChangePasswordRequest { if self.password != self.confirm_password { let error_message = ErrorMessage { key: String::from("confirm_password"), - message: t!("current_not_same_as_new_password").to_string() + message: t!("current_not_same_as_new_password").to_string(), }; errors.push(error_message); @@ -51,4 +51,4 @@ impl ChangePasswordRequest { Ok(errors) } -} \ No newline at end of file +} diff --git a/src/api/handlers/admin_user/request/mod.rs b/src/api/handlers/admin_user/request/mod.rs index 4dbf86e8..4d97c218 100644 --- a/src/api/handlers/admin_user/request/mod.rs +++ b/src/api/handlers/admin_user/request/mod.rs @@ -1,6 +1,6 @@ -pub mod update_admin_user_request; -pub mod authenticate_admin_user_request; -pub mod store_admin_user_request; pub mod admin_user_forgot_password_request; pub mod admin_user_reset_password_request; -pub mod change_password_request; \ No newline at end of file +pub mod authenticate_admin_user_request; +pub mod change_password_request; +pub mod store_admin_user_request; +pub mod update_admin_user_request; diff --git a/src/api/handlers/admin_user/request/store_admin_user_request.rs b/src/api/handlers/admin_user/request/store_admin_user_request.rs index adf9be35..2bd0f99d 100644 --- a/src/api/handlers/admin_user/request/store_admin_user_request.rs +++ b/src/api/handlers/admin_user/request/store_admin_user_request.rs @@ -1,7 +1,7 @@ -use rust_i18n::t; -use serde::Deserialize; use crate::avored_state::AvoRedState; use crate::models::validation_error::{ErrorMessage, Validate}; +use rust_i18n::t; +use serde::Deserialize; #[derive(Deserialize, Debug, Clone)] pub struct StoreAdminUserRequest { @@ -10,17 +10,16 @@ pub struct StoreAdminUserRequest { pub password: String, pub confirmation_password: String, pub is_super_admin: bool, - pub role_ids: Vec + pub role_ids: Vec, } impl StoreAdminUserRequest { pub async fn validate(&self, state: &AvoRedState) -> crate::error::Result> { - let mut errors: Vec = vec![]; if !self.full_name.required()? { let error_message = ErrorMessage { key: String::from("full_name"), - message: t!("validation_required", attribute = t!("full_name")).to_string() + message: t!("validation_required", attribute = t!("full_name")).to_string(), }; errors.push(error_message); @@ -29,16 +28,16 @@ impl StoreAdminUserRequest { if !self.email.required()? { let error_message = ErrorMessage { key: String::from("email"), - message: t!("validation_required", attribute = t!("email")).to_string() + message: t!("validation_required", attribute = t!("email")).to_string(), }; errors.push(error_message); } - if ! self.email.validate_email()? { + if !self.email.validate_email()? { let error_message = ErrorMessage { key: String::from("email"), - message: t!("email_address_not_valid").to_string() + message: t!("email_address_not_valid").to_string(), }; errors.push(error_message); @@ -47,7 +46,7 @@ impl StoreAdminUserRequest { if !self.password.required()? { let error_message = ErrorMessage { key: String::from("password"), - message: t!("validation_required", attribute = t!("password")).to_string() + message: t!("validation_required", attribute = t!("password")).to_string(), }; errors.push(error_message); @@ -56,7 +55,11 @@ impl StoreAdminUserRequest { if !self.confirmation_password.required()? { let error_message = ErrorMessage { key: String::from("confirmation_password"), - message: t!("validation_required", attribute = t!("confirmation_password")).to_string() + message: t!( + "validation_required", + attribute = t!("confirmation_password") + ) + .to_string(), }; errors.push(error_message); @@ -65,7 +68,7 @@ impl StoreAdminUserRequest { if self.password != self.confirmation_password { let error_message = ErrorMessage { key: String::from("password"), - message: t!("password_did_not_match_confirmation_password").to_string() + message: t!("password_did_not_match_confirmation_password").to_string(), }; errors.push(error_message); @@ -79,14 +82,12 @@ impl StoreAdminUserRequest { if admin_user_model.total > 0 { let error_message = ErrorMessage { key: String::from("email"), - message: t!("validation_count", attribute = t!("email")).to_string() + message: t!("validation_count", attribute = t!("email")).to_string(), }; errors.push(error_message); } - - Ok(errors) } -} \ No newline at end of file +} diff --git a/src/api/handlers/admin_user/request/update_admin_user_request.rs b/src/api/handlers/admin_user/request/update_admin_user_request.rs index e090e2d7..b1f088dc 100644 --- a/src/api/handlers/admin_user/request/update_admin_user_request.rs +++ b/src/api/handlers/admin_user/request/update_admin_user_request.rs @@ -1,12 +1,12 @@ +use crate::models::validation_error::{ErrorMessage, Validate}; use rust_i18n::t; use serde::Deserialize; -use crate::models::validation_error::{ErrorMessage, Validate}; #[derive(Deserialize, Debug, Clone)] pub struct UpdateAdminUserRequest { pub full_name: String, pub is_super_admin: bool, - pub role_ids: Vec + pub role_ids: Vec, } impl UpdateAdminUserRequest { @@ -15,11 +15,11 @@ impl UpdateAdminUserRequest { if !self.full_name.required()? { let error_message = ErrorMessage { key: String::from("full_name"), - message: t!("validation_required", attribute = t!("full_name")).to_string() + message: t!("validation_required", attribute = t!("full_name")).to_string(), }; errors.push(error_message); } Ok(errors) } -} \ No newline at end of file +} diff --git a/src/api/handlers/admin_user/store_admin_user_api_handler.rs b/src/api/handlers/admin_user/store_admin_user_api_handler.rs index 413e896a..c6d7759a 100644 --- a/src/api/handlers/admin_user/store_admin_user_api_handler.rs +++ b/src/api/handlers/admin_user/store_admin_user_api_handler.rs @@ -1,22 +1,22 @@ -use std::path::Path; -use std::sync::Arc; -use axum::extract::{Multipart, State}; -use axum::{Extension, Json}; -use rand::distributions::Alphanumeric; -use rand::Rng; -use serde::Serialize; -use urlencoding::decode_binary; use crate::api::handlers::admin_user::request::store_admin_user_request::StoreAdminUserRequest; use crate::avored_state::AvoRedState; use crate::error::{Error, Result}; use crate::models::admin_user_model::{AdminUserModel, CreatableAdminUserModel}; use crate::models::token_claim_model::LoggedInUser; use crate::models::validation_error::ErrorResponse; +use axum::extract::{Multipart, State}; +use axum::{Extension, Json}; +use rand::distributions::Alphanumeric; +use rand::Rng; +use serde::Serialize; +use std::path::Path; +use std::sync::Arc; +use urlencoding::decode_binary; pub async fn store_admin_user_api_handler( Extension(logged_in_user): Extension, state: State>, - mut multipart: Multipart + mut multipart: Multipart, ) -> Result> { println!("->> {:<12} - store_admin_user_api_handler", "HANDLER"); let has_permission_bool = state @@ -33,7 +33,7 @@ pub async fn store_admin_user_api_handler( password: String::from(""), is_super_admin: false, confirmation_password: String::from(""), - role_ids: vec![] + role_ids: vec![], }; let mut profile_image = String::from(""); @@ -102,7 +102,7 @@ pub async fn store_admin_user_api_handler( let confirmation_password = String::from_utf8_lossy(&decoded).into_owned(); payload.confirmation_password = confirmation_password; - }, + } "role_ids[]" => { let bytes = field.bytes().await.unwrap(); let decoded = decode_binary(&bytes).into_owned(); @@ -119,16 +119,14 @@ pub async fn store_admin_user_api_handler( if !error_messages.is_empty() { let error_response = ErrorResponse { status: false, - errors: error_messages + errors: error_messages, }; return Err(Error::BadRequest(error_response)); } let password_hash = state .admin_user_service - .get_password_hash_from_raw_password( - payload.password, &state.config.password_salt - )?; + .get_password_hash_from_raw_password(payload.password, &state.config.password_salt)?; let creatable_admin_user = CreatableAdminUserModel { full_name: payload.full_name, @@ -137,7 +135,7 @@ pub async fn store_admin_user_api_handler( profile_image, is_super_admin: payload.is_super_admin, logged_in_username: logged_in_user.email.clone(), - role_ids: payload.role_ids + role_ids: payload.role_ids, }; let created_admin_user = state @@ -147,16 +145,14 @@ pub async fn store_admin_user_api_handler( let create_admin_user_response = CreateAdminUserResponse { status: true, - admin_user_model: created_admin_user + admin_user_model: created_admin_user, }; Ok(Json(create_admin_user_response)) } - #[derive(Serialize, Debug)] pub struct CreateAdminUserResponse { pub status: bool, - pub admin_user_model: AdminUserModel + pub admin_user_model: AdminUserModel, } - diff --git a/src/api/handlers/admin_user/update_admin_user_api_handler.rs b/src/api/handlers/admin_user/update_admin_user_api_handler.rs index ff28912a..e71e18c6 100644 --- a/src/api/handlers/admin_user/update_admin_user_api_handler.rs +++ b/src/api/handlers/admin_user/update_admin_user_api_handler.rs @@ -1,20 +1,21 @@ +use crate::{avored_state::AvoRedState, error::Result}; use std::path::Path; use std::sync::Arc; -use crate::{ - avored_state::AvoRedState, error::Result -}; -use axum::{Extension, extract::{Path as AxumPath, State}, Json}; -use axum::extract::Multipart; -use rand::distributions::Alphanumeric; -use rand::Rng; -use serde::Serialize; -use urlencoding::decode_binary; use crate::api::handlers::admin_user::request::update_admin_user_request::UpdateAdminUserRequest; use crate::error::Error; use crate::models::admin_user_model::{AdminUserModel, UpdatableAdminUserModel}; use crate::models::token_claim_model::LoggedInUser; use crate::models::validation_error::ErrorResponse; +use axum::extract::Multipart; +use axum::{ + extract::{Path as AxumPath, State}, + Extension, Json, +}; +use rand::distributions::Alphanumeric; +use rand::Rng; +use serde::Serialize; +use urlencoding::decode_binary; pub async fn update_admin_user_api_handler( Extension(logged_in_user): Extension, @@ -34,7 +35,7 @@ pub async fn update_admin_user_api_handler( let mut payload = UpdateAdminUserRequest { full_name: String::from(""), is_super_admin: false, - role_ids: vec![] + role_ids: vec![], }; let mut profile_image = String::from(""); @@ -82,7 +83,7 @@ pub async fn update_admin_user_api_handler( } payload.is_super_admin = bool_super_admin; - }, + } "role_ids[]" => { let bytes = field.bytes().await.unwrap(); let decoded = decode_binary(&bytes).into_owned(); @@ -98,7 +99,7 @@ pub async fn update_admin_user_api_handler( if !error_messages.is_empty() { let error_response = ErrorResponse { status: false, - errors: error_messages + errors: error_messages, }; return Err(Error::BadRequest(error_response)); } @@ -109,7 +110,7 @@ pub async fn update_admin_user_api_handler( profile_image, is_super_admin: payload.is_super_admin, logged_in_username: logged_in_user.email.clone(), - role_ids: payload.role_ids + role_ids: payload.role_ids, }; let updated_admin_user_model = state .admin_user_service @@ -117,15 +118,14 @@ pub async fn update_admin_user_api_handler( .await?; let response = UpdatableAdminUserResponse { status: true, - admin_user_model: updated_admin_user_model + admin_user_model: updated_admin_user_model, }; Ok(Json(response)) } - #[derive(Serialize, Debug)] pub struct UpdatableAdminUserResponse { pub status: bool, - pub admin_user_model: AdminUserModel -} \ No newline at end of file + pub admin_user_model: AdminUserModel, +} diff --git a/src/api/handlers/asset/asset_table_api_handler.rs b/src/api/handlers/asset/asset_table_api_handler.rs index 2f2ff43c..f815d7e7 100644 --- a/src/api/handlers/asset/asset_table_api_handler.rs +++ b/src/api/handlers/asset/asset_table_api_handler.rs @@ -1,11 +1,11 @@ -use std::sync::Arc; -use axum::extract::{Query, State}; -use axum::{Extension, Json}; use crate::api::handlers::page::request::page_table_request::PageTableRequest; use crate::avored_state::AvoRedState; use crate::error::{Error, Result}; use crate::models::asset_model::AssetPagination; use crate::models::token_claim_model::LoggedInUser; +use axum::extract::{Query, State}; +use axum::{Extension, Json}; +use std::sync::Arc; pub async fn asset_table_api_handler( state: State>, @@ -24,7 +24,10 @@ pub async fn asset_table_api_handler( let parent_id = query_param.parent_id.unwrap_or_default(); let current_page = query_param.page.unwrap_or(1); - let asset_pagination = state.asset_service.paginate(&state.db, current_page, parent_id).await?; + let asset_pagination = state + .asset_service + .paginate(&state.db, current_page, parent_id) + .await?; Ok(Json(asset_pagination)) } diff --git a/src/api/handlers/asset/create_folder_api_handler.rs b/src/api/handlers/asset/create_folder_api_handler.rs index 118b9266..f23403d7 100644 --- a/src/api/handlers/asset/create_folder_api_handler.rs +++ b/src/api/handlers/asset/create_folder_api_handler.rs @@ -1,14 +1,12 @@ -use std::sync::Arc; -use crate::{ - avored_state::AvoRedState, error::Result -}; -use axum::{Extension, extract::State, Json}; use crate::api::handlers::asset::request::create_folder_request::CreateFolderRequest; use crate::error::Error; use crate::models::asset_model::AssetModel; use crate::models::token_claim_model::LoggedInUser; use crate::models::validation_error::ErrorResponse; use crate::responses::ApiResponse; +use crate::{avored_state::AvoRedState, error::Result}; +use axum::{extract::State, Extension, Json}; +use std::sync::Arc; pub async fn create_folder_api_handler( Extension(logged_in_user): Extension, @@ -30,7 +28,7 @@ pub async fn create_folder_api_handler( if !error_messages.is_empty() { let error_response = ErrorResponse { status: false, - errors: error_messages + errors: error_messages, }; return Err(Error::BadRequest(error_response)); @@ -45,7 +43,7 @@ pub async fn create_folder_api_handler( let created_response = ApiResponse { status: true, - data: created_asset_folder + data: created_asset_folder, }; Ok(Json(created_response)) diff --git a/src/api/handlers/asset/delete_asset_api_handler.rs b/src/api/handlers/asset/delete_asset_api_handler.rs index 6779b248..8ef553d0 100644 --- a/src/api/handlers/asset/delete_asset_api_handler.rs +++ b/src/api/handlers/asset/delete_asset_api_handler.rs @@ -1,19 +1,17 @@ -use std::sync::Arc; -use crate::{ - avored_state::AvoRedState, error::Result -}; -use axum::{Extension, extract::State}; +use crate::error::Error; +use crate::models::token_claim_model::LoggedInUser; +use crate::{avored_state::AvoRedState, error::Result}; use axum::extract::Path; use axum::http::StatusCode; use axum::response::IntoResponse; +use axum::{extract::State, Extension}; +use std::sync::Arc; use tokio::fs; -use crate::error::Error; -use crate::models::token_claim_model::LoggedInUser; pub async fn delete_asset_api_handler( Path(asset_id): Path, Extension(logged_in_user): Extension, - state: State> + state: State>, ) -> Result { println!("->> {:<12} - delete_asset_api_handler", "HANDLER"); @@ -25,9 +23,7 @@ pub async fn delete_asset_api_handler( return Err(Error::Forbidden); } - let asset_model = state.asset_service - .find_by_id(&state.db, &asset_id) - .await?; + let asset_model = state.asset_service.find_by_id(&state.db, &asset_id).await?; let asset_path = format!("./{path}", path = asset_model.new_path); @@ -40,11 +36,11 @@ pub async fn delete_asset_api_handler( .delete_by_id(&state.db, &asset_id) .await?; if !result { - return Err(Error::Generic(String::from("there is an issue while deleting an asset record in DB"))); + return Err(Error::Generic(String::from( + "there is an issue while deleting an asset record in DB", + ))); } } - - Ok(StatusCode::OK) } diff --git a/src/api/handlers/asset/delete_folder_api_handler.rs b/src/api/handlers/asset/delete_folder_api_handler.rs index fec0f34f..e808bd56 100644 --- a/src/api/handlers/asset/delete_folder_api_handler.rs +++ b/src/api/handlers/asset/delete_folder_api_handler.rs @@ -1,20 +1,18 @@ -use std::sync::Arc; -use crate::{ - avored_state::AvoRedState, error::Result -}; -use axum::{Extension, extract::State}; +use crate::error::Error; +use crate::models::token_claim_model::LoggedInUser; +use crate::models::validation_error::{ErrorMessage, ErrorResponse}; +use crate::{avored_state::AvoRedState, error::Result}; use axum::extract::Path; use axum::http::StatusCode; use axum::response::IntoResponse; +use axum::{extract::State, Extension}; +use std::sync::Arc; use tokio::fs; -use crate::error::Error; -use crate::models::token_claim_model::LoggedInUser; -use crate::models::validation_error::{ErrorMessage, ErrorResponse}; pub async fn delete_folder_api_handler( Path(asset_id): Path, Extension(logged_in_user): Extension, - state: State> + state: State>, ) -> Result { println!("->> {:<12} - delete_folder_api_handler", "HANDLER"); @@ -26,9 +24,7 @@ pub async fn delete_folder_api_handler( return Err(Error::Forbidden); } - let asset_model = state.asset_service - .find_by_id(&state.db, &asset_id) - .await?; + let asset_model = state.asset_service.find_by_id(&state.db, &asset_id).await?; let folder_path = format!("./{path}", path = asset_model.new_path); @@ -44,11 +40,13 @@ pub async fn delete_folder_api_handler( if counter > 0 { let error_messages = vec![ErrorMessage { key: String::from("folder_existed"), - message: String::from("folder is not empty. Please make sure folder is empty before deleting it.") + message: String::from( + "folder is not empty. Please make sure folder is empty before deleting it.", + ), }]; let error_response = ErrorResponse { status: false, - errors: error_messages + errors: error_messages, }; return Err(Error::BadRequest(error_response)); @@ -61,7 +59,9 @@ pub async fn delete_folder_api_handler( .delete_by_id(&state.db, &asset_id) .await?; if !result { - return Err(Error::Generic(String::from("there is an issue while deleting an folder"))); + return Err(Error::Generic(String::from( + "there is an issue while deleting an folder", + ))); } Ok(StatusCode::OK) diff --git a/src/api/handlers/asset/mod.rs b/src/api/handlers/asset/mod.rs index 21dea927..c4b47a04 100644 --- a/src/api/handlers/asset/mod.rs +++ b/src/api/handlers/asset/mod.rs @@ -1,7 +1,7 @@ pub mod asset_table_api_handler; -pub mod store_asset_api_handler; pub mod create_folder_api_handler; +pub mod delete_asset_api_handler; pub mod delete_folder_api_handler; pub mod rename_asset_api_handler; -pub mod delete_asset_api_handler; -pub mod request; \ No newline at end of file +pub mod request; +pub mod store_asset_api_handler; diff --git a/src/api/handlers/asset/rename_asset_api_handler.rs b/src/api/handlers/asset/rename_asset_api_handler.rs index 3f7064a5..3b0cc5cb 100644 --- a/src/api/handlers/asset/rename_asset_api_handler.rs +++ b/src/api/handlers/asset/rename_asset_api_handler.rs @@ -1,7 +1,3 @@ -use std::sync::Arc; -use axum::extract::{Path, State}; -use axum::{Extension, Json}; -use tokio::fs; use crate::api::handlers::asset::request::rename_asset_request::RenameAssetRequest; use crate::avored_state::AvoRedState; use crate::error::{Error, Result}; @@ -9,6 +5,10 @@ use crate::models::asset_model::AssetModel; use crate::models::token_claim_model::LoggedInUser; use crate::models::validation_error::ErrorResponse; use crate::responses::ApiResponse; +use axum::extract::{Path, State}; +use axum::{Extension, Json}; +use std::sync::Arc; +use tokio::fs; pub async fn rename_asset_api_handler( Path(asset_id): Path, @@ -31,29 +31,28 @@ pub async fn rename_asset_api_handler( if !error_messages.is_empty() { let error_response = ErrorResponse { status: false, - errors: error_messages + errors: error_messages, }; return Err(Error::BadRequest(error_response)); } - let asset_model = state.asset_service - .find_by_id(&state.db, &asset_id) - .await?; - let old_asset_path = format!(".{}",asset_model.new_path); + let asset_model = state.asset_service.find_by_id(&state.db, &asset_id).await?; + let old_asset_path = format!(".{}", asset_model.new_path); let new_asset_path = format!("/public/upload/{}", &payload.name); if fs::try_exists(&old_asset_path).await? { fs::rename(&old_asset_path, &format!(".{}", new_asset_path)).await?; } - let updated_asset_model = state.asset_service + let updated_asset_model = state + .asset_service .update_asset_path(&state.db, &payload.name, &asset_id, &logged_in_user.email) .await?; let response = ApiResponse { status: false, - data: updated_asset_model + data: updated_asset_model, }; Ok(Json(response)) diff --git a/src/api/handlers/asset/request/create_folder_request.rs b/src/api/handlers/asset/request/create_folder_request.rs index 880354db..80200e40 100644 --- a/src/api/handlers/asset/request/create_folder_request.rs +++ b/src/api/handlers/asset/request/create_folder_request.rs @@ -1,11 +1,11 @@ -use serde::Deserialize; -use rust_i18n::t; use crate::models::validation_error::{ErrorMessage, Validate}; +use rust_i18n::t; +use serde::Deserialize; #[derive(Deserialize, Debug, Clone)] pub struct CreateFolderRequest { pub name: String, - pub parent_id: Option + pub parent_id: Option, } impl CreateFolderRequest { @@ -13,10 +13,9 @@ impl CreateFolderRequest { let mut errors: Vec = vec![]; if !self.name.required()? { - let error_message = ErrorMessage { key: String::from("name"), - message: t!("validation_required", attribute = t!("name")).to_string() + message: t!("validation_required", attribute = t!("name")).to_string(), }; errors.push(error_message); diff --git a/src/api/handlers/asset/request/mod.rs b/src/api/handlers/asset/request/mod.rs index 89476a15..ba6f80c3 100644 --- a/src/api/handlers/asset/request/mod.rs +++ b/src/api/handlers/asset/request/mod.rs @@ -1,3 +1,3 @@ pub mod create_folder_request; pub mod rename_asset_request; -pub mod store_asset_request; \ No newline at end of file +pub mod store_asset_request; diff --git a/src/api/handlers/asset/request/rename_asset_request.rs b/src/api/handlers/asset/request/rename_asset_request.rs index fce696b4..e526d547 100644 --- a/src/api/handlers/asset/request/rename_asset_request.rs +++ b/src/api/handlers/asset/request/rename_asset_request.rs @@ -1,6 +1,6 @@ -use serde::Deserialize; -use rust_i18n::t; use crate::models::validation_error::{ErrorMessage, Validate}; +use rust_i18n::t; +use serde::Deserialize; #[derive(Deserialize, Debug, Clone)] pub struct RenameAssetRequest { @@ -12,10 +12,9 @@ impl RenameAssetRequest { let mut errors: Vec = vec![]; if !self.name.required()? { - let error_message = ErrorMessage { key: String::from("name"), - message: t!("validation_required", attribute = t!("name")).to_string() + message: t!("validation_required", attribute = t!("name")).to_string(), }; errors.push(error_message); diff --git a/src/api/handlers/asset/request/store_asset_request.rs b/src/api/handlers/asset/request/store_asset_request.rs index 2172e46e..5ab8f8ee 100644 --- a/src/api/handlers/asset/request/store_asset_request.rs +++ b/src/api/handlers/asset/request/store_asset_request.rs @@ -2,5 +2,5 @@ use serde::Deserialize; #[derive(Deserialize, Debug, Clone)] pub struct StoreAssetRequest { - pub parent_id: Option -} \ No newline at end of file + pub parent_id: Option, +} diff --git a/src/api/handlers/asset/store_asset_api_handler.rs b/src/api/handlers/asset/store_asset_api_handler.rs index c5af63c5..4cb99a5e 100644 --- a/src/api/handlers/asset/store_asset_api_handler.rs +++ b/src/api/handlers/asset/store_asset_api_handler.rs @@ -1,18 +1,15 @@ use std::sync::Arc; -use crate::{ - avored_state::AvoRedState, - error::Result -}; -use axum::{Extension, extract::State, Json, response::IntoResponse}; +use crate::api::handlers::asset::request::store_asset_request::StoreAssetRequest; +use crate::error::Error; +use crate::models::asset_model::{AssetModel, CreatableAssetModel, MetaDataType}; +use crate::models::token_claim_model::LoggedInUser; +use crate::{avored_state::AvoRedState, error::Result}; use axum::extract::{Multipart, Query}; +use axum::{extract::State, response::IntoResponse, Extension, Json}; use rand::distributions::Alphanumeric; use rand::Rng; use serde::Serialize; -use crate::api::handlers::asset::request::store_asset_request::StoreAssetRequest; -use crate::error::Error; -use crate::models::asset_model::{CreatableAssetModel, MetaDataType, AssetModel}; -use crate::models::token_claim_model::LoggedInUser; const ALLOW_TYPES: [&str; 3] = ["image/jpeg", "image/jpg", "image/png"]; @@ -36,7 +33,7 @@ pub async fn store_asset_api_handler( // we need to make the parent_id works as per it path will be changed too. if exist let mut creatable_asset_model = CreatableAssetModel { logged_in_username: logged_in_user.email, - .. Default::default() + ..Default::default() }; let mut is_allow_file_type = true; @@ -78,22 +75,22 @@ pub async fn store_asset_api_handler( .await?; creatable_asset_model.parent_id = query_parent_id; - asset_file = format!("/{}/{}", parent_asset.new_path, new_file_name.clone()); + asset_file = + format!("/{}/{}", parent_asset.new_path, new_file_name.clone()); } else { asset_file = format!("/public/upload/{}", new_file_name.clone()); } creatable_asset_model.name = new_file_name.clone(); creatable_asset_model.asset_type = String::from("FILE"); - creatable_asset_model.metadata = MetaDataType::FileTypeMetaData {file_type}; + creatable_asset_model.metadata = MetaDataType::FileTypeMetaData { file_type }; let full_path = format!("./{}", asset_file); - tokio::fs::write(full_path, data).await?; } - }, - &_ => continue + } + &_ => continue, } } @@ -101,19 +98,20 @@ pub async fn store_asset_api_handler( let asset_model = AssetModel::default(); let creatable_asset_response = AssetResponseViewModel { asset_model, - success: false + success: false, }; - return Ok(Json(creatable_asset_response).into_response()) + return Ok(Json(creatable_asset_response).into_response()); } - let asset_model = state.asset_service + let asset_model = state + .asset_service .create_asset(&state.db, creatable_asset_model) .await?; let creatable_asset_response = AssetResponseViewModel { asset_model, - success: true + success: true, }; Ok(Json(creatable_asset_response).into_response()) @@ -122,5 +120,5 @@ pub async fn store_asset_api_handler( #[derive(Serialize)] pub struct AssetResponseViewModel { pub asset_model: AssetModel, - pub success: bool + pub success: bool, } diff --git a/src/api/handlers/cms/all_pages_cms_api_handler.rs b/src/api/handlers/cms/all_pages_cms_api_handler.rs index 01c753a5..c1cb79bb 100644 --- a/src/api/handlers/cms/all_pages_cms_api_handler.rs +++ b/src/api/handlers/cms/all_pages_cms_api_handler.rs @@ -1,17 +1,12 @@ +use crate::{avored_state::AvoRedState, error::Result}; +use axum::{extract::State, response::IntoResponse, Json}; use std::sync::Arc; -use crate::{ - avored_state::AvoRedState, error::Result -}; -use axum::{extract::State, Json, response::IntoResponse}; pub async fn all_pages_cms_api_handler( - state: State> + state: State>, ) -> Result { println!("->> {:<12} - all_pages_cms_api_handler", "HANDLER"); - let page_model = state - .page_service - .all(&state.db) - .await?; + let page_model = state.page_service.all(&state.db).await?; Ok(Json(page_model)) } diff --git a/src/api/handlers/cms/fetch_page_cms_api_handler.rs b/src/api/handlers/cms/fetch_page_cms_api_handler.rs index f70c3674..4af75e9f 100644 --- a/src/api/handlers/cms/fetch_page_cms_api_handler.rs +++ b/src/api/handlers/cms/fetch_page_cms_api_handler.rs @@ -1,18 +1,17 @@ -use std::sync::Arc; -use crate::{ - avored_state::AvoRedState, error::Result +use crate::{avored_state::AvoRedState, error::Result}; +use axum::{ + extract::{Path as AxumPath, State}, + response::IntoResponse, + Json, }; -use axum::{extract::{Path as AxumPath, State}, Json, response::IntoResponse}; +use std::sync::Arc; pub async fn fetch_page_cms_api_handler( AxumPath(page_id): AxumPath, - state: State> + state: State>, ) -> Result { println!("->> {:<12} - fetch_page_cms_api_handler", "HANDLER"); - let page_model = state - .page_service - .find_by_id(&state.db, page_id) - .await?; + let page_model = state.page_service.find_by_id(&state.db, page_id).await?; let res = page_model.convert_to_response()?; diff --git a/src/api/handlers/cms/mod.rs b/src/api/handlers/cms/mod.rs index e8b38998..d482836a 100644 --- a/src/api/handlers/cms/mod.rs +++ b/src/api/handlers/cms/mod.rs @@ -1,3 +1,3 @@ -pub mod fetch_page_cms_api_handler; pub mod all_pages_cms_api_handler; -pub mod sent_contact_us_email_handler; \ No newline at end of file +pub mod fetch_page_cms_api_handler; +pub mod sent_contact_us_email_handler; diff --git a/src/api/handlers/cms/sent_contact_us_email_handler.rs b/src/api/handlers/cms/sent_contact_us_email_handler.rs index 52467da7..a13c4d3e 100644 --- a/src/api/handlers/cms/sent_contact_us_email_handler.rs +++ b/src/api/handlers/cms/sent_contact_us_email_handler.rs @@ -1,12 +1,10 @@ -use std::sync::Arc; -use crate::{ - avored_state::AvoRedState, error::Result -}; -use axum::{extract::State, Json, response::IntoResponse}; -use email_address::EmailAddress; -use serde::{Deserialize, Serialize}; use crate::error::Error; use crate::models::validation_error::{ErrorMessage, ErrorResponse}; +use crate::{avored_state::AvoRedState, error::Result}; +use axum::{extract::State, response::IntoResponse, Json}; +use email_address::EmailAddress; +use serde::{Deserialize, Serialize}; +use std::sync::Arc; pub async fn sent_contact_us_email_handler( state: State>, @@ -18,7 +16,7 @@ pub async fn sent_contact_us_email_handler( if !error_messages.is_empty() { let error_response = ErrorResponse { status: false, - errors: error_messages + errors: error_messages, }; return Err(Error::BadRequest(error_response)); @@ -31,22 +29,19 @@ pub async fn sent_contact_us_email_handler( .await?; let res = SentContactUsEmailResponse { - status: contact_email_status + status: contact_email_status, }; // let res = page_model.convert_to_response()?; Ok(Json(res)) } - #[derive(Serialize, Debug)] #[cfg_attr(test, derive(Deserialize, Eq, PartialEq, Copy, Clone, Default))] pub struct SentContactUsEmailResponse { pub status: bool, } - - #[derive(Deserialize, Debug, Clone, Serialize)] pub struct SentContactUsEmailRequest { pub email: String, @@ -63,16 +58,16 @@ impl SentContactUsEmailRequest { if self.email.is_empty() { let error_message = ErrorMessage { key: String::from("email"), - message: String::from("Email is a required field") + message: String::from("Email is a required field"), }; errors.push(error_message); } - if ! EmailAddress::is_valid(&self.email) { + if !EmailAddress::is_valid(&self.email) { let error_message = ErrorMessage { key: String::from("email"), - message: String::from("Invalid email address") + message: String::from("Invalid email address"), }; errors.push(error_message); diff --git a/src/api/handlers/collection/collection_all_api_handler.rs b/src/api/handlers/collection/collection_all_api_handler.rs new file mode 100644 index 00000000..1fc1639f --- /dev/null +++ b/src/api/handlers/collection/collection_all_api_handler.rs @@ -0,0 +1,36 @@ +use crate::avored_state::AvoRedState; +use crate::error::{Error, Result}; +use crate::models::token_claim_model::LoggedInUser; +use axum::extract::State; +use axum::{Extension, Json}; +use std::sync::Arc; +use crate::models::collection_model::CollectionModel; +use crate::responses::ApiResponse; + +pub async fn collection_all_api_handler( + state: State>, + Extension(logged_in_user): Extension +) -> Result>>> { + println!("->> {:<12} - collection_all_api_handler", "HANDLER"); + + let has_permission_bool = state + .admin_user_service + .has_permission(logged_in_user, String::from("collection_all")) + .await?; + if !has_permission_bool { + return Err(Error::Forbidden); + } + + + let collections = state + .collection_service + .all_collections(&state.db) + .await?; + + let response = ApiResponse { + status: true, + data: collections, + }; + + Ok(Json(response)) +} diff --git a/src/api/handlers/collection/collection_table_api_handler.rs b/src/api/handlers/collection/collection_table_api_handler.rs new file mode 100644 index 00000000..2863dd51 --- /dev/null +++ b/src/api/handlers/collection/collection_table_api_handler.rs @@ -0,0 +1,33 @@ +use crate::api::handlers::collection::request::collection_table_request::CollectionTableRequest; +use crate::avored_state::AvoRedState; +use crate::error::{Error, Result}; +use crate::models::collection_model::CollectionPagination; +use crate::models::token_claim_model::LoggedInUser; +use axum::extract::{Query, State}; +use axum::{Extension, Json}; +use std::sync::Arc; + +pub async fn collection_table_api_handler( + state: State>, + Extension(logged_in_user): Extension, + Query(query_param): Query, +) -> Result> { + println!("->> {:<12} - collection_table_api_handler", "HANDLER"); + + let has_permission_bool = state + .admin_user_service + .has_permission(logged_in_user, String::from("collection_table")) + .await?; + if !has_permission_bool { + return Err(Error::Forbidden); + } + + let current_page = query_param.page.unwrap_or(0); + let order = query_param.order.unwrap_or(String::from("")); + let paginated_data = state + .collection_service + .paginate(&state.db, current_page, order) + .await?; + + Ok(Json(paginated_data)) +} diff --git a/src/api/handlers/collection/fetch_collection_api_handler.rs b/src/api/handlers/collection/fetch_collection_api_handler.rs new file mode 100644 index 00000000..e492264c --- /dev/null +++ b/src/api/handlers/collection/fetch_collection_api_handler.rs @@ -0,0 +1,36 @@ +use crate::avored_state::AvoRedState; +use crate::error::{Error, Result}; +use crate::models::collection_model::CollectionModel; +use crate::models::token_claim_model::LoggedInUser; +use crate::responses::ApiResponse; +use axum::extract::{Path, State}; +use axum::{Extension, Json}; +use std::sync::Arc; + +pub async fn fetch_collection_api_handler( + state: State>, + Path(collection_id): Path, + Extension(logged_in_user): Extension, +) -> Result>> { + println!("->> {:<12} - fetch_model_api_handler", "HANDLER"); + + let has_permission_bool = state + .admin_user_service + .has_permission(logged_in_user, String::from("get_model")) + .await?; + if !has_permission_bool { + return Err(Error::Forbidden); + } + + let model_model = state + .collection_service + .find_by_id(&state.db, collection_id) + .await?; + + let response = ApiResponse { + status: true, + data: model_model, + }; + + Ok(Json(response)) +} diff --git a/src/api/handlers/collection/mod.rs b/src/api/handlers/collection/mod.rs new file mode 100644 index 00000000..859a0d9a --- /dev/null +++ b/src/api/handlers/collection/mod.rs @@ -0,0 +1,7 @@ +pub mod collection_table_api_handler; +pub mod fetch_collection_api_handler; +pub mod request; +pub mod store_collection_api_handler; +pub mod update_collection_api_handler; +pub mod put_collection_identifier_api_handler; +pub mod collection_all_api_handler; diff --git a/src/api/handlers/collection/put_collection_identifier_api_handler.rs b/src/api/handlers/collection/put_collection_identifier_api_handler.rs new file mode 100644 index 00000000..028c3079 --- /dev/null +++ b/src/api/handlers/collection/put_collection_identifier_api_handler.rs @@ -0,0 +1,56 @@ +use std::sync::Arc; +use axum::{Extension, Json}; +use axum::extract::{Path, State}; +use crate::api::handlers::collection::request::put_collection_identifier_request::PutCollectionRequest; +use crate::avored_state::AvoRedState; +use crate::error::{Error, Result}; +use crate::models::collection_model::{CollectionModel, PutCollectionIdentifierModel}; +use crate::models::token_claim_model::LoggedInUser; +use crate::models::validation_error::ErrorResponse; +use crate::responses::ApiResponse; + +pub async fn put_collection_identifier_api_handler( + Path(collection_id): Path, + Extension(logged_in_user): Extension, + state: State>, + Json(payload): Json, +) -> Result>> { + println!("->> {:<12} - put_collection_identifier_api_handler", "HANDLER"); + + let has_permission_bool = state + .admin_user_service + .has_permission(logged_in_user.clone(), String::from("collection_edit")) + .await?; + if !has_permission_bool { + return Err(Error::Forbidden); + } + + let error_messages = payload.validate(state.clone()).await?; + + if !error_messages.is_empty() { + let error_response = ErrorResponse { + status: false, + errors: error_messages, + }; + + return Err(Error::BadRequest(error_response)); + } + + let put_collection_identifier = PutCollectionIdentifierModel { + id: collection_id, + identifier: payload.identifier, + logged_in_username: logged_in_user.email, + }; + let updated_collection = state + .collection_service + .update_collection_identifier(&state.db, put_collection_identifier) + .await?; + + + let api_response = ApiResponse { + status: true, + data: updated_collection, + }; + + Ok(Json(api_response)) +} \ No newline at end of file diff --git a/src/api/handlers/collection/request/collection_table_request.rs b/src/api/handlers/collection/request/collection_table_request.rs new file mode 100644 index 00000000..f5c36731 --- /dev/null +++ b/src/api/handlers/collection/request/collection_table_request.rs @@ -0,0 +1,7 @@ +use serde::Deserialize; + +#[derive(Deserialize, Debug)] +pub struct CollectionTableRequest { + pub page: Option, + pub order: Option, +} diff --git a/src/api/handlers/collection/request/mod.rs b/src/api/handlers/collection/request/mod.rs new file mode 100644 index 00000000..161c8d47 --- /dev/null +++ b/src/api/handlers/collection/request/mod.rs @@ -0,0 +1,4 @@ +pub mod collection_table_request; +pub mod store_collection_request; +pub mod update_collection_request; +pub mod put_collection_identifier_request; diff --git a/src/api/handlers/collection/request/put_collection_identifier_request.rs b/src/api/handlers/collection/request/put_collection_identifier_request.rs new file mode 100644 index 00000000..88450c3a --- /dev/null +++ b/src/api/handlers/collection/request/put_collection_identifier_request.rs @@ -0,0 +1,44 @@ +use crate::avored_state::AvoRedState; +use crate::models::validation_error::{ErrorMessage, Validate}; +use axum::extract::State; +use rust_i18n::t; +use serde::Deserialize; +use std::sync::Arc; + +#[derive(Deserialize, Debug, Clone, Default)] +pub struct PutCollectionRequest { + pub identifier: String, +} + +impl PutCollectionRequest { + pub async fn validate( + &self, + state: State>, + ) -> crate::error::Result> { + let mut errors: Vec = vec![]; + + if !self.identifier.required()? { + let error_message = ErrorMessage { + key: String::from("identifier"), + message: t!("validation_required", attribute = t!("identifier")).to_string(), + }; + + errors.push(error_message); + } + + let collection_count = state + .collection_service + .count_of_identifier(&state.db, self.identifier.clone()) + .await?; + + if collection_count.total > 0 { + let error_message = ErrorMessage { + key: String::from("identifier"), + message: t!("validation_count", attribute = t!("identifier")).to_string(), + }; + errors.push(error_message); + } + + Ok(errors) + } +} diff --git a/src/api/handlers/collection/request/store_collection_request.rs b/src/api/handlers/collection/request/store_collection_request.rs new file mode 100644 index 00000000..29fe57de --- /dev/null +++ b/src/api/handlers/collection/request/store_collection_request.rs @@ -0,0 +1,35 @@ +use crate::models::validation_error::{ErrorMessage, Validate}; +use rust_i18n::t; +use serde::Deserialize; + +#[derive(Deserialize, Debug, Clone, Default)] +pub struct StoreCollectionRequest { + pub name: String, + pub identifier: String, +} + +impl StoreCollectionRequest { + pub fn validate(&self) -> crate::error::Result> { + let mut errors: Vec = vec![]; + + if !self.name.required()? { + let error_message = ErrorMessage { + key: String::from("name"), + message: t!("validation_required", attribute = t!("name")).to_string(), + }; + + errors.push(error_message); + } + + if !self.identifier.required()? { + let error_message = ErrorMessage { + key: String::from("identifier"), + message: format!("Identifier is a required field {}", t!("identifier")), + }; + + errors.push(error_message); + } + + Ok(errors) + } +} diff --git a/src/api/handlers/collection/request/update_collection_request.rs b/src/api/handlers/collection/request/update_collection_request.rs new file mode 100644 index 00000000..97adb471 --- /dev/null +++ b/src/api/handlers/collection/request/update_collection_request.rs @@ -0,0 +1,26 @@ +use rust_i18n::t; +use serde::Deserialize; + +use crate::models::validation_error::{ErrorMessage, Validate}; + +#[derive(Deserialize, Debug, Clone, Default)] +pub struct UpdateCollectionRequest { + pub name: String, +} + +impl UpdateCollectionRequest { + pub fn validate(&self) -> crate::error::Result> { + let mut errors: Vec = vec![]; + + if !self.name.required()? { + let error_message = ErrorMessage { + key: String::from("name"), + message: t!("validation_required", attribute = t!("name")).to_string(), + }; + + errors.push(error_message); + } + + Ok(errors) + } +} diff --git a/src/api/handlers/collection/store_collection_api_handler.rs b/src/api/handlers/collection/store_collection_api_handler.rs new file mode 100644 index 00000000..c3d1e0c3 --- /dev/null +++ b/src/api/handlers/collection/store_collection_api_handler.rs @@ -0,0 +1,54 @@ +use crate::api::handlers::collection::request::store_collection_request::StoreCollectionRequest; +use crate::avored_state::AvoRedState; +use crate::error::{Error, Result}; +use crate::models::collection_model::{CollectionModel, CreatableCollection}; +use crate::models::token_claim_model::LoggedInUser; +use crate::models::validation_error::ErrorResponse; +use crate::responses::ApiResponse; +use axum::extract::State; +use axum::{Extension, Json}; +use std::sync::Arc; + +pub async fn store_collection_api_handler( + state: State>, + Extension(logged_in_user): Extension, + Json(payload): Json, +) -> Result>> { + println!("->> {:<12} - store_collection_api_handler", "HANDLER"); + + let has_permission_bool = state + .admin_user_service + .has_permission(logged_in_user.clone(), String::from("collection_create")) + .await?; + if !has_permission_bool { + return Err(Error::Forbidden); + } + + let error_messages = payload.validate()?; + + if !error_messages.is_empty() { + let error_response = ErrorResponse { + status: false, + errors: error_messages, + }; + + return Err(Error::BadRequest(error_response)); + } + + let creatable_model = CreatableCollection { + name: payload.name, + identifier: payload.identifier, + logged_in_username: logged_in_user.email, + }; + + let created_model = state + .collection_service + .create_collection(&state.db, creatable_model) + .await?; + let response = ApiResponse { + status: true, + data: created_model, + }; + + Ok(Json(response)) +} diff --git a/src/api/handlers/collection/update_collection_api_handler.rs b/src/api/handlers/collection/update_collection_api_handler.rs new file mode 100644 index 00000000..78e9da73 --- /dev/null +++ b/src/api/handlers/collection/update_collection_api_handler.rs @@ -0,0 +1,55 @@ +use crate::api::handlers::collection::request::update_collection_request::UpdateCollectionRequest; +use crate::avored_state::AvoRedState; +use crate::error::{Error, Result}; +use crate::models::collection_model::{CollectionModel, UpdatableCollection}; +use crate::models::token_claim_model::LoggedInUser; +use crate::models::validation_error::ErrorResponse; +use crate::responses::ApiResponse; +use axum::extract::{Path, State}; +use axum::{Extension, Json}; +use std::sync::Arc; + +pub async fn update_collection_api_handler( + state: State>, + Path(collection_id): Path, + Extension(logged_in_user): Extension, + Json(payload): Json, +) -> Result>> { + println!("->> {:<12} - update_collection_api_handler", "HANDLER"); + + let has_permission_bool = state + .admin_user_service + .has_permission(logged_in_user.clone(), String::from("collection_update")) + .await?; + if !has_permission_bool { + return Err(Error::Forbidden); + } + + let error_messages = payload.validate()?; + + if !error_messages.is_empty() { + let error_response = ErrorResponse { + status: false, + errors: error_messages, + }; + + return Err(Error::BadRequest(error_response)); + } + + let creatable_collection = UpdatableCollection { + name: payload.name, + id: collection_id, + logged_in_username: logged_in_user.email, + }; + + let updated_model = state + .collection_service + .update_collection(&state.db, creatable_collection) + .await?; + let response = ApiResponse { + status: true, + data: updated_model, + }; + + Ok(Json(response)) +} diff --git a/src/api/handlers/component/component_all_api_handler.rs b/src/api/handlers/component/component_all_api_handler.rs index 40155278..33c6a15d 100644 --- a/src/api/handlers/component/component_all_api_handler.rs +++ b/src/api/handlers/component/component_all_api_handler.rs @@ -1,37 +1,36 @@ -use std::sync::Arc; -use axum::extract::State; -use axum::Json; -use crate::responses::ApiResponse; use crate::avored_state::AvoRedState; use crate::error::Result; use crate::models::component_model::ComponentModel; +use crate::responses::ApiResponse; +use axum::extract::State; +use axum::Json; +use std::sync::Arc; pub async fn component_all_api_handler( - state: State> + state: State>, ) -> Result>>> { println!("->> {:<12} - component_all_api_handler", "HANDLER"); let all_components = state.component_service.all(&state.db).await?; let response = ApiResponse { status: true, - data: all_components + data: all_components, }; Ok(Json(response)) } #[cfg(test)] mod tests { - use axum::http::StatusCode; - use serde_json::{json, Value}; - use tower::ServiceExt; use crate::api::rest_api_routes::tests::{get_auth_token, get_axum_app, send_get_request}; - use crate::responses::ApiResponse; - use crate::error::{Result, Error}; + use crate::error::{Error, Result}; use crate::models::component_model::ComponentModel; use crate::repositories::into_iter_objects; + use crate::responses::ApiResponse; + use axum::http::StatusCode; + use serde_json::{json, Value}; + use tower::ServiceExt; #[tokio::test] - async fn test_component_all_api_handler() -> Result<()> - { + async fn test_component_all_api_handler() -> Result<()> { let (app, state) = get_axum_app().await.unwrap(); let token = get_auth_token(state.clone())?; @@ -61,10 +60,13 @@ mod tests { let dummy_res = ApiResponse { status: true, - data: all_components + data: all_components, }; - let response = app.oneshot(send_get_request("/api/component-all", token)).await.unwrap(); + let response = app + .oneshot(send_get_request("/api/component-all", token)) + .await + .unwrap(); assert_eq!(response.status(), StatusCode::OK); let res_b = response.into_body(); @@ -74,4 +76,4 @@ mod tests { println!("Component_ALL: {:?}", json!(&dummy_res)); Ok(()) } -} \ No newline at end of file +} diff --git a/src/api/handlers/component/component_table_api_handler.rs b/src/api/handlers/component/component_table_api_handler.rs index a66eec22..9c80d749 100644 --- a/src/api/handlers/component/component_table_api_handler.rs +++ b/src/api/handlers/component/component_table_api_handler.rs @@ -1,11 +1,11 @@ -use std::sync::Arc; -use axum::extract::{Query, State}; -use axum::{Extension, Json}; use crate::api::handlers::page::request::page_table_request::PageTableRequest; use crate::avored_state::AvoRedState; use crate::error::{Error, Result}; use crate::models::component_model::ComponentPagination; use crate::models::token_claim_model::LoggedInUser; +use axum::extract::{Query, State}; +use axum::{Extension, Json}; +use std::sync::Arc; pub async fn component_table_api_handler( state: State>, @@ -26,7 +26,8 @@ pub async fn component_table_api_handler( let order = query_param.order.unwrap_or(String::from("")); let component_pagination = state .component_service - .paginate(&state.db, current_page, order).await?; + .paginate(&state.db, current_page, order) + .await?; Ok(Json(component_pagination)) } diff --git a/src/api/handlers/component/fetch_component_api_handler.rs b/src/api/handlers/component/fetch_component_api_handler.rs index 30addec2..7bf6c1c7 100644 --- a/src/api/handlers/component/fetch_component_api_handler.rs +++ b/src/api/handlers/component/fetch_component_api_handler.rs @@ -1,19 +1,21 @@ use std::sync::Arc; -use crate::{ - avored_state::AvoRedState, error::Result -}; +use crate::{avored_state::AvoRedState, error::Result}; -use axum::{Extension, extract::{Path as AxumPath, State}, Json, response::IntoResponse}; -use serde::Serialize; use crate::error::Error; use crate::models::component_model::ComponentModel; use crate::models::token_claim_model::LoggedInUser; +use axum::{ + extract::{Path as AxumPath, State}, + response::IntoResponse, + Extension, Json, +}; +use serde::Serialize; pub async fn fetch_component_api_handler( AxumPath(component_id): AxumPath, Extension(logged_in_user): Extension, - state: State> + state: State>, ) -> Result { println!("->> {:<12} - fetch_component_api_handler", "HANDLER"); @@ -31,15 +33,14 @@ pub async fn fetch_component_api_handler( .await?; let response = FetchPageResponse { status: true, - component_model + component_model, }; Ok(Json(response)) } - #[derive(Serialize, Debug)] pub struct FetchPageResponse { pub status: bool, - pub component_model: ComponentModel -} \ No newline at end of file + pub component_model: ComponentModel, +} diff --git a/src/api/handlers/component/mod.rs b/src/api/handlers/component/mod.rs index 3bd572eb..5e05620a 100644 --- a/src/api/handlers/component/mod.rs +++ b/src/api/handlers/component/mod.rs @@ -1,7 +1,7 @@ +pub mod component_all_api_handler; pub mod component_table_api_handler; -pub mod store_component_api_handler; pub mod fetch_component_api_handler; -pub mod update_component_api_handler; pub mod put_component_identifier_api_handler; pub mod request; -pub mod component_all_api_handler; \ No newline at end of file +pub mod store_component_api_handler; +pub mod update_component_api_handler; diff --git a/src/api/handlers/component/put_component_identifier_api_handler.rs b/src/api/handlers/component/put_component_identifier_api_handler.rs index f87fd7c7..e43fa4ba 100644 --- a/src/api/handlers/component/put_component_identifier_api_handler.rs +++ b/src/api/handlers/component/put_component_identifier_api_handler.rs @@ -1,18 +1,19 @@ use std::sync::Arc; use crate::models::component_model::PutComponentIdentifierModel; -use crate::{ - avored_state::AvoRedState, error::Result -}; +use crate::{avored_state::AvoRedState, error::Result}; -use axum::{Extension, extract::{Path as AxumPath, State}, Json}; -use axum::response::IntoResponse; use crate::api::handlers::component::request::put_component_request::PutComponentRequest; use crate::error::Error; use crate::models::token_claim_model::LoggedInUser; use crate::models::validation_error::ErrorResponse; -use crate::responses::ApiResponse; use crate::responses::component::PutComponentIdentifierResponse; +use crate::responses::ApiResponse; +use axum::response::IntoResponse; +use axum::{ + extract::{Path as AxumPath, State}, + Extension, Json, +}; pub async fn put_component_identifier_api_handler( AxumPath(component_id): AxumPath, @@ -20,7 +21,10 @@ pub async fn put_component_identifier_api_handler( state: State>, Json(payload): Json, ) -> Result { - println!("->> {:<12} - put_component_identifier_api_handler", "HANDLER"); + println!( + "->> {:<12} - put_component_identifier_api_handler", + "HANDLER" + ); let has_permission_bool = state .admin_user_service @@ -35,17 +39,16 @@ pub async fn put_component_identifier_api_handler( if !error_messages.is_empty() { let error_response = ErrorResponse { status: false, - errors: error_messages + errors: error_messages, }; return Err(Error::BadRequest(error_response)); } - let put_component_identifier = PutComponentIdentifierModel { id: component_id, identifier: payload.identifier, - logged_in_username: logged_in_user.email + logged_in_username: logged_in_user.email, }; let updated_component_model = state .component_service @@ -53,13 +56,13 @@ pub async fn put_component_identifier_api_handler( .await?; let updated_component_response = PutComponentIdentifierResponse { - component: updated_component_model + component: updated_component_model, }; let api_response = ApiResponse { status: true, - data: updated_component_response + data: updated_component_response, }; Ok(Json(api_response)) -} \ No newline at end of file +} diff --git a/src/api/handlers/component/request/mod.rs b/src/api/handlers/component/request/mod.rs index f1d836c1..1e2b5565 100644 --- a/src/api/handlers/component/request/mod.rs +++ b/src/api/handlers/component/request/mod.rs @@ -1,3 +1,3 @@ +pub mod put_component_request; pub mod store_component_request; pub mod update_component_request; -pub mod put_component_request; \ No newline at end of file diff --git a/src/api/handlers/component/request/put_component_request.rs b/src/api/handlers/component/request/put_component_request.rs index 7a5ebc97..dab75c39 100644 --- a/src/api/handlers/component/request/put_component_request.rs +++ b/src/api/handlers/component/request/put_component_request.rs @@ -1,9 +1,9 @@ -use std::sync::Arc; +use crate::avored_state::AvoRedState; +use crate::models::validation_error::{ErrorMessage, Validate}; use axum::extract::State; use rust_i18n::t; use serde::Deserialize; -use crate::avored_state::AvoRedState; -use crate::models::validation_error::{ErrorMessage, Validate}; +use std::sync::Arc; #[derive(Deserialize, Debug, Clone, Default)] pub struct PutComponentRequest { @@ -11,13 +11,16 @@ pub struct PutComponentRequest { } impl PutComponentRequest { - pub async fn validate(&self, state: State>) -> crate::error::Result> { + pub async fn validate( + &self, + state: State>, + ) -> crate::error::Result> { let mut errors: Vec = vec![]; if !self.identifier.required()? { let error_message = ErrorMessage { key: String::from("identifier"), - message: t!("validation_required", attribute = t!("identifier")).to_string() + message: t!("validation_required", attribute = t!("identifier")).to_string(), }; errors.push(error_message); @@ -31,7 +34,7 @@ impl PutComponentRequest { if component_model_count.total > 0 { let error_message = ErrorMessage { key: String::from("identifier"), - message: t!("validation_count", attribute = t!("identifier")).to_string() + message: t!("validation_count", attribute = t!("identifier")).to_string(), }; errors.push(error_message); } diff --git a/src/api/handlers/component/request/store_component_request.rs b/src/api/handlers/component/request/store_component_request.rs index 50037ab6..02ab5cd3 100644 --- a/src/api/handlers/component/request/store_component_request.rs +++ b/src/api/handlers/component/request/store_component_request.rs @@ -1,6 +1,6 @@ -use serde::Deserialize; -use rust_i18n::t; use crate::models::validation_error::{ErrorMessage, Validate}; +use rust_i18n::t; +use serde::Deserialize; #[derive(Deserialize, Debug, Clone)] pub struct StoreComponentRequest { @@ -15,7 +15,7 @@ pub struct CreatableElementRequest { pub identifier: String, pub element_type: String, pub element_data_type: String, - pub element_data: Option> + pub element_data: Option>, } #[derive(Deserialize, Debug, Clone, Default)] @@ -29,10 +29,9 @@ impl StoreComponentRequest { let mut errors: Vec = vec![]; if !self.name.required()? { - let error_message = ErrorMessage { key: String::from("name"), - message: t!("validation_required", attribute = t!("name")).to_string() + message: t!("validation_required", attribute = t!("name")).to_string(), }; errors.push(error_message); @@ -41,7 +40,7 @@ impl StoreComponentRequest { if !self.identifier.required()? { let error_message = ErrorMessage { key: String::from("identifier"), - message: t!("validation_required", attribute = t!("identifier")).to_string() + message: t!("validation_required", attribute = t!("identifier")).to_string(), }; errors.push(error_message); diff --git a/src/api/handlers/component/request/update_component_request.rs b/src/api/handlers/component/request/update_component_request.rs index 4d5d5a61..fe33ae64 100644 --- a/src/api/handlers/component/request/update_component_request.rs +++ b/src/api/handlers/component/request/update_component_request.rs @@ -1,6 +1,6 @@ -use serde::Deserialize; -use rust_i18n::t; use crate::models::validation_error::{ErrorMessage, Validate}; +use rust_i18n::t; +use serde::Deserialize; #[derive(Deserialize, Debug, Clone, Default)] pub struct UpdateComponentRequest { @@ -14,7 +14,7 @@ pub struct UpdatableElementRequest { pub identifier: String, pub element_type: String, pub element_data_type: String, - pub element_data: Option> + pub element_data: Option>, } #[derive(Deserialize, Debug, Clone, Default)] @@ -23,7 +23,6 @@ pub struct UpdatableComponentElementDataRequest { pub value: String, } - impl UpdateComponentRequest { pub fn validate(&self) -> crate::error::Result> { let mut errors: Vec = vec![]; @@ -31,7 +30,7 @@ impl UpdateComponentRequest { if !self.name.required()? { let error_message = ErrorMessage { key: String::from("name"), - message: t!("validation_required", attribute = t!("name")).to_string() + message: t!("validation_required", attribute = t!("name")).to_string(), }; errors.push(error_message); diff --git a/src/api/handlers/component/store_component_api_handler.rs b/src/api/handlers/component/store_component_api_handler.rs index 1aa47a59..5257b35b 100644 --- a/src/api/handlers/component/store_component_api_handler.rs +++ b/src/api/handlers/component/store_component_api_handler.rs @@ -1,16 +1,17 @@ use std::sync::Arc; -use crate::{ - avored_state::AvoRedState, error::Result +use crate::api::handlers::component::request::store_component_request::{ + CreatableComponentElementDataRequest, StoreComponentRequest, }; -use axum::{Extension, extract::State, Json}; -use serde::Serialize; -use crate::api::handlers::component::request::store_component_request::{CreatableComponentElementDataRequest, StoreComponentRequest}; use crate::error::Error; -use crate::models::component_model::{ComponentElementDataModel, ComponentModel, CreatableComponent, CreatableComponentElementModel}; +use crate::models::component_model::{ + ComponentElementDataModel, ComponentModel, CreatableComponent, CreatableComponentElementModel, +}; use crate::models::token_claim_model::LoggedInUser; use crate::models::validation_error::ErrorResponse; - +use crate::{avored_state::AvoRedState, error::Result}; +use axum::{extract::State, Extension, Json}; +use serde::Serialize; pub async fn store_component_api_handler( Extension(logged_in_user): Extension, @@ -32,7 +33,7 @@ pub async fn store_component_api_handler( if !error_messages.is_empty() { let error_response = ErrorResponse { status: false, - errors: error_messages + errors: error_messages, }; return Err(Error::BadRequest(error_response)); @@ -40,15 +41,17 @@ pub async fn store_component_api_handler( let mut creatable_elements: Vec = vec![]; for payload_element in payload.elements { - let mut creatable_element_data: Vec = vec![]; - let create_element_data_requests: Vec = payload_element.element_data.unwrap_or_else(std::vec::Vec::new); + let create_element_data_requests: Vec = + payload_element + .element_data + .unwrap_or_else(std::vec::Vec::new); for create_element_data_request in create_element_data_requests { let create_element_data_option = ComponentElementDataModel { label: create_element_data_request.label, - value: create_element_data_request.value + value: create_element_data_request.value, }; creatable_element_data.push(create_element_data_option); } @@ -58,18 +61,16 @@ pub async fn store_component_api_handler( identifier: payload_element.identifier, element_type: payload_element.element_type, element_data_type: payload_element.element_data_type, - element_data: Some(creatable_element_data) - + element_data: Some(creatable_element_data), }; creatable_elements.push(creatable_component_element_model); } - let creatable_component = CreatableComponent { name: payload.name, identifier: payload.identifier, logged_in_username: logged_in_user.email.clone(), - elements: creatable_elements + elements: creatable_elements, }; let created_component = state @@ -79,7 +80,7 @@ pub async fn store_component_api_handler( let created_response = CreatedComponentResponse { status: true, - component_model: created_component + component_model: created_component, }; Ok(Json(created_response)) @@ -88,5 +89,5 @@ pub async fn store_component_api_handler( #[derive(Serialize, Debug)] pub struct CreatedComponentResponse { pub status: bool, - pub component_model: ComponentModel -} \ No newline at end of file + pub component_model: ComponentModel, +} diff --git a/src/api/handlers/component/update_component_api_handler.rs b/src/api/handlers/component/update_component_api_handler.rs index 4014fdce..1ebcf0ce 100644 --- a/src/api/handlers/component/update_component_api_handler.rs +++ b/src/api/handlers/component/update_component_api_handler.rs @@ -1,16 +1,22 @@ use std::sync::Arc; -use crate::{ - avored_state::AvoRedState, - error::Result, +use crate::api::handlers::component::request::update_component_request::{ + UpdatableComponentElementDataRequest, UpdateComponentRequest, }; -use axum::{Extension, extract::{Path as AxumPath, State}, Json, response::IntoResponse}; -use serde::Serialize; -use crate::api::handlers::component::request::update_component_request::{UpdatableComponentElementDataRequest, UpdateComponentRequest}; use crate::error::Error; -use crate::models::component_model::{ComponentElementDataModel, ComponentModel, UpdatableComponentElementModel, UpdatableComponentModel}; +use crate::models::component_model::{ + ComponentElementDataModel, ComponentModel, UpdatableComponentElementModel, + UpdatableComponentModel, +}; use crate::models::token_claim_model::LoggedInUser; use crate::models::validation_error::ErrorResponse; +use crate::{avored_state::AvoRedState, error::Result}; +use axum::{ + extract::{Path as AxumPath, State}, + response::IntoResponse, + Extension, Json, +}; +use serde::Serialize; pub async fn update_component_api_handler( Extension(logged_in_user): Extension, @@ -33,7 +39,7 @@ pub async fn update_component_api_handler( if !error_messages.is_empty() { let error_response = ErrorResponse { status: false, - errors: error_messages + errors: error_messages, }; return Err(Error::BadRequest(error_response)); @@ -41,15 +47,17 @@ pub async fn update_component_api_handler( let mut updatable_elements: Vec = vec![]; for payload_element in payload.elements { - let mut updatable_element_data: Vec = vec![]; - let updatable_element_data_requests: Vec = payload_element.element_data.unwrap_or_else(std::vec::Vec::new); + let updatable_element_data_requests: Vec = + payload_element + .element_data + .unwrap_or_else(std::vec::Vec::new); for update_element_data_request in updatable_element_data_requests { let create_element_data_option = ComponentElementDataModel { label: update_element_data_request.label, - value: update_element_data_request.value + value: update_element_data_request.value, }; updatable_element_data.push(create_element_data_option); } @@ -59,7 +67,7 @@ pub async fn update_component_api_handler( identifier: payload_element.identifier, element_type: payload_element.element_type, element_data_type: payload_element.element_data_type, - element_data: Some(updatable_element_data) + element_data: Some(updatable_element_data), }; updatable_elements.push(updatable_component_element_model); } @@ -68,7 +76,7 @@ pub async fn update_component_api_handler( id: component_id, name: payload.name, logged_in_username: logged_in_user.email.clone(), - elements: updatable_elements + elements: updatable_elements, }; let updated_component_model = state .component_service @@ -77,7 +85,7 @@ pub async fn update_component_api_handler( let response = UpdatedComponentResponse { status: true, - component_model: updated_component_model + component_model: updated_component_model, }; Ok(Json(response)) @@ -86,5 +94,5 @@ pub async fn update_component_api_handler( #[derive(Serialize, Debug)] pub struct UpdatedComponentResponse { pub status: bool, - pub component_model: ComponentModel -} \ No newline at end of file + pub component_model: ComponentModel, +} diff --git a/src/api/handlers/content/content_table_api_handler.rs b/src/api/handlers/content/content_table_api_handler.rs new file mode 100644 index 00000000..b27240f2 --- /dev/null +++ b/src/api/handlers/content/content_table_api_handler.rs @@ -0,0 +1,40 @@ +use crate::api::handlers::page::request::page_table_request::PageTableRequest; +use crate::avored_state::AvoRedState; +use crate::error::{Error, Result}; +use crate::models::token_claim_model::LoggedInUser; +use axum::extract::{Path, Query, State}; +use axum::{Extension, Json}; +use std::sync::Arc; +use crate::models::content_model::ContentPagination; +use crate::responses::ApiResponse; + +pub async fn content_table_api_handler( + state: State>, + Extension(logged_in_user): Extension, + Query(query_param): Query, + Path(content_type):Path, +) -> Result>> { + println!("->> {:<12} - content_table_api_handler", "HANDLER"); + + let has_permission_bool = state + .admin_user_service + .has_permission(logged_in_user, String::from("content_table")) + .await?; + if !has_permission_bool { + return Err(Error::Forbidden); + } + + let current_page = query_param.page.unwrap_or(0); + let order = query_param.order.unwrap_or(String::from("")); + let content_pagination = state + .content_service + .paginate(&state.db, &content_type, current_page, order) + .await?; + + let response = ApiResponse { + status: true, + data: content_pagination + }; + + Ok(Json(response)) +} diff --git a/src/api/handlers/content/fetch_content_api_handler.rs b/src/api/handlers/content/fetch_content_api_handler.rs new file mode 100644 index 00000000..e621b435 --- /dev/null +++ b/src/api/handlers/content/fetch_content_api_handler.rs @@ -0,0 +1,40 @@ +use std::sync::Arc; +use crate::{avored_state::AvoRedState, error::Result}; + +use crate::error::Error; +use crate::models::token_claim_model::LoggedInUser; +use axum::{ + extract::{Path as AxumPath, State}, + Extension, Json, +}; +use crate::models::content_model::ContentModel; +use crate::responses::ApiResponse; + +pub async fn fetch_content_api_handler( + AxumPath((content_type, content_id)): AxumPath<(String, String)>, + Extension(logged_in_user): Extension, + state: State>, +) -> Result>> { + println!("->> {:<12} - fetch_content_api_handler", "HANDLER"); + + let has_permission_bool = state + .admin_user_service + .has_permission(logged_in_user, String::from("get_content")) + .await?; + if !has_permission_bool { + return Err(Error::Forbidden); + } + + + let model = state.content_service + .find_by_id(&state.db, content_type, &content_id) + .await?; + + let response = ApiResponse { + status: true, + data: model, + }; + + Ok(Json(response)) +} + diff --git a/src/api/handlers/content/mod.rs b/src/api/handlers/content/mod.rs new file mode 100644 index 00000000..55568a4d --- /dev/null +++ b/src/api/handlers/content/mod.rs @@ -0,0 +1,6 @@ +pub mod request; +pub mod store_content_api_handler; +pub mod content_table_api_handler; +pub mod fetch_content_api_handler; +pub mod update_content_api_handler; +pub mod put_content_identifier_api_handler; \ No newline at end of file diff --git a/src/api/handlers/content/put_content_identifier_api_handler.rs b/src/api/handlers/content/put_content_identifier_api_handler.rs new file mode 100644 index 00000000..e930efc2 --- /dev/null +++ b/src/api/handlers/content/put_content_identifier_api_handler.rs @@ -0,0 +1,64 @@ +use std::sync::Arc; + +use crate::models::page_model::PutPageIdentifierModel; +use crate::{avored_state::AvoRedState, error::Result}; + +use crate::api::handlers::page::request::put_page_request::PutPageRequest; +use crate::error::Error; +use crate::models::token_claim_model::LoggedInUser; +use crate::models::validation_error::ErrorResponse; +use crate::responses::page::PutPageIdentifierResponse; +use crate::responses::ApiResponse; +use axum::response::IntoResponse; +use axum::{ + extract::{Path as AxumPath, State}, + Extension, Json, +}; +use crate::api::handlers::content::request::put_content_identifier_request::PutContentIdentifierRequest; +use crate::models::content_model::PutContentIdentifierModel; + +pub async fn put_content_identifier_api_handler( + AxumPath((content_type, content_id)): AxumPath<(String, String)>, + Extension(logged_in_user): Extension, + state: State>, + Json(payload): Json, +) -> Result { + println!("->> {:<12} - put_page_identifier_api_handler", "HANDLER"); + + let has_permission_bool = state + .admin_user_service + .has_permission(logged_in_user.clone(), String::from("content_edit")) + .await?; + if !has_permission_bool { + return Err(Error::Forbidden); + } + + let error_messages = payload.validate(state.clone(), &content_type).await?; + + if !error_messages.is_empty() { + let error_response = ErrorResponse { + status: false, + errors: error_messages, + }; + + return Err(Error::BadRequest(error_response)); + } + + let put_content_identifier = PutContentIdentifierModel { + id: content_id, + collection_type: content_type, + identifier: payload.identifier, + logged_in_username: logged_in_user.email, + }; + let updated_page_model = state + .content_service + .update_content_identifier(&state.db, put_content_identifier) + .await?; + + let api_response = ApiResponse { + status: true, + data: updated_page_model, + }; + + Ok(Json(api_response)) +} diff --git a/src/api/handlers/content/request/mod.rs b/src/api/handlers/content/request/mod.rs new file mode 100644 index 00000000..f3a2c807 --- /dev/null +++ b/src/api/handlers/content/request/mod.rs @@ -0,0 +1,3 @@ +pub mod store_content_request; +pub mod update_content_request; +pub mod put_content_identifier_request; \ No newline at end of file diff --git a/src/api/handlers/content/request/put_content_identifier_request.rs b/src/api/handlers/content/request/put_content_identifier_request.rs new file mode 100644 index 00000000..d8df7b17 --- /dev/null +++ b/src/api/handlers/content/request/put_content_identifier_request.rs @@ -0,0 +1,45 @@ +use crate::avored_state::AvoRedState; +use crate::models::validation_error::{ErrorMessage, Validate}; +use axum::extract::State; +use rust_i18n::t; +use serde::Deserialize; +use std::sync::Arc; + +#[derive(Deserialize, Debug, Clone, Default)] +pub struct PutContentIdentifierRequest { + pub identifier: String, +} + +impl PutContentIdentifierRequest { + pub async fn validate( + &self, + state: State>, + collection_type: &str, + ) -> crate::error::Result> { + let mut errors: Vec = vec![]; + + if !self.identifier.required()? { + let error_message = ErrorMessage { + key: String::from("identifier"), + message: t!("validation_required", attribute = t!("identifier")).to_string(), + }; + + errors.push(error_message); + } + + let model_count = state + .content_service + .count_of_identifier(&state.db, &self.identifier, collection_type) + .await?; + + if model_count.total > 0 { + let error_message = ErrorMessage { + key: String::from("identifier"), + message: t!("validation_count", attribute = t!("identifier")).to_string(), + }; + errors.push(error_message); + } + + Ok(errors) + } +} diff --git a/src/api/handlers/content/request/store_content_request.rs b/src/api/handlers/content/request/store_content_request.rs new file mode 100644 index 00000000..ca365976 --- /dev/null +++ b/src/api/handlers/content/request/store_content_request.rs @@ -0,0 +1,63 @@ +use crate::avored_state::AvoRedState; +use crate::error::Result; +use crate::models::validation_error::{ErrorMessage, Validate}; +use rust_i18n::t; +use serde::Deserialize; +use crate::models::content_model::{ContentDataType, ContentFieldContentType, ContentFieldType}; + +#[derive(Deserialize, Debug, Clone, Default)] +pub struct StoreContentRequest { + pub name: String, + pub identifier: String, + pub content_type: String, + pub content_fields: Vec, +} + +#[derive(Deserialize, Debug, Clone, Default)] +pub struct CreatableContentFieldRequest { + pub name: String, + pub identifier: String, + pub data_type: ContentDataType, + pub field_type: ContentFieldType, + pub field_content: ContentFieldContentType, +} + +impl StoreContentRequest { + pub async fn validate(&self, _state: &AvoRedState) -> Result> { + let mut errors: Vec = vec![]; + + if !self.name.required()? { + let error_message = ErrorMessage { + key: String::from("name"), + message: t!("validation_required", attribute = t!("name")).to_string(), + }; + + errors.push(error_message); + } + + if !self.identifier.required()? { + let error_message = ErrorMessage { + key: String::from("identifier"), + message: t!("validation_required", attribute = t!("identifier")).to_string(), + }; + + errors.push(error_message); + } + + // let page_count = state + // .page_service + // .count_of_identifier(&state.db, self.identifier.clone()) + // .await?; + // + // if page_count.total > 0 { + // let error_message = ErrorMessage { + // key: String::from("identifier"), + // message: t!("validation_count", attribute = t!("identifier")).to_string(), + // }; + // + // errors.push(error_message); + // } + + Ok(errors) + } +} diff --git a/src/api/handlers/content/request/update_content_request.rs b/src/api/handlers/content/request/update_content_request.rs new file mode 100644 index 00000000..da009e63 --- /dev/null +++ b/src/api/handlers/content/request/update_content_request.rs @@ -0,0 +1,37 @@ +use crate::models::validation_error::{ErrorMessage, Validate}; +use rust_i18n::t; +use serde::Deserialize; +use crate::models::content_model::{ContentDataType, ContentFieldContentType, ContentFieldType}; + +#[derive(Deserialize, Debug, Clone, Default)] +pub struct UpdateContentRequest { + pub name: String, + pub identifier: String, + pub content_type: String, + pub content_fields: Vec, +} + +#[derive(Deserialize, Debug, Clone, Default)] +pub struct UpdatableContentField { + pub name: String, + pub identifier: String, + pub data_type: ContentDataType, + pub field_type: ContentFieldType, + pub field_content: ContentFieldContentType, +} + +impl UpdateContentRequest { + pub fn validate(&self) -> crate::error::Result> { + let mut errors: Vec = vec![]; + if !self.name.required()? { + let error_message = ErrorMessage { + key: String::from("name"), + message: t!("validation_required", attribute = t!("name")).to_string(), + }; + + errors.push(error_message); + } + + Ok(errors) + } +} diff --git a/src/api/handlers/content/store_content_api_handler.rs b/src/api/handlers/content/store_content_api_handler.rs new file mode 100644 index 00000000..a0e5e559 --- /dev/null +++ b/src/api/handlers/content/store_content_api_handler.rs @@ -0,0 +1,68 @@ +use std::sync::Arc; + + +use crate::error::Error; +use crate::models::token_claim_model::LoggedInUser; +use crate::models::validation_error::ErrorResponse; +use crate::responses::ApiResponse; +use crate::{avored_state::AvoRedState, error::Result}; +use axum::{extract::State, Extension, Json}; +use crate::api::handlers::content::request::store_content_request::StoreContentRequest; +use crate::models::content_model::{ContentModel, CreatableContentField, CreatableContentModel}; + +pub async fn store_content_api_handler( + Extension(logged_in_user): Extension, + state: State>, + Json(payload): Json, +) -> Result>> { + println!("->> {:<12} - store_content_api_handler", "HANDLER"); + let error_messages = payload.validate(&state).await?; + + let has_permission_bool = state + .admin_user_service + .has_permission(logged_in_user.clone(), String::from("content_create")) + .await?; + if !has_permission_bool { + return Err(Error::Forbidden); + } + + if !error_messages.is_empty() { + let error_response = ErrorResponse { + status: false, + errors: error_messages, + }; + + return Err(Error::BadRequest(error_response)); + } + + let mut creatable_content = CreatableContentModel { + name: payload.name, + identifier: payload.identifier, + logged_in_username: logged_in_user.name.clone(), + content_type: payload.content_type.clone(), + content_fields: vec![], + }; + + for payload_content_field in payload.content_fields { + let creatable_content_field_model = CreatableContentField { + name: payload_content_field.name, + identifier: payload_content_field.identifier, + data_type: payload_content_field.data_type, + field_type: payload_content_field.field_type, + field_content: payload_content_field.field_content, + }; + creatable_content.content_fields.push(creatable_content_field_model); + } + + let created_content_model = state + .content_service + .create_content(&state.db, creatable_content) + .await?; + + let response = ApiResponse { + status: true, + data: created_content_model, + }; + + Ok(Json(response)) +} diff --git a/src/api/handlers/content/update_content_api_handler.rs b/src/api/handlers/content/update_content_api_handler.rs new file mode 100644 index 00000000..845eea1d --- /dev/null +++ b/src/api/handlers/content/update_content_api_handler.rs @@ -0,0 +1,80 @@ +use std::sync::Arc; + +use crate::error::Error; +use crate::{ + avored_state::AvoRedState, error::Result, +}; + +use crate::models::token_claim_model::LoggedInUser; +use crate::models::validation_error::ErrorResponse; +use crate::responses::ApiResponse; +use axum::{ + extract::{Path as AxumPath, State}, + Extension, Json, +}; +use crate::api::handlers::content::request::update_content_request::UpdateContentRequest; +use crate::models::content_model::{ContentModel, UpdatableContentField, UpdatableContentModel}; + +pub async fn update_content_api_handler( + Extension(logged_in_user): Extension, + AxumPath((content_type, content_id)): AxumPath<(String, String)>, + state: State>, + Json(payload): Json, +) -> Result>> { + println!("->> {:<12} - update_content_api_handler", "HANDLER"); + + let has_permission_bool = state + .admin_user_service + .has_permission(logged_in_user.clone(), String::from("content_edit")) + .await?; + if !has_permission_bool { + return Err(Error::Forbidden); + } + + let error_messages = payload.validate()?; + + if !error_messages.is_empty() { + let error_response = ErrorResponse { + status: false, + errors: error_messages, + }; + + return Err(Error::BadRequest(error_response)); + } + + let content_model = state.content_service.find_by_id(&state.db, content_type, &content_id).await?; + + let mut updatable_content = UpdatableContentModel { + id: content_model.id, + name: payload.name, + identifier: payload.identifier, + content_type: payload.content_type, + logged_in_username: logged_in_user.name.clone(), + content_fields: vec![], + created_at: content_model.created_at, + created_by: content_model.created_by, + }; + + for payload_content_field in payload.content_fields { + let content_field_model = UpdatableContentField { + name: payload_content_field.name, + identifier: payload_content_field.identifier, + data_type: payload_content_field.data_type, + field_type: payload_content_field.field_type, + field_content: payload_content_field.field_content, + }; + updatable_content.content_fields.push(content_field_model); + } + + let updated_model = state + .content_service + .update_content(&state.db, updatable_content) + .await?; + + let response = ApiResponse { + status: true, + data: updated_model, + }; + + Ok(Json(response)) +} diff --git a/src/api/handlers/graphql/graphql_api_handler.rs b/src/api/handlers/graphql/graphql_api_handler.rs deleted file mode 100644 index 7a5e159c..00000000 --- a/src/api/handlers/graphql/graphql_api_handler.rs +++ /dev/null @@ -1,18 +0,0 @@ -use std::sync::Arc; -use axum::Extension; -use axum::extract::State; -use juniper_axum::extract::JuniperRequest; -use juniper_axum::response::JuniperResponse; -use crate::avored_state::AvoRedState; -use crate::providers::avored_graphql_provider::{AvoRedGraphqlSchema}; - -pub async fn graphql_api_handler( - Extension(schema): Extension>, - ctx: State>, - JuniperRequest(request): JuniperRequest, -) -> JuniperResponse { - println!("->> {:<12} - graphql_api_handler", "HANDLER"); - let res = request.execute(&*schema, &ctx).await; - JuniperResponse(res) -} - diff --git a/src/api/handlers/graphql/mod.rs b/src/api/handlers/graphql/mod.rs deleted file mode 100644 index 355bae43..00000000 --- a/src/api/handlers/graphql/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod graphql_api_handler; \ No newline at end of file diff --git a/src/api/handlers/misc/delete_demo_data_api_handler.rs b/src/api/handlers/misc/delete_demo_data_api_handler.rs index 6d7e71b6..70acc3f7 100644 --- a/src/api/handlers/misc/delete_demo_data_api_handler.rs +++ b/src/api/handlers/misc/delete_demo_data_api_handler.rs @@ -1,13 +1,10 @@ use std::collections::BTreeMap; use std::sync::Arc; -use crate::{ - avored_state::AvoRedState, - error::Result, -}; -use axum::{extract::State, Json, response::IntoResponse, Extension}; -use serde::Serialize; use crate::models::token_claim_model::LoggedInUser; +use crate::{avored_state::AvoRedState, error::Result}; +use axum::{extract::State, response::IntoResponse, Extension, Json}; +use serde::Serialize; pub async fn delete_demo_data_api_handler( Extension(logged_in_user): Extension, @@ -23,9 +20,7 @@ pub async fn delete_demo_data_api_handler( DELETE pages WHERE identifier='home-page'; "; - let vars = BTreeMap::from([ - ("email".into(), logged_in_user.email.into()), - ]); + let vars = BTreeMap::from([("email".into(), logged_in_user.email.into())]); let (ds, ses) = &state.db; @@ -36,9 +31,7 @@ pub async fn delete_demo_data_api_handler( tokio::fs::remove_file("public/install_demo").await?; - let response = DemoDataViewModel { - status: true - }; + let response = DemoDataViewModel { status: true }; Ok(Json(response)) } diff --git a/src/api/handlers/misc/health_check_api_handler.rs b/src/api/handlers/misc/health_check_api_handler.rs index 6b0228cd..812bc455 100644 --- a/src/api/handlers/misc/health_check_api_handler.rs +++ b/src/api/handlers/misc/health_check_api_handler.rs @@ -1,12 +1,12 @@ +use crate::error::Result; use axum::Json; use serde::Serialize; -use crate::error::Result; pub async fn health_check_api_handler() -> Result> { println!("->> {:<12} - health_check_api_handler", "HANDLER"); let response = ResponseData { status: true, - data: String::from("ok") + data: String::from("ok"), }; Ok(Json(response)) @@ -15,28 +15,30 @@ pub async fn health_check_api_handler() -> Result> { #[derive(Serialize)] pub struct ResponseData { status: bool, - data: String + data: String, } #[cfg(test)] mod tests { - use axum::http::StatusCode; - use serde_json::{json, Value}; - use tower::ServiceExt; use crate::api::handlers::misc::health_check_api_handler::ResponseData; use crate::api::rest_api_routes::tests::{get_axum_app, send_get_request}; use crate::error::Result; + use axum::http::StatusCode; + use serde_json::{json, Value}; + use tower::ServiceExt; #[tokio::test] - async fn test_health_check_api_handler() -> Result<()> - { + async fn test_health_check_api_handler() -> Result<()> { let (app, _state) = get_axum_app().await.unwrap(); - let response = app.oneshot(send_get_request("/api/health-check", String::from(""))).await.unwrap(); + let response = app + .oneshot(send_get_request("/api/health-check", String::from(""))) + .await + .unwrap(); let dummy_res = ResponseData { status: true, - data: String::from("ok") + data: String::from("ok"), }; assert_eq!(response.status(), StatusCode::OK); @@ -46,4 +48,4 @@ mod tests { assert_eq!(body, json!(&dummy_res)); Ok(()) } -} \ No newline at end of file +} diff --git a/src/api/handlers/misc/install_demo_data_api_handler.rs b/src/api/handlers/misc/install_demo_data_api_handler.rs index 42da49eb..8705f368 100644 --- a/src/api/handlers/misc/install_demo_data_api_handler.rs +++ b/src/api/handlers/misc/install_demo_data_api_handler.rs @@ -1,18 +1,15 @@ -use std::collections::BTreeMap; -use std::sync::Arc; -use argon2::{Argon2, PasswordHasher}; +use crate::models::admin_user_model::CreatableAdminUserModel; +use crate::models::role_model::CreatableRole; +use crate::models::token_claim_model::LoggedInUser; +use crate::{avored_state::AvoRedState, error::Result}; use argon2::password_hash::SaltString; -use crate::{ - avored_state::AvoRedState, - error::Result, -}; -use axum::{extract::State, Json, response::IntoResponse, Extension}; +use argon2::{Argon2, PasswordHasher}; +use axum::{extract::State, response::IntoResponse, Extension, Json}; use serde::Serialize; +use std::collections::BTreeMap; +use std::sync::Arc; use tokio::fs::File; use tokio::io::AsyncWriteExt; -use crate::models::admin_user_model::CreatableAdminUserModel; -use crate::models::role_model::CreatableRole; -use crate::models::token_claim_model::LoggedInUser; pub async fn install_demo_data_api_handler( Extension(logged_in_user): Extension, @@ -238,9 +235,7 @@ pub async fn install_demo_data_api_handler( }; "; - let vars = BTreeMap::from([ - ("email".into(), logged_in_user.email.clone().into()), - ]); + let vars = BTreeMap::from([("email".into(), logged_in_user.email.clone().into())]); let (ds, ses) = &state.db; @@ -271,14 +266,10 @@ pub async fn install_demo_data_api_handler( String::from("asset_create"), String::from("asset_edit"), String::from("asset_delete"), - ], }; - let created_role_model = state - .role_service - .create_role(&state.db, demo_role) - .await?; + let created_role_model = state.role_service.create_role(&state.db, demo_role).await?; let password = "admin123".as_bytes(); let salt = SaltString::from_b64(&state.config.password_salt)?; @@ -304,12 +295,9 @@ pub async fn install_demo_data_api_handler( .create_admin_user(&state.db, creatable_admin_user, logged_in_user) .await?; - println!("Created admin user: {:?}", created_admin_user); - let response = DemoDataViewModel { - status: true - }; + let response = DemoDataViewModel { status: true }; Ok(Json(response)) } diff --git a/src/api/handlers/misc/mod.rs b/src/api/handlers/misc/mod.rs index 37a37ccc..7e6d13ac 100644 --- a/src/api/handlers/misc/mod.rs +++ b/src/api/handlers/misc/mod.rs @@ -1,5 +1,5 @@ +pub mod delete_demo_data_api_handler; pub mod health_check_api_handler; -pub mod openapi_api_handler; pub mod install_demo_data_api_handler; -pub mod delete_demo_data_api_handler; -pub mod testing_api_handler; \ No newline at end of file +pub mod openapi_api_handler; +pub mod testing_api_handler; diff --git a/src/api/handlers/misc/openapi_api_handler.rs b/src/api/handlers/misc/openapi_api_handler.rs index 9a62b09b..86f4301c 100644 --- a/src/api/handlers/misc/openapi_api_handler.rs +++ b/src/api/handlers/misc/openapi_api_handler.rs @@ -1,14 +1,14 @@ -use std::sync::Arc; -use axum::extract::State; -use axum::Json; -use utoipa::OpenApi; -use utoipa::openapi::Server; -use crate::error::Result; use crate::api::handlers::admin_user::admin_user_login_api_handler::LoginResponseData; -use crate::models::admin_user_model::AdminUserModel; -use crate::models::role_model::RoleModel; use crate::api::handlers::admin_user::request::authenticate_admin_user_request::AuthenticateAdminUserRequest; use crate::avored_state::AvoRedState; +use crate::error::Result; +use crate::models::admin_user_model::AdminUserModel; +use crate::models::role_model::RoleModel; +use axum::extract::State; +use axum::Json; +use std::sync::Arc; +use utoipa::openapi::Server; +use utoipa::OpenApi; pub async fn openapi_api_handler( state: State>, @@ -23,7 +23,6 @@ pub async fn openapi_api_handler( Ok(Json(json)) } - #[derive(OpenApi)] #[openapi( paths( diff --git a/src/api/handlers/misc/testing_api_handler.rs b/src/api/handlers/misc/testing_api_handler.rs index 312585ad..f7de45d1 100644 --- a/src/api/handlers/misc/testing_api_handler.rs +++ b/src/api/handlers/misc/testing_api_handler.rs @@ -1,24 +1,22 @@ +use crate::error::Result; use axum::Json; use serde::{Deserialize, Serialize}; -use crate::error::Result; pub async fn testing_api_handler( Json(payload): Json, ) -> Result> { println!("->> {:<12} - testing_api_handler", "HANDLER"); - println!("->> {:<12} - {:?}", "Payload", payload.name); let response = ResponseData { status: true, - data: String::from("ok") + data: String::from("ok"), }; Ok(Json(response)) } - #[derive(Deserialize, Debug)] pub struct TestingRequest { name: String, @@ -44,6 +42,5 @@ pub struct TestingRequest { #[derive(Serialize)] pub struct ResponseData { status: bool, - data: String + data: String, } - diff --git a/src/api/handlers/mod.rs b/src/api/handlers/mod.rs index c0e4bf89..6dae97a3 100644 --- a/src/api/handlers/mod.rs +++ b/src/api/handlers/mod.rs @@ -1,12 +1,13 @@ pub mod admin_user; -pub mod page; -pub mod cms; -pub mod role; pub mod asset; +pub mod cms; +pub mod collection; pub mod component; -pub mod setup; -pub mod setting; -pub mod graphql; -pub mod model; pub mod misc; +pub mod model; +pub mod page; +pub mod role; +pub mod setting; +pub mod setup; +pub mod content; \ No newline at end of file diff --git a/src/api/handlers/model/fetch_model_api_handler.rs b/src/api/handlers/model/fetch_model_api_handler.rs index b2e6ac3d..7d08fb07 100644 --- a/src/api/handlers/model/fetch_model_api_handler.rs +++ b/src/api/handlers/model/fetch_model_api_handler.rs @@ -1,19 +1,21 @@ use std::sync::Arc; use crate::models::model_model::ModelModel; -use crate::{ - avored_state::AvoRedState, error::Result -}; +use crate::{avored_state::AvoRedState, error::Result}; -use axum::{Extension, extract::{Path as AxumPath, State}, Json, response::IntoResponse}; -use serde::Serialize; use crate::error::Error; use crate::models::token_claim_model::LoggedInUser; +use axum::{ + extract::{Path as AxumPath, State}, + response::IntoResponse, + Extension, Json, +}; +use serde::Serialize; pub async fn fetch_model_api_handler( AxumPath(model_id): AxumPath, Extension(logged_in_user): Extension, - state: State> + state: State>, ) -> Result { println!("->> {:<12} - fetch_model_api_handler", "HANDLER"); @@ -25,21 +27,17 @@ pub async fn fetch_model_api_handler( return Err(Error::Forbidden); } - let model_model = state - .model_service - .find_by_id(&state.db, model_id) - .await?; + let model_model = state.model_service.find_by_id(&state.db, model_id).await?; let response = FetchModelResponse { status: true, - model_model + model_model, }; Ok(Json(response)) } - #[derive(Serialize, Debug)] pub struct FetchModelResponse { pub status: bool, - pub model_model: ModelModel -} \ No newline at end of file + pub model_model: ModelModel, +} diff --git a/src/api/handlers/model/mod.rs b/src/api/handlers/model/mod.rs index 6d1a21a0..d2234409 100644 --- a/src/api/handlers/model/mod.rs +++ b/src/api/handlers/model/mod.rs @@ -1,6 +1,6 @@ -pub mod store_model_api_handler; -mod request; -pub mod model_table_api_handler; pub mod fetch_model_api_handler; +pub mod model_table_api_handler; pub mod put_model_identifier_api_handler; -pub mod update_model_api_handler; \ No newline at end of file +mod request; +pub mod store_model_api_handler; +pub mod update_model_api_handler; diff --git a/src/api/handlers/model/model_table_api_handler.rs b/src/api/handlers/model/model_table_api_handler.rs index 24627b25..e619a147 100644 --- a/src/api/handlers/model/model_table_api_handler.rs +++ b/src/api/handlers/model/model_table_api_handler.rs @@ -1,11 +1,11 @@ -use std::sync::Arc; -use axum::extract::{Query, State}; -use axum::{Extension, Json}; use crate::api::handlers::model::request::model_table_request::ModelTableRequest; use crate::avored_state::AvoRedState; use crate::error::{Error, Result}; use crate::models::model_model::ModelPagination; use crate::models::token_claim_model::LoggedInUser; +use axum::extract::{Query, State}; +use axum::{Extension, Json}; +use std::sync::Arc; pub async fn model_table_api_handler( state: State>, @@ -26,7 +26,8 @@ pub async fn model_table_api_handler( let order = query_param.order.unwrap_or(String::from("")); let paginated_data = state .model_service - .paginate(&state.db, current_page, order).await?; + .paginate(&state.db, current_page, order) + .await?; Ok(Json(paginated_data)) } diff --git a/src/api/handlers/model/put_model_identifier_api_handler.rs b/src/api/handlers/model/put_model_identifier_api_handler.rs index 4c184765..ed51b013 100644 --- a/src/api/handlers/model/put_model_identifier_api_handler.rs +++ b/src/api/handlers/model/put_model_identifier_api_handler.rs @@ -1,18 +1,19 @@ use std::sync::Arc; use crate::models::model_model::PutModelIdentifierModel; -use crate::{ - avored_state::AvoRedState, error::Result -}; +use crate::{avored_state::AvoRedState, error::Result}; -use axum::{Extension, extract::{Path as AxumPath, State}, Json}; -use axum::response::IntoResponse; use crate::api::handlers::model::request::put_model_request::PutModelRequest; use crate::error::Error; use crate::models::token_claim_model::LoggedInUser; use crate::models::validation_error::ErrorResponse; -use crate::responses::ApiResponse; use crate::responses::model::PutModelIdentifierResponse; +use crate::responses::ApiResponse; +use axum::response::IntoResponse; +use axum::{ + extract::{Path as AxumPath, State}, + Extension, Json, +}; pub async fn put_model_identifier_api_handler( AxumPath(model_id): AxumPath, @@ -35,17 +36,16 @@ pub async fn put_model_identifier_api_handler( if !error_messages.is_empty() { let error_response = ErrorResponse { status: false, - errors: error_messages + errors: error_messages, }; return Err(Error::BadRequest(error_response)); } - let put_model_identifier = PutModelIdentifierModel { id: model_id, identifier: payload.identifier, - logged_in_username: logged_in_user.email + logged_in_username: logged_in_user.email, }; let updated_model_model = state .model_service @@ -53,13 +53,13 @@ pub async fn put_model_identifier_api_handler( .await?; let updated_model_response = PutModelIdentifierResponse { - model: updated_model_model + model: updated_model_model, }; let api_response = ApiResponse { status: true, - data: updated_model_response + data: updated_model_response, }; Ok(Json(api_response)) -} \ No newline at end of file +} diff --git a/src/api/handlers/model/request/mod.rs b/src/api/handlers/model/request/mod.rs index 69284ebb..339ff44b 100644 --- a/src/api/handlers/model/request/mod.rs +++ b/src/api/handlers/model/request/mod.rs @@ -1,4 +1,4 @@ -pub mod store_model_request; pub mod model_table_request; pub mod put_model_request; -pub mod update_model_request; \ No newline at end of file +pub mod store_model_request; +pub mod update_model_request; diff --git a/src/api/handlers/model/request/put_model_request.rs b/src/api/handlers/model/request/put_model_request.rs index 08f4c0b7..0535731b 100644 --- a/src/api/handlers/model/request/put_model_request.rs +++ b/src/api/handlers/model/request/put_model_request.rs @@ -1,9 +1,9 @@ -use std::sync::Arc; +use crate::avored_state::AvoRedState; +use crate::models::validation_error::{ErrorMessage, Validate}; use axum::extract::State; use rust_i18n::t; use serde::Deserialize; -use crate::avored_state::AvoRedState; -use crate::models::validation_error::{ErrorMessage, Validate}; +use std::sync::Arc; #[derive(Deserialize, Debug, Clone, Default)] pub struct PutModelRequest { @@ -11,13 +11,16 @@ pub struct PutModelRequest { } impl PutModelRequest { - pub async fn validate(&self, state: State>) -> crate::error::Result> { + pub async fn validate( + &self, + state: State>, + ) -> crate::error::Result> { let mut errors: Vec = vec![]; if !self.identifier.required()? { let error_message = ErrorMessage { key: String::from("identifier"), - message: t!("validation_required", attribute = t!("identifier")).to_string() + message: t!("validation_required", attribute = t!("identifier")).to_string(), }; errors.push(error_message); @@ -31,7 +34,7 @@ impl PutModelRequest { if model_model_count.total > 0 { let error_message = ErrorMessage { key: String::from("identifier"), - message: t!("validation_count", attribute = t!("identifier")).to_string() + message: t!("validation_count", attribute = t!("identifier")).to_string(), }; errors.push(error_message); } diff --git a/src/api/handlers/model/request/store_model_request.rs b/src/api/handlers/model/request/store_model_request.rs index 6e406fdf..cb00e8c0 100644 --- a/src/api/handlers/model/request/store_model_request.rs +++ b/src/api/handlers/model/request/store_model_request.rs @@ -6,7 +6,7 @@ use crate::models::validation_error::{ErrorMessage, Validate}; #[derive(Deserialize, Debug, Clone, Default)] pub struct StoreModelRequest { pub name: String, - pub identifier: String + pub identifier: String, } impl StoreModelRequest { @@ -16,7 +16,7 @@ impl StoreModelRequest { if !self.name.required()? { let error_message = ErrorMessage { key: String::from("name"), - message: t!("validation_required", attribute = t!("name")).to_string() + message: t!("validation_required", attribute = t!("name")).to_string(), }; errors.push(error_message); @@ -25,7 +25,7 @@ impl StoreModelRequest { if !self.identifier.required()? { let error_message = ErrorMessage { key: String::from("identifier"), - message: format!("Identifier is a required field {}", t!("identifier")) + message: format!("Identifier is a required field {}", t!("identifier")), }; errors.push(error_message); diff --git a/src/api/handlers/model/request/update_model_request.rs b/src/api/handlers/model/request/update_model_request.rs index 30ed563c..9135aef3 100644 --- a/src/api/handlers/model/request/update_model_request.rs +++ b/src/api/handlers/model/request/update_model_request.rs @@ -15,7 +15,7 @@ impl UpdateModelRequest { if !self.name.required()? { let error_message = ErrorMessage { key: String::from("name"), - message: t!("validation_required", attribute = t!("name")).to_string() + message: t!("validation_required", attribute = t!("name")).to_string(), }; errors.push(error_message); diff --git a/src/api/handlers/model/store_model_api_handler.rs b/src/api/handlers/model/store_model_api_handler.rs index 90fb7c85..535047c1 100644 --- a/src/api/handlers/model/store_model_api_handler.rs +++ b/src/api/handlers/model/store_model_api_handler.rs @@ -1,16 +1,13 @@ use std::sync::Arc; -use crate::error::Error; -use crate::models::validation_error::ErrorResponse; -use crate::{ - avored_state::AvoRedState, error::Result -}; -use axum::{Extension, extract::State, Json}; -use serde::Serialize; use crate::api::handlers::model::request::store_model_request::StoreModelRequest; +use crate::error::Error; use crate::models::model_model::{CreatableModel, ModelModel}; use crate::models::token_claim_model::LoggedInUser; - +use crate::models::validation_error::ErrorResponse; +use crate::{avored_state::AvoRedState, error::Result}; +use axum::{extract::State, Extension, Json}; +use serde::Serialize; pub async fn store_model_api_handler( Extension(logged_in_user): Extension, @@ -32,7 +29,7 @@ pub async fn store_model_api_handler( if !error_messages.is_empty() { let error_response = ErrorResponse { status: false, - errors: error_messages + errors: error_messages, }; return Err(Error::BadRequest(error_response)); @@ -41,7 +38,7 @@ pub async fn store_model_api_handler( let creatable_model = CreatableModel { name: payload.name, identifier: payload.identifier, - logged_in_username: logged_in_user.email + logged_in_username: logged_in_user.email, }; let created_model_model = state @@ -50,7 +47,7 @@ pub async fn store_model_api_handler( .await?; let response = CreatedModelResponse { status: true, - model_model: created_model_model + model_model: created_model_model, }; Ok(Json(response)) @@ -59,5 +56,5 @@ pub async fn store_model_api_handler( #[derive(Serialize, Debug)] pub struct CreatedModelResponse { pub status: bool, - pub model_model: ModelModel + pub model_model: ModelModel, } diff --git a/src/api/handlers/model/update_model_api_handler.rs b/src/api/handlers/model/update_model_api_handler.rs index 616723b3..d46ab4ff 100644 --- a/src/api/handlers/model/update_model_api_handler.rs +++ b/src/api/handlers/model/update_model_api_handler.rs @@ -1,15 +1,19 @@ use std::sync::Arc; +use crate::api::handlers::model::request::update_model_request::UpdateModelRequest; +use crate::models::model_model::ModelModel; +use crate::models::token_claim_model::LoggedInUser; use crate::{ avored_state::AvoRedState, error::{Error, Result}, models::{model_model::UpdatableModelModel, validation_error::ErrorResponse}, }; -use axum::{Extension, extract::{Path as AxumPath, State}, Json, response::IntoResponse}; +use axum::{ + extract::{Path as AxumPath, State}, + response::IntoResponse, + Extension, Json, +}; use serde::Serialize; -use crate::api::handlers::model::request::update_model_request::UpdateModelRequest; -use crate::models::model_model::ModelModel; -use crate::models::token_claim_model::LoggedInUser; pub async fn update_model_api_handler( Extension(logged_in_user): Extension, @@ -32,7 +36,7 @@ pub async fn update_model_api_handler( if !error_messages.is_empty() { let error_response = ErrorResponse { status: false, - errors: error_messages + errors: error_messages, }; return Err(Error::BadRequest(error_response)); @@ -50,7 +54,7 @@ pub async fn update_model_api_handler( let response = UpdatedModelResponse { status: true, - model_model: updated_model_model + model_model: updated_model_model, }; Ok(Json(response)) @@ -59,5 +63,5 @@ pub async fn update_model_api_handler( #[derive(Serialize, Debug)] pub struct UpdatedModelResponse { pub status: bool, - pub model_model: ModelModel + pub model_model: ModelModel, } diff --git a/src/api/handlers/page/delete_page_handler.rs b/src/api/handlers/page/delete_page_handler.rs index ef6249d6..034cbfee 100644 --- a/src/api/handlers/page/delete_page_handler.rs +++ b/src/api/handlers/page/delete_page_handler.rs @@ -1,18 +1,18 @@ -use std::sync::Arc; -use serde::Serialize; -use crate::{ error::Result }; -use axum::{Extension, extract::Path}; -use axum::extract::State; -use axum::http::StatusCode; -use axum::response::IntoResponse; use crate::avored_state::AvoRedState; use crate::error::Error; +use crate::error::Result; use crate::models::token_claim_model::LoggedInUser; +use axum::extract::State; +use axum::http::StatusCode; +use axum::response::IntoResponse; +use axum::{extract::Path, Extension}; +use serde::Serialize; +use std::sync::Arc; pub async fn delete_page_handler( Extension(logged_in_user): Extension, Path(page_id): Path, - state: State> + state: State>, ) -> Result { let has_permission_bool = state .admin_user_service @@ -28,4 +28,4 @@ pub async fn delete_page_handler( #[derive(Serialize, Debug)] pub struct RemovedPageResponse { pub status: bool, -} \ No newline at end of file +} diff --git a/src/api/handlers/page/fetch_page_api_handler.rs b/src/api/handlers/page/fetch_page_api_handler.rs index 08fe541a..3d4570b8 100644 --- a/src/api/handlers/page/fetch_page_api_handler.rs +++ b/src/api/handlers/page/fetch_page_api_handler.rs @@ -1,19 +1,21 @@ use std::sync::Arc; use crate::models::page_model::PageModel; -use crate::{ - avored_state::AvoRedState, error::Result -}; +use crate::{avored_state::AvoRedState, error::Result}; -use axum::{Extension, extract::{Path as AxumPath, State}, Json, response::IntoResponse}; -use serde::Serialize; use crate::error::Error; use crate::models::token_claim_model::LoggedInUser; +use axum::{ + extract::{Path as AxumPath, State}, + response::IntoResponse, + Extension, Json, +}; +use serde::Serialize; pub async fn fetch_page_api_handler( AxumPath(page_id): AxumPath, Extension(logged_in_user): Extension, - state: State> + state: State>, ) -> Result { println!("->> {:<12} - fetch_page_api_handler", "HANDLER"); @@ -25,21 +27,17 @@ pub async fn fetch_page_api_handler( return Err(Error::Forbidden); } - let page_model = state - .page_service - .find_by_id(&state.db, page_id) - .await?; + let page_model = state.page_service.find_by_id(&state.db, page_id).await?; let response = FetchPageResponse { status: true, - page_model + page_model, }; Ok(Json(response)) } - #[derive(Serialize, Debug)] pub struct FetchPageResponse { pub status: bool, - pub page_model: PageModel -} \ No newline at end of file + pub page_model: PageModel, +} diff --git a/src/api/handlers/page/mod.rs b/src/api/handlers/page/mod.rs index 9b251e76..a44a475e 100644 --- a/src/api/handlers/page/mod.rs +++ b/src/api/handlers/page/mod.rs @@ -1,7 +1,7 @@ +pub mod delete_page_handler; +pub mod fetch_page_api_handler; pub mod page_table_api_handler; +pub mod put_page_identifier_api_handler; pub mod request; pub mod store_page_api_handler; pub mod update_page_api_handler; -pub mod fetch_page_api_handler; -pub mod put_page_identifier_api_handler; -pub mod delete_page_handler; \ No newline at end of file diff --git a/src/api/handlers/page/page_table_api_handler.rs b/src/api/handlers/page/page_table_api_handler.rs index affc2587..6c2ecc80 100644 --- a/src/api/handlers/page/page_table_api_handler.rs +++ b/src/api/handlers/page/page_table_api_handler.rs @@ -1,11 +1,11 @@ -use std::sync::Arc; -use axum::extract::{Query, State}; -use axum::{Extension, Json}; use crate::api::handlers::page::request::page_table_request::PageTableRequest; use crate::avored_state::AvoRedState; use crate::error::{Error, Result}; use crate::models::page_model::PagePagination; use crate::models::token_claim_model::LoggedInUser; +use axum::extract::{Query, State}; +use axum::{Extension, Json}; +use std::sync::Arc; pub async fn page_table_api_handler( state: State>, @@ -26,7 +26,8 @@ pub async fn page_table_api_handler( let order = query_param.order.unwrap_or(String::from("")); let page_pagination = state .page_service - .paginate(&state.db, current_page, order).await?; + .paginate(&state.db, current_page, order) + .await?; Ok(Json(page_pagination)) } diff --git a/src/api/handlers/page/put_page_identifier_api_handler.rs b/src/api/handlers/page/put_page_identifier_api_handler.rs index 794e99b9..7effe822 100644 --- a/src/api/handlers/page/put_page_identifier_api_handler.rs +++ b/src/api/handlers/page/put_page_identifier_api_handler.rs @@ -1,18 +1,19 @@ use std::sync::Arc; use crate::models::page_model::PutPageIdentifierModel; -use crate::{ - avored_state::AvoRedState, error::Result -}; +use crate::{avored_state::AvoRedState, error::Result}; -use axum::{Extension, extract::{Path as AxumPath, State}, Json}; -use axum::response::IntoResponse; use crate::api::handlers::page::request::put_page_request::PutPageRequest; use crate::error::Error; use crate::models::token_claim_model::LoggedInUser; use crate::models::validation_error::ErrorResponse; -use crate::responses::ApiResponse; use crate::responses::page::PutPageIdentifierResponse; +use crate::responses::ApiResponse; +use axum::response::IntoResponse; +use axum::{ + extract::{Path as AxumPath, State}, + Extension, Json, +}; pub async fn put_page_identifier_api_handler( AxumPath(page_id): AxumPath, @@ -35,17 +36,16 @@ pub async fn put_page_identifier_api_handler( if !error_messages.is_empty() { let error_response = ErrorResponse { status: false, - errors: error_messages + errors: error_messages, }; return Err(Error::BadRequest(error_response)); } - let put_page_identifier = PutPageIdentifierModel { id: page_id, identifier: payload.identifier, - logged_in_username: logged_in_user.email + logged_in_username: logged_in_user.email, }; let updated_page_model = state .page_service @@ -53,13 +53,13 @@ pub async fn put_page_identifier_api_handler( .await?; let updated_page_response = PutPageIdentifierResponse { - page: updated_page_model + page: updated_page_model, }; let api_response = ApiResponse { status: true, - data: updated_page_response + data: updated_page_response, }; Ok(Json(api_response)) -} \ No newline at end of file +} diff --git a/src/api/handlers/page/request/mod.rs b/src/api/handlers/page/request/mod.rs index 138f53fb..02a15098 100644 --- a/src/api/handlers/page/request/mod.rs +++ b/src/api/handlers/page/request/mod.rs @@ -1,4 +1,4 @@ pub mod page_table_request; +pub mod put_page_request; pub mod store_page_request; pub mod update_page_request; -pub mod put_page_request; \ No newline at end of file diff --git a/src/api/handlers/page/request/put_page_request.rs b/src/api/handlers/page/request/put_page_request.rs index aa09dbee..4d1dfae3 100644 --- a/src/api/handlers/page/request/put_page_request.rs +++ b/src/api/handlers/page/request/put_page_request.rs @@ -1,9 +1,9 @@ -use std::sync::Arc; +use crate::avored_state::AvoRedState; +use crate::models::validation_error::{ErrorMessage, Validate}; use axum::extract::State; use rust_i18n::t; use serde::Deserialize; -use crate::avored_state::AvoRedState; -use crate::models::validation_error::{ErrorMessage, Validate}; +use std::sync::Arc; #[derive(Deserialize, Debug, Clone, Default)] pub struct PutPageRequest { @@ -11,13 +11,16 @@ pub struct PutPageRequest { } impl PutPageRequest { - pub async fn validate(&self, state: State>) -> crate::error::Result> { + pub async fn validate( + &self, + state: State>, + ) -> crate::error::Result> { let mut errors: Vec = vec![]; if !self.identifier.required()? { let error_message = ErrorMessage { key: String::from("identifier"), - message: t!("validation_required", attribute = t!("identifier")).to_string() + message: t!("validation_required", attribute = t!("identifier")).to_string(), }; errors.push(error_message); @@ -31,7 +34,7 @@ impl PutPageRequest { if page_model_count.total > 0 { let error_message = ErrorMessage { key: String::from("identifier"), - message: t!("validation_count", attribute = t!("identifier")).to_string() + message: t!("validation_count", attribute = t!("identifier")).to_string(), }; errors.push(error_message); } diff --git a/src/api/handlers/page/request/store_page_request.rs b/src/api/handlers/page/request/store_page_request.rs index f3cb17c9..8f26db6a 100644 --- a/src/api/handlers/page/request/store_page_request.rs +++ b/src/api/handlers/page/request/store_page_request.rs @@ -1,9 +1,11 @@ -use rust_i18n::t; -use serde::Deserialize; use crate::avored_state::AvoRedState; -use crate::models::validation_error::{ErrorMessage, Validate}; use crate::error::Result; -use crate::models::page_model::{PageDataType, PageFieldContentType, PageFieldData, PageFieldType, PageStatus}; +use crate::models::page_model::{ + PageDataType, PageFieldContentType, PageFieldData, PageFieldType, PageStatus, +}; +use crate::models::validation_error::{ErrorMessage, Validate}; +use rust_i18n::t; +use serde::Deserialize; #[derive(Deserialize, Debug, Clone, Default)] pub struct StorePageRequest { @@ -30,7 +32,7 @@ impl StorePageRequest { if !self.name.required()? { let error_message = ErrorMessage { key: String::from("name"), - message: t!("validation_required", attribute = t!("name")).to_string() + message: t!("validation_required", attribute = t!("name")).to_string(), }; errors.push(error_message); @@ -39,7 +41,7 @@ impl StorePageRequest { if !self.identifier.required()? { let error_message = ErrorMessage { key: String::from("identifier"), - message: t!("validation_required", attribute = t!("identifier")).to_string() + message: t!("validation_required", attribute = t!("identifier")).to_string(), }; errors.push(error_message); @@ -53,7 +55,7 @@ impl StorePageRequest { if page_count.total > 0 { let error_message = ErrorMessage { key: String::from("identifier"), - message: t!("validation_count", attribute = t!("identifier")).to_string() + message: t!("validation_count", attribute = t!("identifier")).to_string(), }; errors.push(error_message); diff --git a/src/api/handlers/page/request/update_page_request.rs b/src/api/handlers/page/request/update_page_request.rs index 83b54362..0599c4e6 100644 --- a/src/api/handlers/page/request/update_page_request.rs +++ b/src/api/handlers/page/request/update_page_request.rs @@ -1,7 +1,9 @@ +use crate::models::page_model::{ + PageDataType, PageFieldContentType, PageFieldData, PageFieldType, PageStatus, +}; +use crate::models::validation_error::{ErrorMessage, Validate}; use rust_i18n::t; use serde::Deserialize; -use crate::models::page_model::{PageDataType, PageFieldContentType, PageFieldType, PageFieldData, PageStatus}; -use crate::models::validation_error::{ErrorMessage, Validate}; #[derive(Deserialize, Debug, Clone, Default)] pub struct UpdatePageRequest { @@ -18,7 +20,7 @@ pub struct UpdatablePageField { pub data_type: PageDataType, pub field_type: PageFieldType, pub field_content: PageFieldContentType, - pub field_data: PageFieldData + pub field_data: PageFieldData, } impl UpdatePageRequest { @@ -27,7 +29,7 @@ impl UpdatePageRequest { if !self.name.required()? { let error_message = ErrorMessage { key: String::from("name"), - message: t!("validation_required", attribute = t!("name")).to_string() + message: t!("validation_required", attribute = t!("name")).to_string(), }; errors.push(error_message); diff --git a/src/api/handlers/page/store_page_api_handler.rs b/src/api/handlers/page/store_page_api_handler.rs index f410195f..4be4c2a1 100644 --- a/src/api/handlers/page/store_page_api_handler.rs +++ b/src/api/handlers/page/store_page_api_handler.rs @@ -1,15 +1,13 @@ use std::sync::Arc; +use crate::api::handlers::page::request::store_page_request::StorePageRequest; use crate::error::Error; use crate::models::page_model::{CreatablePageField, CreatablePageModel, PageModel}; -use crate::models::validation_error::ErrorResponse; -use crate::{ - avored_state::AvoRedState, error::Result -}; -use axum::{Extension, extract::State, Json}; -use crate::api::handlers::page::request::store_page_request::StorePageRequest; use crate::models::token_claim_model::LoggedInUser; +use crate::models::validation_error::ErrorResponse; use crate::responses::ApiResponse; +use crate::{avored_state::AvoRedState, error::Result}; +use axum::{extract::State, Extension, Json}; pub async fn store_page_api_handler( Extension(logged_in_user): Extension, @@ -30,7 +28,7 @@ pub async fn store_page_api_handler( if !error_messages.is_empty() { let error_response = ErrorResponse { status: false, - errors: error_messages + errors: error_messages, }; return Err(Error::BadRequest(error_response)); @@ -41,17 +39,17 @@ pub async fn store_page_api_handler( identifier: payload.identifier, status: payload.status, logged_in_username: logged_in_user.name.clone(), - page_fields: vec![] + page_fields: vec![], }; - for payload_page_field in payload.page_fields { + for payload_page_field in payload.page_fields { let creatable_page_field_model = CreatablePageField { name: payload_page_field.name, identifier: payload_page_field.identifier, data_type: payload_page_field.data_type, field_type: payload_page_field.field_type, field_content: payload_page_field.field_content, - field_data: payload_page_field.field_data + field_data: payload_page_field.field_data, }; creatable_page.page_fields.push(creatable_page_field_model); } @@ -63,7 +61,7 @@ pub async fn store_page_api_handler( let response = ApiResponse { status: true, - data: created_page_model + data: created_page_model, }; Ok(Json(response)) diff --git a/src/api/handlers/page/update_page_api_handler.rs b/src/api/handlers/page/update_page_api_handler.rs index d60203d0..e7981733 100644 --- a/src/api/handlers/page/update_page_api_handler.rs +++ b/src/api/handlers/page/update_page_api_handler.rs @@ -1,16 +1,19 @@ use std::sync::Arc; use crate::error::Error; -use crate::models::page_model::{PageModel, UpdatablePageModel, UpdatablePageField}; +use crate::models::page_model::{PageModel, UpdatablePageField, UpdatablePageModel}; use crate::{ api::handlers::page::request::update_page_request::UpdatePageRequest, - avored_state::AvoRedState, error::Result + avored_state::AvoRedState, error::Result, }; -use axum::{Extension, extract::{Path as AxumPath, State}, Json}; use crate::models::token_claim_model::LoggedInUser; use crate::models::validation_error::ErrorResponse; use crate::responses::ApiResponse; +use axum::{ + extract::{Path as AxumPath, State}, + Extension, Json, +}; pub async fn update_page_api_handler( Extension(logged_in_user): Extension, @@ -33,16 +36,13 @@ pub async fn update_page_api_handler( if !error_messages.is_empty() { let error_response = ErrorResponse { status: false, - errors: error_messages + errors: error_messages, }; return Err(Error::BadRequest(error_response)); } - let page_model = state - .page_service - .find_by_id(&state.db, page_id) - .await?; + let page_model = state.page_service.find_by_id(&state.db, page_id).await?; let mut updatable_page = UpdatablePageModel { id: page_model.id, @@ -52,17 +52,17 @@ pub async fn update_page_api_handler( logged_in_username: logged_in_user.name.clone(), page_fields: vec![], created_at: page_model.created_at, - created_by: page_model.created_by + created_by: page_model.created_by, }; - for payload_page_field in payload.page_fields { + for payload_page_field in payload.page_fields { let page_field_model = UpdatablePageField { name: payload_page_field.name, identifier: payload_page_field.identifier, data_type: payload_page_field.data_type, field_type: payload_page_field.field_type, field_content: payload_page_field.field_content, - field_data: payload_page_field.field_data + field_data: payload_page_field.field_data, }; updatable_page.page_fields.push(page_field_model); } @@ -74,7 +74,7 @@ pub async fn update_page_api_handler( let response = ApiResponse { status: true, - data: created_page_model + data: created_page_model, }; Ok(Json(response)) diff --git a/src/api/handlers/role/fetch_role_api_handler.rs b/src/api/handlers/role/fetch_role_api_handler.rs index 910e2a47..b749cbab 100644 --- a/src/api/handlers/role/fetch_role_api_handler.rs +++ b/src/api/handlers/role/fetch_role_api_handler.rs @@ -1,19 +1,21 @@ use std::sync::Arc; use crate::models::role_model::RoleModel; -use crate::{ - avored_state::AvoRedState, error::Result -}; +use crate::{avored_state::AvoRedState, error::Result}; -use axum::{Extension, extract::{Path as AxumPath, State}, Json, response::IntoResponse}; -use serde::Serialize; use crate::error::Error; use crate::models::token_claim_model::LoggedInUser; +use axum::{ + extract::{Path as AxumPath, State}, + response::IntoResponse, + Extension, Json, +}; +use serde::Serialize; -pub async fn fetch_role_api_handler ( +pub async fn fetch_role_api_handler( AxumPath(role_id): AxumPath, Extension(logged_in_user): Extension, - state: State> + state: State>, ) -> Result { println!("->> {:<12} - fetch_role_api_handler", "HANDLER"); @@ -25,52 +27,50 @@ pub async fn fetch_role_api_handler ( return Err(Error::Forbidden); } - let role_model = state - .role_service - .find_by_id(&state.db, role_id) - .await?; + let role_model = state.role_service.find_by_id(&state.db, role_id).await?; let response = FetchPageResponse { status: true, - role_model + role_model, }; Ok(Json(response)) } - #[derive(Serialize, Debug)] pub struct FetchPageResponse { pub status: bool, - pub role_model: RoleModel + pub role_model: RoleModel, } - #[cfg(test)] mod tests { - use tower::ServiceExt; - use crate::api::rest_api_routes::tests::{get_axum_app, get_login_response, send_get_request, setup_avored_db}; + use crate::api::rest_api_routes::tests::{ + get_axum_app, get_login_response, send_get_request, setup_avored_db, + }; use crate::models::role_model::RolePagination; + use tower::ServiceExt; #[tokio::test] - async fn test_fetch_role_api_handler() -> crate::error::Result<()> - { + async fn test_fetch_role_api_handler() -> crate::error::Result<()> { let (app, _state) = get_axum_app().await?; setup_avored_db(app.clone()).await; let logged_in_user_response = get_login_response(app.clone()).await?; let token = logged_in_user_response.data; - let response = app.oneshot(send_get_request("/api/role", token)).await.unwrap(); + let response = app + .oneshot(send_get_request("/api/role", token)) + .await + .unwrap(); let res_b = response.into_body(); let body = axum::body::to_bytes(res_b, usize::MAX).await.unwrap(); - let body_str: String = String::from_utf8(body.to_vec()) - .expect("Failed to convert body to string"); + let body_str: String = + String::from_utf8(body.to_vec()).expect("Failed to convert body to string"); - let body: RolePagination = serde_json::from_str(&body_str) - .expect("Failed to parse JSON"); + let body: RolePagination = serde_json::from_str(&body_str).expect("Failed to parse JSON"); - assert_eq!(body.data.len() , 1); + assert_eq!(body.data.len(), 1); Ok(()) } -} \ No newline at end of file +} diff --git a/src/api/handlers/role/mod.rs b/src/api/handlers/role/mod.rs index 943c8331..db7cc8ce 100644 --- a/src/api/handlers/role/mod.rs +++ b/src/api/handlers/role/mod.rs @@ -1,7 +1,7 @@ +pub mod fetch_role_api_handler; +pub mod put_role_identifier_api_handler; pub mod request; +pub mod role_option_api_handler; pub mod role_table_api_handler; -pub mod fetch_role_api_handler; pub mod store_role_api_handler; pub mod update_role_api_handler; -pub mod role_option_api_handler; -pub mod put_role_identifier_api_handler; \ No newline at end of file diff --git a/src/api/handlers/role/put_role_identifier_api_handler.rs b/src/api/handlers/role/put_role_identifier_api_handler.rs index 425cb296..b837f77a 100644 --- a/src/api/handlers/role/put_role_identifier_api_handler.rs +++ b/src/api/handlers/role/put_role_identifier_api_handler.rs @@ -1,18 +1,19 @@ use std::sync::Arc; use crate::models::role_model::PutRoleIdentifierModel; -use crate::{ - avored_state::AvoRedState, error::Result -}; +use crate::{avored_state::AvoRedState, error::Result}; -use axum::{Extension, extract::{Path as AxumPath, State}, Json}; -use axum::response::IntoResponse; use crate::api::handlers::role::request::put_role_request::PutRoleRequest; use crate::error::Error; use crate::models::token_claim_model::LoggedInUser; use crate::models::validation_error::ErrorResponse; -use crate::responses::ApiResponse; use crate::responses::role::PutRoleIdentifierResponse; +use crate::responses::ApiResponse; +use axum::response::IntoResponse; +use axum::{ + extract::{Path as AxumPath, State}, + Extension, Json, +}; pub async fn put_role_identifier_api_handler( AxumPath(role_id): AxumPath, @@ -35,17 +36,16 @@ pub async fn put_role_identifier_api_handler( if !error_messages.is_empty() { let error_response = ErrorResponse { status: false, - errors: error_messages + errors: error_messages, }; return Err(Error::BadRequest(error_response)); } - let put_role_identifier = PutRoleIdentifierModel { id: role_id, identifier: payload.identifier, - logged_in_username: logged_in_user.email + logged_in_username: logged_in_user.email, }; let updated_role_model = state .role_service @@ -53,13 +53,13 @@ pub async fn put_role_identifier_api_handler( .await?; let updated_role_response = PutRoleIdentifierResponse { - role: updated_role_model + role: updated_role_model, }; let api_response = ApiResponse { status: true, - data: updated_role_response + data: updated_role_response, }; Ok(Json(api_response)) -} \ No newline at end of file +} diff --git a/src/api/handlers/role/request/mod.rs b/src/api/handlers/role/request/mod.rs index 941cb7de..8d48fe09 100644 --- a/src/api/handlers/role/request/mod.rs +++ b/src/api/handlers/role/request/mod.rs @@ -1,3 +1,3 @@ +pub mod put_role_request; pub mod store_role_request; pub mod update_role_request; -pub mod put_role_request; \ No newline at end of file diff --git a/src/api/handlers/role/request/put_role_request.rs b/src/api/handlers/role/request/put_role_request.rs index 8191b186..15fc3037 100644 --- a/src/api/handlers/role/request/put_role_request.rs +++ b/src/api/handlers/role/request/put_role_request.rs @@ -1,9 +1,9 @@ -use std::sync::Arc; +use crate::avored_state::AvoRedState; +use crate::models::validation_error::{ErrorMessage, Validate}; use axum::extract::State; use rust_i18n::t; use serde::Deserialize; -use crate::avored_state::AvoRedState; -use crate::models::validation_error::{ErrorMessage, Validate}; +use std::sync::Arc; #[derive(Deserialize, Debug, Clone, Default)] pub struct PutRoleRequest { @@ -11,13 +11,16 @@ pub struct PutRoleRequest { } impl PutRoleRequest { - pub async fn validate(&self, state: State>) -> crate::error::Result> { + pub async fn validate( + &self, + state: State>, + ) -> crate::error::Result> { let mut errors: Vec = vec![]; if !self.identifier.required()? { let error_message = ErrorMessage { key: String::from("identifier"), - message: t!("validation_required", attribute = t!("identifier")).to_string() + message: t!("validation_required", attribute = t!("identifier")).to_string(), }; errors.push(error_message); @@ -31,7 +34,7 @@ impl PutRoleRequest { if role_model_count.total > 0 { let error_message = ErrorMessage { key: String::from("identifier"), - message: t!("validation_count", attribute = t!("identifier")).to_string() + message: t!("validation_count", attribute = t!("identifier")).to_string(), }; errors.push(error_message); } diff --git a/src/api/handlers/role/request/store_role_request.rs b/src/api/handlers/role/request/store_role_request.rs index 85b30795..38c44478 100644 --- a/src/api/handlers/role/request/store_role_request.rs +++ b/src/api/handlers/role/request/store_role_request.rs @@ -17,7 +17,7 @@ impl StoreRoleRequest { if !self.name.required()? { let error_message = ErrorMessage { key: String::from("name"), - message: t!("validation_required", attribute = t!("name")).to_string() + message: t!("validation_required", attribute = t!("name")).to_string(), }; errors.push(error_message); @@ -26,7 +26,7 @@ impl StoreRoleRequest { if !self.identifier.required()? { let error_message = ErrorMessage { key: String::from("identifier"), - message: format!("Identifier is a required field {}", t!("identifier")) + message: format!("Identifier is a required field {}", t!("identifier")), }; errors.push(error_message); diff --git a/src/api/handlers/role/request/update_role_request.rs b/src/api/handlers/role/request/update_role_request.rs index be124e52..6d206e51 100644 --- a/src/api/handlers/role/request/update_role_request.rs +++ b/src/api/handlers/role/request/update_role_request.rs @@ -16,7 +16,7 @@ impl UpdateRoleRequest { if !self.name.required()? { let error_message = ErrorMessage { key: String::from("name"), - message: t!("validation_required", attribute = t!("name")).to_string() + message: t!("validation_required", attribute = t!("name")).to_string(), }; errors.push(error_message); diff --git a/src/api/handlers/role/role_option_api_handler.rs b/src/api/handlers/role/role_option_api_handler.rs index c28fea52..b810f255 100644 --- a/src/api/handlers/role/role_option_api_handler.rs +++ b/src/api/handlers/role/role_option_api_handler.rs @@ -1,10 +1,10 @@ -use std::sync::Arc; -use axum::extract::State; -use axum::Json; -use serde::Serialize; use crate::avored_state::AvoRedState; use crate::error::Result; use crate::models::role_model::RoleOptionModel; +use axum::extract::State; +use axum::Json; +use serde::Serialize; +use std::sync::Arc; pub async fn role_option_api_handler( state: State>, @@ -15,7 +15,7 @@ pub async fn role_option_api_handler( let role_option_response = RoleOptionResponse { status: true, - options: roles + options: roles, }; Ok(Json(role_option_response)) @@ -24,5 +24,5 @@ pub async fn role_option_api_handler( #[derive(Serialize, Debug)] pub struct RoleOptionResponse { status: bool, - options: Vec -} \ No newline at end of file + options: Vec, +} diff --git a/src/api/handlers/role/role_table_api_handler.rs b/src/api/handlers/role/role_table_api_handler.rs index 47ea69a1..27c81b93 100644 --- a/src/api/handlers/role/role_table_api_handler.rs +++ b/src/api/handlers/role/role_table_api_handler.rs @@ -1,11 +1,11 @@ -use std::sync::Arc; -use axum::extract::{Query, State}; -use axum::{Extension, Json}; use crate::api::handlers::page::request::page_table_request::PageTableRequest; use crate::avored_state::AvoRedState; use crate::error::{Error, Result}; use crate::models::role_model::RolePagination; use crate::models::token_claim_model::LoggedInUser; +use axum::extract::{Query, State}; +use axum::{Extension, Json}; +use std::sync::Arc; pub async fn role_table_api_handler( state: State>, @@ -23,7 +23,10 @@ pub async fn role_table_api_handler( let current_page = query_param.page.unwrap_or(0); let order = query_param.order.unwrap_or(String::from("")); - let role_pagination = state.role_service.paginate(&state.db, current_page, order).await?; + let role_pagination = state + .role_service + .paginate(&state.db, current_page, order) + .await?; Ok(Json(role_pagination)) } diff --git a/src/api/handlers/role/store_role_api_handler.rs b/src/api/handlers/role/store_role_api_handler.rs index fa66898e..3e60afd9 100644 --- a/src/api/handlers/role/store_role_api_handler.rs +++ b/src/api/handlers/role/store_role_api_handler.rs @@ -1,16 +1,13 @@ use std::sync::Arc; -use crate::error::Error; -use crate::models::validation_error::ErrorResponse; -use crate::{ - avored_state::AvoRedState, error::Result -}; -use axum::{Extension, extract::State, Json}; -use serde::Serialize; use crate::api::handlers::role::request::store_role_request::StoreRoleRequest; +use crate::error::Error; use crate::models::role_model::{CreatableRole, RoleModel}; use crate::models::token_claim_model::LoggedInUser; - +use crate::models::validation_error::ErrorResponse; +use crate::{avored_state::AvoRedState, error::Result}; +use axum::{extract::State, Extension, Json}; +use serde::Serialize; pub async fn store_role_api_handler( Extension(logged_in_user): Extension, @@ -32,7 +29,7 @@ pub async fn store_role_api_handler( if !error_messages.is_empty() { let error_response = ErrorResponse { status: false, - errors: error_messages + errors: error_messages, }; return Err(Error::BadRequest(error_response)); @@ -51,7 +48,7 @@ pub async fn store_role_api_handler( .await?; let response = CreatedRoleResponse { status: true, - role_model: created_role_model + role_model: created_role_model, }; Ok(Json(response)) @@ -60,5 +57,5 @@ pub async fn store_role_api_handler( #[derive(Serialize, Debug)] pub struct CreatedRoleResponse { pub status: bool, - pub role_model: RoleModel + pub role_model: RoleModel, } diff --git a/src/api/handlers/role/update_role_api_handler.rs b/src/api/handlers/role/update_role_api_handler.rs index f8c02973..363e9c78 100644 --- a/src/api/handlers/role/update_role_api_handler.rs +++ b/src/api/handlers/role/update_role_api_handler.rs @@ -1,15 +1,19 @@ use std::sync::Arc; +use crate::api::handlers::role::request::update_role_request::UpdateRoleRequest; +use crate::models::role_model::RoleModel; +use crate::models::token_claim_model::LoggedInUser; use crate::{ avored_state::AvoRedState, error::{Error, Result}, models::{role_model::UpdatableRoleModel, validation_error::ErrorResponse}, }; -use axum::{Extension, extract::{Path as AxumPath, State}, Json, response::IntoResponse}; +use axum::{ + extract::{Path as AxumPath, State}, + response::IntoResponse, + Extension, Json, +}; use serde::Serialize; -use crate::api::handlers::role::request::update_role_request::UpdateRoleRequest; -use crate::models::role_model::RoleModel; -use crate::models::token_claim_model::LoggedInUser; pub async fn update_role_api_handler( Extension(logged_in_user): Extension, @@ -32,7 +36,7 @@ pub async fn update_role_api_handler( if !error_messages.is_empty() { let error_response = ErrorResponse { status: false, - errors: error_messages + errors: error_messages, }; return Err(Error::BadRequest(error_response)); @@ -51,7 +55,7 @@ pub async fn update_role_api_handler( let response = UpdatedRoleResponse { status: true, - role_model: updated_role_model + role_model: updated_role_model, }; Ok(Json(response)) @@ -60,5 +64,5 @@ pub async fn update_role_api_handler( #[derive(Serialize, Debug)] pub struct UpdatedRoleResponse { pub status: bool, - pub role_model: RoleModel + pub role_model: RoleModel, } diff --git a/src/api/handlers/setting/mod.rs b/src/api/handlers/setting/mod.rs index 79384a04..acf2c2bc 100644 --- a/src/api/handlers/setting/mod.rs +++ b/src/api/handlers/setting/mod.rs @@ -1,3 +1,3 @@ +pub mod request; pub mod setting_all_api_handler; pub mod update_setting_all_api_handler; -pub mod request; \ No newline at end of file diff --git a/src/api/handlers/setting/request/mod.rs b/src/api/handlers/setting/request/mod.rs index 34a44a7e..e95d170a 100644 --- a/src/api/handlers/setting/request/mod.rs +++ b/src/api/handlers/setting/request/mod.rs @@ -1 +1 @@ -pub mod update_setting_request; \ No newline at end of file +pub mod update_setting_request; diff --git a/src/api/handlers/setting/request/update_setting_request.rs b/src/api/handlers/setting/request/update_setting_request.rs index 35acc20e..c95c4cb5 100644 --- a/src/api/handlers/setting/request/update_setting_request.rs +++ b/src/api/handlers/setting/request/update_setting_request.rs @@ -1,18 +1,17 @@ +use crate::models::validation_error::{ErrorMessage, Validate}; use rust_i18n::t; use serde::Deserialize; -use crate::models::validation_error::{ErrorMessage, Validate}; #[derive(Deserialize, Debug, Clone, Default)] pub struct UpdateSettingRequest { pub settings: Vec, } - #[derive(Deserialize, Debug, Clone, Default)] pub struct UpdatableSettingRequest { pub id: String, pub identifier: String, - pub value: String + pub value: String, } impl UpdateSettingRequest { pub fn validate(&self) -> crate::error::Result> { @@ -21,7 +20,7 @@ impl UpdateSettingRequest { if !setting_request.id.required()? { let error_message = ErrorMessage { key: format!("settings.{}.id", setting_request.identifier), - message: String::from(t!("validation_required", attribute = t!("id"))) + message: String::from(t!("validation_required", attribute = t!("id"))), }; errors.push(error_message); @@ -29,7 +28,7 @@ impl UpdateSettingRequest { if !setting_request.value.required()? { let error_message = ErrorMessage { key: format!("settings.{}.value", setting_request.identifier), - message: String::from(t!("validation_required", attribute = t!("value"))) + message: String::from(t!("validation_required", attribute = t!("value"))), }; errors.push(error_message); diff --git a/src/api/handlers/setting/setting_all_api_handler.rs b/src/api/handlers/setting/setting_all_api_handler.rs index 6466ad09..3fce22d8 100644 --- a/src/api/handlers/setting/setting_all_api_handler.rs +++ b/src/api/handlers/setting/setting_all_api_handler.rs @@ -1,18 +1,17 @@ -use std::sync::Arc; -use axum::extract::State; -use axum::{Extension, Json}; use crate::avored_state::AvoRedState; use crate::error::{Error, Result}; use crate::models::setting_model::SettingModel; use crate::models::token_claim_model::LoggedInUser; +use axum::extract::State; +use axum::{Extension, Json}; +use std::sync::Arc; pub async fn setting_all_api_handler( Extension(logged_in_user): Extension, - state: State> + state: State>, ) -> Result>> { println!("->> {:<12} - setting_all_api_handler", "HANDLER"); - let has_permission_bool = state .admin_user_service .has_permission(logged_in_user, String::from("get_setting")) diff --git a/src/api/handlers/setting/update_setting_all_api_handler.rs b/src/api/handlers/setting/update_setting_all_api_handler.rs index f0833eea..66a11bbc 100644 --- a/src/api/handlers/setting/update_setting_all_api_handler.rs +++ b/src/api/handlers/setting/update_setting_all_api_handler.rs @@ -1,12 +1,12 @@ -use std::sync::Arc; -use axum::extract::State; -use axum::{Extension, Json}; use crate::api::handlers::setting::request::update_setting_request::UpdateSettingRequest; use crate::avored_state::AvoRedState; use crate::error::{Error, Result}; use crate::models::setting_model::{SettingModel, UpdatableSettingModel}; use crate::models::token_claim_model::LoggedInUser; use crate::models::validation_error::ErrorResponse; +use axum::extract::State; +use axum::{Extension, Json}; +use std::sync::Arc; pub async fn update_setting_all_api_handler( Extension(logged_in_user): Extension, @@ -28,7 +28,7 @@ pub async fn update_setting_all_api_handler( if !error_messages.is_empty() { let error_response = ErrorResponse { status: false, - errors: error_messages + errors: error_messages, }; return Err(Error::BadRequest(error_response)); @@ -38,11 +38,13 @@ pub async fn update_setting_all_api_handler( let updatable_setting_model = UpdatableSettingModel { id: updatable_setting.id, value: updatable_setting.value, - logged_in_username: logged_in_user.email.clone() + logged_in_username: logged_in_user.email.clone(), }; - state.setting_service.update_setting(&state.db, updatable_setting_model).await?; + state + .setting_service + .update_setting(&state.db, updatable_setting_model) + .await?; } - Ok(Json(state.setting_service.all(&state.db).await?)) } diff --git a/src/api/handlers/setup/mod.rs b/src/api/handlers/setup/mod.rs index d34e59f2..22cc082d 100644 --- a/src/api/handlers/setup/mod.rs +++ b/src/api/handlers/setup/mod.rs @@ -1 +1 @@ -pub mod post_setup_avored_handler; \ No newline at end of file +pub mod post_setup_avored_handler; diff --git a/src/api/handlers/setup/post_setup_avored_handler.rs b/src/api/handlers/setup/post_setup_avored_handler.rs index eff91d35..d5d71e64 100644 --- a/src/api/handlers/setup/post_setup_avored_handler.rs +++ b/src/api/handlers/setup/post_setup_avored_handler.rs @@ -1,19 +1,12 @@ use std::{collections::BTreeMap, sync::Arc}; -use crate::{ - avored_state::AvoRedState, - error::Result, -}; -use argon2::{ - password_hash::SaltString, - Argon2, PasswordHasher, -}; -use axum::{extract::State, Json, response::IntoResponse}; -use email_address::EmailAddress; -use serde::{Deserialize, Serialize}; use crate::error::Error; use crate::models::validation_error::{ErrorMessage, ErrorResponse}; - +use crate::{avored_state::AvoRedState, error::Result}; +use argon2::{password_hash::SaltString, Argon2, PasswordHasher}; +use axum::{extract::State, response::IntoResponse, Json}; +use email_address::EmailAddress; +use serde::{Deserialize, Serialize}; pub async fn post_setup_avored_handler( state: State>, @@ -26,13 +19,12 @@ pub async fn post_setup_avored_handler( if !error_messages.is_empty() { let error_response = ErrorResponse { status: false, - errors: error_messages + errors: error_messages, }; return Err(Error::BadRequest(error_response)); } - let sql = " REMOVE TABLE settings; @@ -157,6 +149,27 @@ pub async fn post_setup_avored_handler( DEFINE TABLE fields; + REMOVE TABLE collections; + DEFINE TABLE collections; + + DEFINE FIELD name ON TABLE collections TYPE string; + DEFINE FIELD identifier ON TABLE collections TYPE string; + DEFINE FIELD created_by ON TABLE collections TYPE string; + DEFINE FIELD updated_by ON TABLE collections TYPE string; + DEFINE FIELD created_at ON TABLE collections TYPE datetime; + DEFINE FIELD updated_at ON TABLE collections TYPE datetime; + DEFINE INDEX collections_identifier_index ON TABLE collections COLUMNS identifier UNIQUE; + DEFINE INDEX collections_identifier_index ON TABLE collections COLUMNS identifier UNIQUE; + + CREATE collections CONTENT { + name: 'Pages', + identifier: 'pages', + created_by: $email, + updated_by: $email, + created_at: time::now(), + updated_at: time::now() + }; + "; let password = payload.password.as_bytes(); @@ -184,9 +197,7 @@ pub async fn post_setup_avored_handler( println!(); println!("Migrate fresh done!"); - let response = SetupViewModel { - status: true - }; + let response = SetupViewModel { status: true }; Ok(Json(response)) } @@ -197,8 +208,6 @@ pub struct SetupViewModel { pub status: bool, } - - #[derive(Deserialize, Debug, Clone)] pub struct SetupAvoRedRequest { // #[validate(email(message = "The email field must be a valid email address."))] @@ -214,16 +223,16 @@ impl SetupAvoRedRequest { if self.email.is_empty() { let error_message = ErrorMessage { key: String::from("email"), - message: String::from("Email is a required field") + message: String::from("Email is a required field"), }; errors.push(error_message); } - if ! EmailAddress::is_valid(&self.email) { + if !EmailAddress::is_valid(&self.email) { let error_message = ErrorMessage { key: String::from("email"), - message: String::from("Invalid email address") + message: String::from("Invalid email address"), }; errors.push(error_message); @@ -232,7 +241,7 @@ impl SetupAvoRedRequest { if self.password.is_empty() { let error_message = ErrorMessage { key: String::from("password"), - message: String::from("Password is a required field") + message: String::from("Password is a required field"), }; errors.push(error_message); @@ -240,4 +249,4 @@ impl SetupAvoRedRequest { Ok(errors) } -} \ No newline at end of file +} diff --git a/src/api/mod.rs b/src/api/mod.rs index f1361dea..c76421f0 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -1,2 +1,2 @@ pub mod handlers; -pub mod rest_api_routes; \ No newline at end of file +pub mod rest_api_routes; diff --git a/src/api/rest_api_routes.rs b/src/api/rest_api_routes.rs index 3732a3ab..8d783bf8 100644 --- a/src/api/rest_api_routes.rs +++ b/src/api/rest_api_routes.rs @@ -1,15 +1,13 @@ -use std::sync::Arc; -use axum::{middleware, routing::get, Extension, Router}; -use axum::routing::{delete, on, post, put, MethodFilter}; -use axum::http::header::{AUTHORIZATION, CONTENT_TYPE}; -use axum::http::HeaderValue; -use juniper::{EmptyMutation, EmptySubscription}; -use crate::avored_state::AvoRedState; -use crate::middleware::require_jwt_authentication::require_jwt_authentication; -use tower_http::cors::CorsLayer; +use crate::api::handlers::cms::all_pages_cms_api_handler::all_pages_cms_api_handler; +use crate::api::handlers::cms::sent_contact_us_email_handler::sent_contact_us_email_handler; +use crate::api::handlers::collection::collection_table_api_handler::collection_table_api_handler; +use crate::api::handlers::collection::fetch_collection_api_handler::fetch_collection_api_handler; +use crate::api::handlers::collection::store_collection_api_handler::store_collection_api_handler; +use crate::api::handlers::collection::update_collection_api_handler::update_collection_api_handler; +use crate::api::handlers::misc::delete_demo_data_api_handler::delete_demo_data_api_handler; +use crate::api::handlers::misc::install_demo_data_api_handler::install_demo_data_api_handler; +use crate::api::handlers::misc::testing_api_handler::testing_api_handler; use crate::api::handlers::{ - - page::delete_page_handler::delete_page_handler, admin_user::admin_user_forgot_password_api_handler::admin_user_forgot_password_api_handler, admin_user::admin_user_login_api_handler::admin_user_login_api_handler, admin_user::admin_user_reset_password_api_handler::admin_user_reset_password_api_handler, @@ -20,12 +18,26 @@ use crate::api::handlers::{ admin_user::store_admin_user_api_handler::store_admin_user_api_handler, admin_user::update_admin_user_api_handler::update_admin_user_api_handler, asset::asset_table_api_handler::asset_table_api_handler, + asset::create_folder_api_handler::create_folder_api_handler, + asset::delete_asset_api_handler::delete_asset_api_handler, + asset::delete_folder_api_handler::delete_folder_api_handler, + asset::rename_asset_api_handler::rename_asset_api_handler, asset::store_asset_api_handler::store_asset_api_handler, cms::fetch_page_cms_api_handler::fetch_page_cms_api_handler, + component::component_all_api_handler::component_all_api_handler, component::component_table_api_handler::component_table_api_handler, component::fetch_component_api_handler::fetch_component_api_handler, + component::put_component_identifier_api_handler::put_component_identifier_api_handler, component::store_component_api_handler::store_component_api_handler, component::update_component_api_handler::update_component_api_handler, + misc::health_check_api_handler::health_check_api_handler, + misc::openapi_api_handler::openapi_api_handler, + model::fetch_model_api_handler::fetch_model_api_handler, + model::model_table_api_handler::model_table_api_handler, + model::put_model_identifier_api_handler::put_model_identifier_api_handler, + model::store_model_api_handler::store_model_api_handler, + model::update_model_api_handler::update_model_api_handler, + page::delete_page_handler::delete_page_handler, page::fetch_page_api_handler::fetch_page_api_handler, page::page_table_api_handler::page_table_api_handler, page::put_page_identifier_api_handler::put_page_identifier_api_handler, @@ -40,46 +52,40 @@ use crate::api::handlers::{ setting::setting_all_api_handler::setting_all_api_handler, setting::update_setting_all_api_handler::update_setting_all_api_handler, setup::post_setup_avored_handler::post_setup_avored_handler, - misc::health_check_api_handler::health_check_api_handler, - misc::openapi_api_handler::openapi_api_handler, - component::component_all_api_handler::component_all_api_handler, - component::put_component_identifier_api_handler::put_component_identifier_api_handler, - model::fetch_model_api_handler::fetch_model_api_handler, - model::model_table_api_handler::model_table_api_handler, - model::put_model_identifier_api_handler::put_model_identifier_api_handler, - model::store_model_api_handler::store_model_api_handler, - model::update_model_api_handler::update_model_api_handler, - asset::create_folder_api_handler::create_folder_api_handler, - asset::delete_folder_api_handler::delete_folder_api_handler, - asset::rename_asset_api_handler::rename_asset_api_handler, - asset::delete_asset_api_handler::delete_asset_api_handler, }; -use crate::api::handlers::cms::all_pages_cms_api_handler::all_pages_cms_api_handler; -use crate::api::handlers::cms::sent_contact_us_email_handler::sent_contact_us_email_handler; -use crate::api::handlers::graphql::graphql_api_handler::graphql_api_handler; -use crate::api::handlers::misc::delete_demo_data_api_handler::delete_demo_data_api_handler; -use crate::api::handlers::misc::install_demo_data_api_handler::install_demo_data_api_handler; -use crate::api::handlers::misc::testing_api_handler::testing_api_handler; +use crate::avored_state::AvoRedState; +use crate::middleware::require_jwt_authentication::require_jwt_authentication; use crate::middleware::validate_cms_authentication::validate_cms_authentication; -use crate::providers::avored_graphql_provider::AvoRedGraphqlSchema; -use crate::query::AvoRedQuery; +use axum::http::header::{AUTHORIZATION, CONTENT_TYPE}; +use axum::http::HeaderValue; +use axum::routing::{delete, post, put}; +use axum::{middleware, routing::get, Router}; +use std::sync::Arc; +use tower_http::cors::CorsLayer; +use crate::api::handlers::collection::collection_all_api_handler::collection_all_api_handler; +use crate::api::handlers::collection::put_collection_identifier_api_handler::put_collection_identifier_api_handler; +use crate::api::handlers::content::content_table_api_handler::content_table_api_handler; +use crate::api::handlers::content::fetch_content_api_handler::fetch_content_api_handler; +use crate::api::handlers::content::put_content_identifier_api_handler::put_content_identifier_api_handler; +use crate::api::handlers::content::store_content_api_handler::store_content_api_handler; +use crate::api::handlers::content::update_content_api_handler::update_content_api_handler; pub fn rest_api_routes(state: Arc) -> Router { - Router::new() .merge(admin_api_routes(state.clone())) .merge(cms_api_routes(state.clone())) - } - // Ideally cms routes will have all the frontend api calls in future more api will end points will be added fn cms_api_routes(state: Arc) -> Router { let cors = get_cors_urls(state.clone()); Router::new() - .route("/cms/page/:page_id", get(fetch_page_cms_api_handler)) + .route("/cms/page/{page_id}", get(fetch_page_cms_api_handler)) .route("/cms/page", get(all_pages_cms_api_handler)) - .route("/cms/sent-contact-us-email", post(sent_contact_us_email_handler)) + .route( + "/cms/sent-contact-us-email", + post(sent_contact_us_email_handler), + ) .route_layer(middleware::from_fn_with_state( state.clone(), validate_cms_authentication, @@ -90,58 +96,115 @@ fn cms_api_routes(state: Arc) -> Router { fn admin_api_routes(state: Arc) -> Router { let cors = get_cors_urls(state.clone()); - let schema = AvoRedGraphqlSchema::new( - AvoRedQuery, - EmptyMutation::new(), - EmptySubscription::new() - ); Router::new() .route("/api/component", get(component_table_api_handler)) .route("/api/component", post(store_component_api_handler)) - .route("/api/component/:component_id", get(fetch_component_api_handler)) - .route("/api/component/:component_id", put(update_component_api_handler)) - .route("/api/put-component-identifier/:page_id", put(put_component_identifier_api_handler)) + .route( + "/api/component/{component_id}", + get(fetch_component_api_handler), + ) + .route( + "/api/component/{component_id}", + put(update_component_api_handler), + ) + .route( + "/api/put-component-identifier/{page_id}", + put(put_component_identifier_api_handler), + ) .route("/api/asset", get(asset_table_api_handler)) .route("/api/asset", post(store_asset_api_handler)) - .route("/api/rename-asset/:asset_id", post(rename_asset_api_handler)) + .route( + "/api/rename-asset/{asset_id}", + post(rename_asset_api_handler), + ) .route("/api/create-folder", post(create_folder_api_handler)) - .route("/api/delete-folder/:asset_id", delete(delete_folder_api_handler)) - .route("/api/delete-asset/:asset_id", delete(delete_asset_api_handler)) + .route( + "/api/delete-folder/{asset_id}", + delete(delete_folder_api_handler), + ) + .route( + "/api/delete-asset/{asset_id}", + delete(delete_asset_api_handler), + ) .route("/api/role-options", get(role_option_api_handler)) .route("/api/role", get(role_table_api_handler)) .route("/api/role", post(store_role_api_handler)) - .route("/api/role/:role_id", get(fetch_role_api_handler)) - .route("/api/put-role-identifier/:role_id", put(put_role_identifier_api_handler)) - .route("/api/role/:role_id", put(update_role_api_handler)) + .route("/api/role/{role_id}", get(fetch_role_api_handler)) + .route( + "/api/put-role-identifier/{role_id}", + put(put_role_identifier_api_handler), + ) + .route("/api/role/{role_id}", put(update_role_api_handler)) .route("/api/admin-user", get(admin_user_table_api_handler)) .route("/api/admin-user", post(store_admin_user_api_handler)) .route("/api/change-password", post(change_password_api_handler)) - .route("/api/admin-user/:admin_user_id", put(update_admin_user_api_handler)) + .route( + "/api/admin-user/{admin_user_id}", + put(update_admin_user_api_handler), + ) .route("/api/logged-in-user", get(logged_in_user_api_handler)) - .route("/api/admin-user/:admin_user_id", get(fetch_admin_user_api_handler)) + .route( + "/api/admin-user/{admin_user_id}", + get(fetch_admin_user_api_handler), + ) + .route("/api/collection", get(collection_table_api_handler)) + .route("/api/collection", post(store_collection_api_handler)) + .route( + "/api/collection/{collection_id}", + get(fetch_collection_api_handler), + ) + .route( + "/api/collection/{collection_id}", + put(update_collection_api_handler), + ) + .route( + "/api/put-collection-identifier/{collection_id}", + put(put_collection_identifier_api_handler), + ) + .route("/api/collection-all", get(collection_all_api_handler)) .route("/api/model", get(model_table_api_handler)) .route("/api/model", post(store_model_api_handler)) - .route("/api/model/:model_id", put(update_model_api_handler)) - .route("/api/model/:model_id", get(fetch_model_api_handler)) - .route("/api/put-model-identifier/:model_id", put(put_model_identifier_api_handler)) + .route("/api/model/{model_id}", put(update_model_api_handler)) + .route("/api/model/{model_id}", get(fetch_model_api_handler)) + .route( + "/api/put-model-identifier/{model_id}", + put(put_model_identifier_api_handler), + ) + .route("/api/content/{content_type}", get(content_table_api_handler)) + .route("/api/content/{content_type}/{content_id}", get(fetch_content_api_handler)) + .route("/api/content/{content_type}/{content_id}", put(update_content_api_handler)) + .route( + "/api/put-content-identifier/{content_type}/{content_id}", + put(put_content_identifier_api_handler), + ) + .route("/api/content", post(store_content_api_handler)) .route("/api/page", get(page_table_api_handler)) .route("/api/page", post(store_page_api_handler)) - .route("/api/page/:page_id", put(update_page_api_handler)) - .route("/api/page/:page_id", get(fetch_page_api_handler)) - .route("/api/page/:page_id", delete(delete_page_handler)) - .route("/api/put-page-identifier/:page_id", put(put_page_identifier_api_handler)) + .route("/api/page/{page_id}", put(update_page_api_handler)) + .route("/api/page/{page_id}", get(fetch_page_api_handler)) + .route("/api/page/{page_id}", delete(delete_page_handler)) + .route( + "/api/put-page-identifier/{page_id}", + put(put_page_identifier_api_handler), + ) .route("/api/component-all", get(component_all_api_handler)) .route("/api/openapi.json", get(openapi_api_handler)) .route("/api/setting", get(setting_all_api_handler)) .route("/api/setting", post(update_setting_all_api_handler)) - .route("/api/install-demo-data", post(install_demo_data_api_handler)) + .route( + "/api/install-demo-data", + post(install_demo_data_api_handler), + ) .route("/api/delete-demo-data", post(delete_demo_data_api_handler)) // .route("/test", get(test_handler)) - .route("/graphql", on( - MethodFilter::GET.or(MethodFilter::POST), - graphql_api_handler, - ),) + // .route( + // "/graphql", + // on( + // MethodFilter::GET.or(MethodFilter::POST), + // graphql_api_handler, + // ), + // ) .route_layer(middleware::from_fn_with_state( state.clone(), require_jwt_authentication, @@ -150,15 +213,20 @@ fn admin_api_routes(state: Arc) -> Router { .route("/api/setup", post(post_setup_avored_handler)) .route("/api/login", post(admin_user_login_api_handler)) .route("/api/testing", post(testing_api_handler)) - .route("/api/reset-password", post(admin_user_reset_password_api_handler)) - .route("/api/forgot-password", post(admin_user_forgot_password_api_handler)) + .route( + "/api/reset-password", + post(admin_user_reset_password_api_handler), + ) + .route( + "/api/forgot-password", + post(admin_user_forgot_password_api_handler), + ) .with_state(state) .layer(cors) - .layer(Extension(Arc::new(schema))) } fn get_cors_urls(state: Arc) -> CorsLayer { - let mut origins: Vec = vec![]; + let mut origins: Vec = vec![]; for origin in &state.config.cors_allowed_app_url { origins.push(HeaderValue::from_str(origin).unwrap()); } @@ -178,24 +246,23 @@ fn get_cors_urls(state: Arc) -> CorsLayer { #[cfg(test)] pub mod tests { - use std::env; - use std::sync::Arc; - use axum::body::Body; - use axum::http::{self, header, Request, StatusCode}; - use axum::Router; - use jsonwebtoken::{encode, EncodingKey, Header}; - use tower::ServiceExt; use crate::api::handlers::admin_user::admin_user_login_api_handler::LoginResponseData; use crate::api::handlers::setup::post_setup_avored_handler::SetupViewModel; use crate::avored_state::AvoRedState; use crate::error::Result; use crate::models::admin_user_model::AdminUserModel; use crate::models::token_claim_model::TokenClaims; + use axum::body::Body; + use axum::http::{self, header, Request, StatusCode}; + use axum::Router; + use jsonwebtoken::{encode, EncodingKey, Header}; + use std::env; + use std::sync::Arc; + use tower::ServiceExt; use super::rest_api_routes; pub fn send_get_request(uri: &str, token: String) -> Request { - Request::builder() .uri(uri) .header(header::AUTHORIZATION, format!("Bearer {token}")) @@ -214,34 +281,31 @@ pub mod tests { .unwrap() } - pub async fn setup_avored_db(app: Router) { + pub async fn setup_avored_db(app: Router) { let payload = Body::from( r#"{ "email": "admin@admin.com", "password": "admin123" }"#, ); - let expected_response = SetupViewModel { - status: true - }; - let response = app.oneshot(send_post_request("/api/setup", payload)).await.unwrap(); - + let expected_response = SetupViewModel { status: true }; + let response = app + .oneshot(send_post_request("/api/setup", payload)) + .await + .unwrap(); assert_eq!(response.status(), StatusCode::OK); - let res_b = response.into_body(); let body = axum::body::to_bytes(res_b, usize::MAX).await.unwrap(); - let body_str = String::from_utf8(body.to_vec()) - .expect("Failed to convert body to string"); + let body_str = String::from_utf8(body.to_vec()).expect("Failed to convert body to string"); - let body: SetupViewModel = serde_json::from_str(&body_str) - .expect("Failed to parse JSON"); + let body: SetupViewModel = serde_json::from_str(&body_str).expect("Failed to parse JSON"); assert_eq!(body, expected_response); } - pub async fn get_login_response(app: Router) -> Result { + pub async fn get_login_response(app: Router) -> Result { let payload = Body::from( r#"{ "email": "admin@admin.com", @@ -249,17 +313,19 @@ pub mod tests { }"#, ); - let response = app.oneshot(send_post_request("/api/login", payload)).await.unwrap(); + let response = app + .oneshot(send_post_request("/api/login", payload)) + .await + .unwrap(); assert_eq!(response.status(), StatusCode::OK); let res_b = response.into_body(); let body = axum::body::to_bytes(res_b, usize::MAX).await.unwrap(); - let body_str: String = String::from_utf8(body.to_vec()) - .expect("Failed to convert body to string"); + let body_str: String = + String::from_utf8(body.to_vec()).expect("Failed to convert body to string"); - - let body: LoginResponseData = serde_json::from_str(&body_str) - .expect("Failed to parse JSON"); + let body: LoginResponseData = + serde_json::from_str(&body_str).expect("Failed to parse JSON"); Ok(body) } @@ -273,7 +339,7 @@ pub mod tests { let claims: TokenClaims = TokenClaims { sub: admin_user_model.clone().id, name: admin_user_model.clone().full_name, - email:admin_user_model.clone().email, + email: admin_user_model.clone().email, admin_user_model: admin_user_model.clone(), exp, iat, @@ -282,19 +348,24 @@ pub mod tests { &Header::default(), &claims, &EncodingKey::from_secret(state.config.jwt_secret_key.as_ref()), - ).unwrap()) + ) + .unwrap()) } - pub async fn get_axum_app() -> Result<(Router, Arc)> - { + pub async fn get_axum_app() -> Result<(Router, Arc)> { env::set_var("AVORED_DATABASE_NAMESPACE", "public_test"); env::set_var("AVORED_DATABASE_NAME", "avored_cms_test"); env::set_var("AVORED_DATABASE_FOLDER_NAME", "memory"); + env::set_var( + "AVORED_PASSWORD_SALT", + "UnitTestUnitTestUnitTestUnitTestUnitTestUnitTestUnitTestUnitTest", + ); - env::set_var("AVORED_PASSWORD_SALT", "UnitTestUnitTestUnitTestUnitTestUnitTestUnitTestUnitTestUnitTest"); - - env::set_var("AVORED_JWT_SECRET", "UnitTestUnitTestUnitTestUnitTestUnitTestUnitTestUnitTestUnitTest"); + env::set_var( + "AVORED_JWT_SECRET", + "UnitTestUnitTestUnitTestUnitTestUnitTestUnitTestUnitTestUnitTest", + ); env::set_var("AVORED_JWT_EXPIRED_IN", "60"); env::set_var("AVORED_JWT_MAXAGE", "60"); diff --git a/src/avored_state.rs b/src/avored_state.rs index 91666315..652088a3 100644 --- a/src/avored_state.rs +++ b/src/avored_state.rs @@ -3,20 +3,24 @@ use crate::providers::avored_config_provider::AvoRedConfigProvider; use crate::providers::avored_database_provider::{AvoRedDatabaseProvider, DB}; use crate::providers::avored_template_provider::AvoRedTemplateProvider; use crate::repositories::admin_user_repository::AdminUserRepository; -use crate::repositories::component_repository::ComponentRepository; -use crate::repositories::page_repository::PageRepository; -use crate::repositories::role_repository::RoleRepository; -use crate::services::admin_user_service::AdminUserService; -use crate::services::component_service::ComponentService; -use crate::services::page_service::PageService; -use crate::services::role_service::RoleService; use crate::repositories::asset_repository::AssetRepository; +use crate::repositories::collection_repository::CollectionRepository; +use crate::repositories::component_repository::ComponentRepository; +use crate::repositories::content_repository::ContentRepository; use crate::repositories::model_repository::ModelRepository; +use crate::repositories::page_repository::PageRepository; use crate::repositories::password_reset_repository::PasswordResetRepository; +use crate::repositories::role_repository::RoleRepository; use crate::repositories::setting_repository::SettingRepository; +use crate::services::admin_user_service::AdminUserService; use crate::services::asset_service::AssetService; use crate::services::cms_service::CmsService; +use crate::services::collection_service::CollectionService; +use crate::services::component_service::ComponentService; +use crate::services::content_service::ContentService; use crate::services::model_service::ModelService; +use crate::services::page_service::PageService; +use crate::services::role_service::RoleService; use crate::services::setting_service::SettingService; pub struct AvoRedState { @@ -30,20 +34,19 @@ pub struct AvoRedState { pub asset_service: AssetService, pub setting_service: SettingService, pub model_service: ModelService, - pub cms_service: CmsService + pub cms_service: CmsService, + pub collection_service: CollectionService, + pub content_service: ContentService, } -impl juniper::Context for AvoRedState{} - impl AvoRedState { pub async fn new() -> Result { let avored_config_provider = AvoRedConfigProvider::register()?; let avored_database_provider = AvoRedDatabaseProvider::register(avored_config_provider.clone()).await?; - let avored_template_provider = AvoRedTemplateProvider::register( - avored_config_provider.clone() - ).await?; + let avored_template_provider = + AvoRedTemplateProvider::register(avored_config_provider.clone()).await?; let model_repository = ModelRepository::new(); let admin_user_repository = AdminUserRepository::new(); @@ -53,8 +56,14 @@ impl AvoRedState { let asset_repository = AssetRepository::new(); let password_reset_repository = PasswordResetRepository::new(); let setting_repository = SettingRepository::new(); + let collection_repository = CollectionRepository::new(); + let content_repository = ContentRepository::new(); - let admin_user_service = AdminUserService::new(admin_user_repository, role_repository.clone(), password_reset_repository.clone())?; + let admin_user_service = AdminUserService::new( + admin_user_repository, + role_repository.clone(), + password_reset_repository.clone(), + )?; let role_service = RoleService::new(role_repository)?; let component_service = ComponentService::new(component_repository)?; let page_service = PageService::new(page_repository)?; @@ -62,6 +71,8 @@ impl AvoRedState { let setting_service = SettingService::new(setting_repository)?; let model_service = ModelService::new(model_repository)?; let cms_service = CmsService::new()?; + let collection_service = CollectionService::new(collection_repository)?; + let content_service = ContentService::new(content_repository)?; Ok(AvoRedState { config: avored_config_provider, @@ -74,7 +85,9 @@ impl AvoRedState { asset_service, setting_service, model_service, - cms_service + cms_service, + collection_service, + content_service, }) } } diff --git a/src/error.rs b/src/error.rs index ecf380d8..92ca5e55 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,15 +1,15 @@ -use std::num::ParseIntError; +use crate::models::validation_error::{ErrorMessage, ErrorResponse}; +use axum::extract::multipart::MultipartError; use axum::{ http::StatusCode, response::{IntoResponse, Response}, }; -use axum::extract::multipart::MultipartError; use handlebars::{RenderError, TemplateError}; use lettre::address::AddressError; use rust_i18n::t; use serde::Serialize; +use std::num::ParseIntError; use tracing::log::error; -use crate::models::validation_error::{ErrorMessage, ErrorResponse}; pub type Result = core::result::Result; @@ -27,7 +27,7 @@ pub enum Error { NotFound(String), - Forbidden + Forbidden, } impl core::fmt::Display for Error { @@ -52,7 +52,6 @@ impl From for Error { } } - impl From for Error { fn from(val: MultipartError) -> Self { error!("there is an issue with multipart error: {val:?}"); @@ -94,7 +93,6 @@ impl From for Error { } } - impl From for Error { fn from(actual_error: TemplateError) -> Self { error!("there is an issue while registering the handlebar template with avored: {actual_error:?}"); @@ -109,7 +107,6 @@ impl From for Error { } } - impl From for Error { fn from(actual_error: AddressError) -> Self { error!("there is an issue while parsing email address: {actual_error:?}"); @@ -117,7 +114,6 @@ impl From for Error { } } - impl From for Error { fn from(actual_error: lettre::error::Error) -> Self { error!("there is an issue lettre error: {actual_error:?}"); @@ -127,8 +123,6 @@ impl From for Error { impl IntoResponse for Error { fn into_response(self) -> Response { - - // Create a placeholder Axum response. // let mut response = StatusCode::INTERNAL_SERVER_ERROR.into_response(); // let mut response = self { @@ -138,50 +132,45 @@ impl IntoResponse for Error { // response.extensions_mut().insert(response); match self { - Error::BadRequest(str) => { - (StatusCode::BAD_REQUEST, str).into_response() - }, + Error::BadRequest(str) => (StatusCode::BAD_REQUEST, str).into_response(), Error::Authentication => { let mut errors: Vec = vec![]; let error_message = ErrorMessage { key: String::from("email"), - message: String::from(t!("email_password_not_matched")) + message: String::from(t!("email_password_not_matched")), }; errors.push(error_message); let error_response = ErrorResponse { status: false, - errors + errors, }; (StatusCode::UNAUTHORIZED, error_response).into_response() - }, + } Error::Forbidden => { let mut errors: Vec = vec![]; let error_message = ErrorMessage { key: String::from("email"), - message: String::from(t!("admin_user_forbidden")) + message: String::from(t!("admin_user_forbidden")), }; errors.push(error_message); let error_response = ErrorResponse { status: false, - errors + errors, }; (StatusCode::FORBIDDEN, error_response).into_response() - }, - Error::NotFound(msg) => { - (StatusCode::NOT_FOUND, msg).into_response() - }, - _ => (StatusCode::INTERNAL_SERVER_ERROR, "test 500").into_response() + } + Error::NotFound(msg) => (StatusCode::NOT_FOUND, msg).into_response(), + _ => (StatusCode::INTERNAL_SERVER_ERROR, "test 500").into_response(), } } } impl IntoResponse for ErrorResponse { fn into_response(self) -> Response { - - let validation_errors = match serde_json::to_string(&self) { + let validation_errors = match serde_json::to_string(&self) { Ok(str) => str, - _ => "validation error 400.".to_string() + _ => "validation error 400.".to_string(), }; (StatusCode::BAD_REQUEST, validation_errors).into_response() diff --git a/src/main.rs b/src/main.rs index e13da141..0cff90fe 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,30 +1,26 @@ extern crate core; +use crate::api::rest_api_routes::rest_api_routes; +use crate::{avored_state::AvoRedState, error::Result}; +use axum::extract::DefaultBodyLimit; use axum::Router; use std::{fs::File, path::Path, sync::Arc}; -use axum::extract::DefaultBodyLimit; use tokio::net::TcpListener; use tower_http::services::ServeDir; use tracing::info; use tracing_subscriber::{ filter, prelude::__tracing_subscriber_SubscriberExt, util::SubscriberInitExt, Layer, }; -use crate::{ - avored_state::AvoRedState, - error::Result -}; -use crate::api::rest_api_routes::rest_api_routes; const PER_PAGE: i64 = 10; -mod models; mod api; +mod avored_state; +mod error; mod middleware; +mod models; mod providers; mod repositories; -mod services; pub mod responses; -mod avored_state; -mod error; -mod query; +mod services; rust_i18n::i18n!("resources/locales"); @@ -37,8 +33,7 @@ async fn main() -> Result<()> { let app = Router::new() .merge(rest_api_routes(state.clone())) .nest_service("/public", static_routing_service) - .layer(DefaultBodyLimit::max(104857600)) - ; + .layer(DefaultBodyLimit::max(104857600)); println!(r" _ ____ _ "); println!(r" / \__ _____ | _ \ ___ __| |"); @@ -54,7 +49,7 @@ async fn main() -> Result<()> { // let addr = SocketAddr::from(([127, 0, 0, 1], 8080)); let listener = TcpListener::bind("0.0.0.0:8081").await.unwrap(); info!("{:<12} - on {:?}\n", "LISTENING", listener.local_addr()); - axum::serve(listener , app.into_make_service()) + axum::serve(listener, app.into_make_service()) .await .unwrap(); // endregion: --- Start Server @@ -100,4 +95,4 @@ fn init_log() { .init(); tracing::info!(target: "metrics::cool_stuff_count", value = 42); -} \ No newline at end of file +} diff --git a/src/middleware/require_jwt_authentication.rs b/src/middleware/require_jwt_authentication.rs index c2d918b8..a98cfb81 100644 --- a/src/middleware/require_jwt_authentication.rs +++ b/src/middleware/require_jwt_authentication.rs @@ -1,15 +1,15 @@ -use std::sync::Arc; -use axum::{http::Request, Json, middleware::Next}; use axum::body::Body; use axum::extract::State; use axum::http::{header, StatusCode}; use axum::response::IntoResponse; +use axum::{http::Request, middleware::Next, Json}; +use std::sync::Arc; +use crate::avored_state::AvoRedState; +use crate::models::token_claim_model::{LoggedInUser, TokenClaims}; use axum_extra::extract::CookieJar; use jsonwebtoken::{decode, DecodingKey, Validation}; use serde::Serialize; -use crate::avored_state::AvoRedState; -use crate::models::token_claim_model::{LoggedInUser, TokenClaims}; #[derive(Debug, Serialize, Default)] pub struct ErrorResponse { @@ -17,7 +17,7 @@ pub struct ErrorResponse { pub message: String, } -pub async fn require_jwt_authentication ( +pub async fn require_jwt_authentication( state: State>, // Extension(ctx): Extension>, cookie_jar: CookieJar, @@ -35,7 +35,7 @@ pub async fn require_jwt_authentication ( if auth_value.starts_with("Bearer ") { match auth_value.strip_prefix("Bearer ") { Some(auth) => Some(auth.to_owned()), - _ => None + _ => None, } } else { None @@ -60,22 +60,24 @@ pub async fn require_jwt_authentication ( &DecodingKey::from_secret(secret.as_ref()), &Validation::default(), ) - .map_err(|_| { - let json_error = ErrorResponse { - status: false, - message: "Invalid token".to_string(), - }; - (StatusCode::UNAUTHORIZED, Json(json_error)) - })? - .claims; - let file_exist = tokio::fs::try_exists("public/install_demo").await.unwrap_or(false); + .map_err(|_| { + let json_error = ErrorResponse { + status: false, + message: "Invalid token".to_string(), + }; + (StatusCode::UNAUTHORIZED, Json(json_error)) + })? + .claims; + let file_exist = tokio::fs::try_exists("public/install_demo") + .await + .unwrap_or(false); let logged_in_user = LoggedInUser { id: claims.sub, name: claims.name, email: claims.email, demo_data_status: file_exist, - admin_user_model: claims.admin_user_model + admin_user_model: claims.admin_user_model, }; req.extensions_mut().insert(logged_in_user); diff --git a/src/middleware/validate_cms_authentication.rs b/src/middleware/validate_cms_authentication.rs index 0f5692b5..54cf7105 100644 --- a/src/middleware/validate_cms_authentication.rs +++ b/src/middleware/validate_cms_authentication.rs @@ -1,22 +1,21 @@ -use std::sync::Arc; +use crate::avored_state::AvoRedState; +use crate::middleware::require_jwt_authentication::ErrorResponse; +use crate::models::setting_model::SettingModel; use axum::body::Body; use axum::extract::State; use axum::http::{header, Request, StatusCode}; -use axum::Json; use axum::middleware::Next; use axum::response::IntoResponse; +use axum::Json; use axum_extra::extract::CookieJar; -use crate::avored_state::AvoRedState; -use crate::middleware::require_jwt_authentication::ErrorResponse; -use crate::models::setting_model::SettingModel; +use std::sync::Arc; -pub async fn validate_cms_authentication ( +pub async fn validate_cms_authentication( state: State>, cookie_jar: CookieJar, req: Request, next: Next, ) -> Result)> { - let token = cookie_jar .get("token") .map(|cookie| cookie.value().to_string()) @@ -28,7 +27,7 @@ pub async fn validate_cms_authentication ( if auth_value.starts_with("Bearer ") { match auth_value.strip_prefix("Bearer ") { Some(auth) => Some(auth.to_owned()), - _ => None + _ => None, } } else { None @@ -36,7 +35,6 @@ pub async fn validate_cms_authentication ( }) }); - let token = token.ok_or_else(|| { let json_error = ErrorResponse { status: false, @@ -47,17 +45,16 @@ pub async fn validate_cms_authentication ( let cms_token_setting_model = state .setting_service .find_by_identifier(&state.db, String::from("auth_cms_token")) - .await.unwrap_or_else(|_err| { - SettingModel::default() - }); + .await + .unwrap_or_else(|_err| SettingModel::default()); if cms_token_setting_model.value.is_empty() | cms_token_setting_model.value.ne(&token) { let json_error = ErrorResponse { status: false, message: "please provide valid token".to_string(), }; - return Err((StatusCode::UNAUTHORIZED, Json(json_error))) + return Err((StatusCode::UNAUTHORIZED, Json(json_error))); }; Ok(next.run(req).await) -} \ No newline at end of file +} diff --git a/src/models/admin_user_model.rs b/src/models/admin_user_model.rs index 587d7eff..061d8914 100644 --- a/src/models/admin_user_model.rs +++ b/src/models/admin_user_model.rs @@ -1,8 +1,8 @@ use crate::error::{Error, Result}; +use crate::models::role_model::RoleModel; use serde::{Deserialize, Serialize}; use surrealdb::sql::{Datetime, Object, Value}; use utoipa::ToSchema; -use crate::models::role_model::RoleModel; use super::{BaseModel, Pagination}; @@ -41,31 +41,27 @@ impl TryFrom for AdminUserModel { let is_super_admin = val.get("is_super_admin").get_bool()?; let roles = match val.get("roles") { - Some(val) => { - - match val.clone() { - Value::Array(v) => { - let mut arr = Vec::new(); + Some(val) => match val.clone() { + Value::Array(v) => { + let mut arr = Vec::new(); - for array in v.into_iter() { - let object = match array.clone() { - Value::Object(v) => v, - _ => surrealdb::sql::Object::default(), - }; + for array in v.into_iter() { + let object = match array.clone() { + Value::Object(v) => v, + _ => surrealdb::sql::Object::default(), + }; - let role_model: RoleModel = object.try_into()?; + let role_model: RoleModel = object.try_into()?; - arr.push(role_model) - } - arr + arr.push(role_model) } - _ => Vec::new(), + arr } - } + _ => Vec::new(), + }, None => Vec::new(), }; - let created_at = val.get("created_at").get_datetime()?; let updated_at = val.get("updated_at").get_datetime()?; let created_by = val.get("created_by").get_string()?; @@ -82,7 +78,7 @@ impl TryFrom for AdminUserModel { updated_at, created_by, updated_by, - roles + roles, }) } } @@ -95,7 +91,7 @@ pub struct CreatableAdminUserModel { pub profile_image: String, pub is_super_admin: bool, pub logged_in_username: String, - pub role_ids: Vec + pub role_ids: Vec, } #[derive(Serialize, Debug, Deserialize, Clone)] @@ -105,7 +101,7 @@ pub struct UpdatableAdminUserModel { pub profile_image: String, pub is_super_admin: bool, pub logged_in_username: String, - pub role_ids: Vec + pub role_ids: Vec, } #[derive(Serialize, Debug, Deserialize, Clone, Default)] diff --git a/src/models/asset_model.rs b/src/models/asset_model.rs index ad518267..8924c7db 100644 --- a/src/models/asset_model.rs +++ b/src/models/asset_model.rs @@ -1,7 +1,7 @@ - use crate::error::{Error, Result}; +use super::{BaseModel, Pagination}; +use crate::error::{Error, Result}; use serde::{Deserialize, Serialize}; use surrealdb::sql::{Datetime, Object, Value}; -use super::{BaseModel, Pagination}; #[derive(Serialize, Debug, Deserialize, Clone, Default)] pub struct AssetModel { @@ -21,24 +21,16 @@ pub struct AssetModel { #[serde(untagged)] pub enum MetaDataType { // values can be folder color or no of files - FolderTypeMetaData { - color: String - }, + FolderTypeMetaData { color: String }, // file type might have a metadata as // file_type, file_size - FileTypeMetaData { - file_type: String - } + FileTypeMetaData { file_type: String }, } impl MetaDataType { pub fn get_file_metadata(&self) -> FileTypeMetaDataStruct { match self.to_owned() { - MetaDataType::FileTypeMetaData {file_type} => { - FileTypeMetaDataStruct { - file_type - } - }, + MetaDataType::FileTypeMetaData { file_type } => FileTypeMetaDataStruct { file_type }, MetaDataType::FolderTypeMetaData { color } => { let _ = color; FileTypeMetaDataStruct::default() @@ -47,10 +39,8 @@ impl MetaDataType { } pub fn get_folder_metadata(&self) -> FolderTypeMetaDataStruct { match self.to_owned() { - MetaDataType::FolderTypeMetaData {color} => { - FolderTypeMetaDataStruct {color} - }, - MetaDataType::FileTypeMetaData { file_type} => { + MetaDataType::FolderTypeMetaData { color } => FolderTypeMetaDataStruct { color }, + MetaDataType::FileTypeMetaData { file_type } => { let _ = file_type; FolderTypeMetaDataStruct::default() } @@ -60,17 +50,19 @@ impl MetaDataType { #[derive(Deserialize, Debug, Clone, Serialize, Default)] pub struct FileTypeMetaDataStruct { - pub file_type: String + pub file_type: String, } #[derive(Deserialize, Debug, Clone, Serialize, Default)] pub struct FolderTypeMetaDataStruct { - pub color: String + pub color: String, } impl Default for MetaDataType { fn default() -> MetaDataType { - MetaDataType::FolderTypeMetaData {color: String::from("")} + MetaDataType::FolderTypeMetaData { + color: String::from(""), + } } } @@ -89,33 +81,33 @@ impl TryFrom for AssetModel { let metadata = match asset_type.as_str() { "FILE" => { let object = match val.get("metadata") { - Some(val) => { - match val.clone() { - Value::Object(v) => v, - _ => Object::default(), - } - } + Some(val) => match val.clone() { + Value::Object(v) => v, + _ => Object::default(), + }, None => Object::default(), }; let file_metadata: FileTypeMetaDataStruct = object.try_into()?; MetaDataType::FileTypeMetaData { - file_type: file_metadata.file_type + file_type: file_metadata.file_type, } - }, + } "FOLDER" => { let object = match val.get("metadata") { - Some(val) => { - match val.clone() { - Value::Object(v) => v, - _ => Object::default(), - } - } + Some(val) => match val.clone() { + Value::Object(v) => v, + _ => Object::default(), + }, None => Object::default(), }; let folder_metadata: FolderTypeMetaDataStruct = object.try_into()?; - MetaDataType::FolderTypeMetaData {color: folder_metadata.color} + MetaDataType::FolderTypeMetaData { + color: folder_metadata.color, + } + } + _ => MetaDataType::FolderTypeMetaData { + color: String::from("text-gray-400"), }, - _ => MetaDataType::FolderTypeMetaData {color: String::from("text-gray-400")} }; let new_path = String::from(""); @@ -135,23 +127,19 @@ impl TryFrom for AssetModel { } } -impl TryFrom for FileTypeMetaDataStruct { +impl TryFrom for FileTypeMetaDataStruct { type Error = Error; fn try_from(val: Object) -> Result { let file_type = val.get("file_type").get_string()?; - Ok(FileTypeMetaDataStruct { - file_type - }) + Ok(FileTypeMetaDataStruct { file_type }) } } -impl TryFrom for FolderTypeMetaDataStruct { +impl TryFrom for FolderTypeMetaDataStruct { type Error = Error; fn try_from(val: Object) -> Result { let color = val.get("color").get_string()?; - Ok(FolderTypeMetaDataStruct { - color - }) + Ok(FolderTypeMetaDataStruct { color }) } } @@ -161,7 +149,6 @@ pub struct AssetPagination { pub pagination: Pagination, } - #[derive(Serialize, Debug, Deserialize, Clone, Default)] pub struct CreatableAssetModel { pub logged_in_username: String, @@ -170,4 +157,3 @@ pub struct CreatableAssetModel { pub asset_type: String, pub metadata: MetaDataType, } - diff --git a/src/models/collection_model.rs b/src/models/collection_model.rs new file mode 100644 index 00000000..a016f41f --- /dev/null +++ b/src/models/collection_model.rs @@ -0,0 +1,66 @@ +use crate::error::Error; +use crate::models::{BaseModel, Pagination}; +use serde::{Deserialize, Serialize}; +use surrealdb::sql::{Datetime, Object}; + +#[derive(Serialize, Debug, Deserialize, Clone, Default)] +pub struct CollectionModel { + pub id: String, + pub name: String, + pub identifier: String, + pub created_at: Datetime, + pub updated_at: Datetime, + pub created_by: String, + pub updated_by: String, +} + +#[derive(Serialize, Debug, Deserialize, Clone, Default)] +pub struct CollectionPagination { + pub data: Vec, + pub pagination: Pagination, +} + +#[derive(Serialize, Debug, Deserialize, Clone)] +pub struct CreatableCollection { + pub name: String, + pub identifier: String, + pub logged_in_username: String, +} + +#[derive(Serialize, Debug, Deserialize, Clone)] +pub struct UpdatableCollection { + pub id: String, + pub name: String, + pub logged_in_username: String, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct PutCollectionIdentifierModel { + pub id: String, + pub identifier: String, + pub logged_in_username: String, +} + + +impl TryFrom for CollectionModel { + type Error = Error; + fn try_from(val: Object) -> crate::error::Result { + let id = val.get("id").get_id()?; + let name = val.get("name").get_string()?; + let identifier = val.get("identifier").get_string()?; + let created_at = val.get("created_at").get_datetime()?; + let updated_at = val.get("updated_at").get_datetime()?; + let created_by = val.get("created_by").get_string()?; + let updated_by = val.get("updated_by").get_string()?; + + Ok(CollectionModel { + id, + name, + identifier, + created_at, + updated_at, + created_by, + updated_by, + }) + } +} diff --git a/src/models/component_model.rs b/src/models/component_model.rs index 44716332..6d5d63de 100644 --- a/src/models/component_model.rs +++ b/src/models/component_model.rs @@ -1,7 +1,7 @@ +use super::{BaseModel, Pagination}; use crate::error::{Error, Result}; use serde::{Deserialize, Serialize}; use surrealdb::sql::{Datetime, Object, Value}; -use super::{BaseModel, Pagination}; #[derive(Serialize, Debug, Deserialize, Clone, Default)] pub struct ComponentModel { @@ -21,7 +21,7 @@ pub struct ComponentElementModel { pub identifier: String, pub element_type: String, pub element_data_type: String, - pub element_data: Option> + pub element_data: Option>, } impl TryFrom for ComponentModel { @@ -36,27 +36,24 @@ impl TryFrom for ComponentModel { let updated_by = val.get("updated_by").get_string()?; let elements = match val.get("elements") { - Some(val) => { + Some(val) => match val.clone() { + Value::Array(v) => { + let mut arr = Vec::new(); - match val.clone() { - Value::Array(v) => { - let mut arr = Vec::new(); + for array in v.into_iter() { + let object = match array.clone() { + Value::Object(v) => v, + _ => surrealdb::sql::Object::default(), + }; - for array in v.into_iter() { - let object = match array.clone() { - Value::Object(v) => v, - _ => surrealdb::sql::Object::default(), - }; + let component_element_model: ComponentElementModel = object.try_into()?; - let component_element_model: ComponentElementModel = object.try_into()?; - - arr.push(component_element_model) - } - arr + arr.push(component_element_model) } - _ => Vec::new(), + arr } - } + _ => Vec::new(), + }, None => Vec::new(), }; @@ -73,37 +70,32 @@ impl TryFrom for ComponentModel { } } - impl TryFrom for ComponentElementModel { type Error = Error; fn try_from(val: Object) -> Result { - let name = val.get("name").get_string()?; let identifier = val.get("identifier").get_string()?; let element_type = val.get("element_type").get_string()?; let element_data_type = val.get("element_data_type").get_string()?; let element_data = match val.get("element_data") { - Some(val) => { - - match val.clone() { - Value::Array(v) => { - let mut arr = Vec::new(); + Some(val) => match val.clone() { + Value::Array(v) => { + let mut arr = Vec::new(); - for array in v.into_iter() { - let object = match array.clone() { - Value::Object(v) => v, - _ => surrealdb::sql::Object::default(), - }; + for array in v.into_iter() { + let object = match array.clone() { + Value::Object(v) => v, + _ => surrealdb::sql::Object::default(), + }; - let field_data_model: ComponentElementDataModel = object.try_into()?; + let field_data_model: ComponentElementDataModel = object.try_into()?; - arr.push(field_data_model) - } - arr + arr.push(field_data_model) } - _ => Vec::new(), + arr } - } + _ => Vec::new(), + }, None => Vec::new(), }; @@ -112,42 +104,30 @@ impl TryFrom for ComponentElementModel { identifier, element_type, element_data_type, - element_data :Some(element_data) + element_data: Some(element_data), }) } } - - impl TryFrom for ComponentElementDataModel { type Error = Error; fn try_from(val: Object) -> Result { let label = match val.get("label") { - Some(val) => { - - match val.clone() { - Value::Strand(v) => v.as_string(), - _ => String::from(""), - } - } + Some(val) => match val.clone() { + Value::Strand(v) => v.as_string(), + _ => String::from(""), + }, None => String::from(""), }; let value = match val.get("value") { - Some(val) => { - - match val.clone() { - Value::Strand(v) => v.as_string(), - _ => String::from(""), - } - } + Some(val) => match val.clone() { + Value::Strand(v) => v.as_string(), + _ => String::from(""), + }, None => String::from(""), }; - - Ok(ComponentElementDataModel { - label, - value - }) + Ok(ComponentElementDataModel { label, value }) } } @@ -156,10 +136,9 @@ pub struct CreatableComponent { pub name: String, pub identifier: String, pub logged_in_username: String, - pub elements: Vec + pub elements: Vec, } - #[derive(Serialize, Debug, Deserialize, Clone)] pub struct CreatableComponentElementModel { pub name: String, @@ -169,12 +148,11 @@ pub struct CreatableComponentElementModel { pub element_data: Option>, } - #[derive(Serialize, Deserialize, Debug, Clone)] pub struct PutComponentIdentifierModel { pub id: String, pub identifier: String, - pub logged_in_username: String + pub logged_in_username: String, } #[derive(Serialize, Debug, Deserialize, Clone)] @@ -182,10 +160,9 @@ pub struct UpdatableComponentModel { pub id: String, pub name: String, pub logged_in_username: String, - pub elements: Vec + pub elements: Vec, } - #[derive(Serialize, Debug, Deserialize, Clone)] pub struct UpdatableComponentElementModel { pub name: String, diff --git a/src/models/content_model.rs b/src/models/content_model.rs new file mode 100644 index 00000000..0fc07160 --- /dev/null +++ b/src/models/content_model.rs @@ -0,0 +1,271 @@ +use std::collections::BTreeMap; +use serde::{Deserialize, Serialize}; +use surrealdb::sql::{Datetime, Object, Value}; +use crate::error::{Error, Result}; +use crate::models::{BaseModel, Pagination}; + + +// region: Content model structs and enums + +#[derive(Serialize, Debug, Deserialize, Clone, Default)] +pub struct ContentPagination { + pub data: Vec, + pub pagination: Pagination, +} +#[derive(Serialize, Debug, Deserialize, Clone, Default)] +pub struct ContentModel { + pub id: String, + pub name: String, + pub identifier: String, + pub content_fields: Vec, + pub created_at: Datetime, + pub updated_at: Datetime, + pub created_by: String, + pub updated_by: String, +} + +#[derive(Serialize, Debug, Deserialize, Clone, Default)] +pub struct ContentFieldModel { + pub name: String, + pub identifier: String, + pub data_type: ContentDataType, + pub field_type: ContentFieldType, + pub field_content: ContentFieldContentType, +} + +#[derive(Deserialize, Debug, Clone, Serialize)] +#[serde(untagged)] +pub enum ContentDataType { + Text(String), +} + +#[derive(Deserialize, Debug, Clone, Serialize, Default)] +pub enum ContentFieldType { + #[default] + Text +} + +#[derive(Deserialize, Debug, Clone, Serialize)] +#[serde(untagged)] +pub enum ContentFieldContentType { + ContentTextType { text_value: ContentTextType }, +} + +#[derive(Deserialize, Debug, Clone, Serialize, Default)] +pub struct ContentTextType { + pub text_value: String, +} + + +#[derive(Serialize, Debug, Deserialize, Clone)] +pub struct CreatableContentModel { + pub name: String, + pub identifier: String, + pub logged_in_username: String, + pub content_type: String, + pub content_fields: Vec, +} + +#[derive(Serialize, Debug, Deserialize, Clone)] +pub struct CreatableContentField { + pub name: String, + pub identifier: String, + pub data_type: ContentDataType, + pub field_type: ContentFieldType, + pub field_content: ContentFieldContentType, +} + + +#[derive(Serialize, Debug, Deserialize, Clone)] +pub struct UpdatableContentModel { + pub id: String, + pub name: String, + pub identifier: String, + pub content_type: String, + pub logged_in_username: String, + pub created_at: Datetime, + pub created_by: String, + pub content_fields: Vec, +} + +#[derive(Serialize, Debug, Deserialize, Clone)] +pub struct UpdatableContentField { + pub name: String, + pub identifier: String, + pub data_type: ContentDataType, + pub field_type: ContentFieldType, + pub field_content: ContentFieldContentType, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct PutContentIdentifierModel { + pub id: String, + pub identifier: String, + pub collection_type: String, + pub logged_in_username: String, +} + +// endregion: Content model structs and enums + + +// region: impl Default for content model enums + +impl Default for ContentDataType { + fn default() -> ContentDataType { + ContentDataType::Text("TEXT".to_string()) + } +} + +impl Default for ContentFieldContentType { + fn default() -> ContentFieldContentType { + ContentFieldContentType::ContentTextType { + text_value: ContentTextType::default(), + } + } +} + +// endregion: impl Default for content model enums + + + +// region: impl surreal Value for content model structs + +impl TryFrom for Value { + type Error = Error; + fn try_from(val: ContentTextType) -> Result { + let val_val: BTreeMap = + [("text_value".into(), val.text_value.into())].into(); + + Ok(val_val.into()) + } +} + + + +// endregion: impl surreal Value for content model structs + + + +// region: impl surreal Object for content model structs + + +impl TryFrom for ContentModel { + type Error = Error; + fn try_from(val: Object) -> Result { + let id = val.get("id").get_id()?; + let name = val.get("name").get_string()?; + let identifier = val.get("identifier").get_string()?; + + + let content_fields = match val.get("content_fields") { + Some(val) => match val.clone() { + Value::Array(v) => { + let mut arr = Vec::new(); + + for array in v.into_iter() { + let object = match array.clone() { + Value::Object(v) => v, + _ => Object::default(), + }; + + let content_field: ContentFieldModel = object.try_into()?; + + arr.push(content_field) + } + arr + } + _ => Vec::new(), + }, + None => Vec::new(), + }; + + let created_at = val.get("created_at").get_datetime()?; + let updated_at = val.get("updated_at").get_datetime()?; + let created_by = val.get("created_by").get_string()?; + let updated_by = val.get("updated_by").get_string()?; + + Ok(ContentModel { + id, + name, + identifier, + content_fields, + created_at, + updated_at, + created_by, + updated_by, + }) + } +} + +impl TryFrom for ContentFieldModel { + type Error = Error; + fn try_from(val: Object) -> Result { + let name = val.get("name").get_string()?; + let identifier = val.get("identifier").get_string()?; + let data_type_str = val.get("data_type").get_string()?; + + let data_type = match data_type_str.as_str() { + "TEXT" => ContentDataType::Text("TEXT".to_string()), + _ => ContentDataType::default(), + }; + + let field_type_str = val.get("field_type").get_string()?; + let field_type = match field_type_str.as_str() { + "Text" => ContentFieldType::Text, + + _ => ContentFieldType::default(), + }; + + let field_content = match data_type_str.as_str() { + "TEXT" => { + let options = match val.get("field_content") { + Some(val) => { + let object = match val.clone() { + Value::Object(v) => v, + _ => Object::default(), + }; + + // println!("before test {:?}", object); + let option: ContentTextType = object.try_into()?; + // println!("test {:?}", option); + + option + } + None => ContentTextType::default(), + }; + + ContentFieldContentType::ContentTextType { + text_value: options, + } + } + + _ => ContentFieldContentType::default(), + }; + + + Ok(ContentFieldModel { + name, + identifier, + data_type, + field_type, + field_content, + }) + } +} + +impl TryFrom for ContentTextType { + type Error = Error; + fn try_from(val: Object) -> Result { + let value = val.get("text_value").get_string()?; + Ok(ContentTextType { text_value: value }) + } +} + + +// endregion: impl surreal Object for content model structs + + +// region: content model creatable and updatable structs + + +// endregion: content model creatable and updatable structs \ No newline at end of file diff --git a/src/models/mod.rs b/src/models/mod.rs index cc2db996..a3060568 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -1,18 +1,20 @@ use crate::error::{Error, Result}; use serde::{Deserialize, Serialize}; -use surrealdb::sql::{Datetime, Object, Value}; use surrealdb::sql::Value::{Bool, Number}; +use surrealdb::sql::{Datetime, Object, Value}; pub mod admin_user_model; +pub mod asset_model; +pub mod collection_model; pub mod component_model; +pub mod model_model; pub mod page_model; +pub mod password_rest_model; pub mod role_model; -pub mod asset_model; +pub mod setting_model; pub mod token_claim_model; pub mod validation_error; -pub mod password_rest_model; -pub mod setting_model; -pub mod model_model; +pub mod content_model; #[derive(Serialize, Debug, Deserialize, Clone, Default)] pub struct Pagination { @@ -32,7 +34,7 @@ pub struct ModelCount { pub total: i64, } -pub trait BaseModel { +pub trait BaseModel { fn get_id(&self) -> Result; fn get_string(&self) -> Result; fn get_datetime(&self) -> Result; @@ -42,7 +44,6 @@ pub trait BaseModel { // fn get_array(&self) -> Result>; } impl BaseModel for Option<&Value> { - fn get_id(&self) -> Result { let value = match self.to_owned() { Some(val) => match val.clone() { @@ -82,12 +83,10 @@ impl BaseModel for Option<&Value> { fn get_bool(&self) -> Result { let value = match self.to_owned() { - Some(val) => { - match val.clone() { - Bool(v) => v, - _ => false, - } - } + Some(val) => match val.clone() { + Bool(v) => v, + _ => false, + }, None => false, }; @@ -136,13 +135,10 @@ impl TryFrom for ModelCount { type Error = Error; fn try_from(val: Object) -> Result { let count = match val.get("count") { - Some(val) => { - - match val.clone() { - Value::Number(v) => v, - _ => surrealdb::sql::Number::Int(0), - } - } + Some(val) => match val.clone() { + Value::Number(v) => v, + _ => surrealdb::sql::Number::Int(0), + }, None => surrealdb::sql::Number::Int(0), }; diff --git a/src/models/model_model.rs b/src/models/model_model.rs index 42e90a5c..ef4e5006 100644 --- a/src/models/model_model.rs +++ b/src/models/model_model.rs @@ -1,7 +1,7 @@ +use super::{BaseModel, Pagination}; use crate::error::{Error, Result}; use serde::{Deserialize, Serialize}; use surrealdb::sql::{Datetime, Object}; -use super::{BaseModel, Pagination}; #[derive(Serialize, Debug, Deserialize, Clone, Default)] pub struct ModelModel { @@ -11,7 +11,7 @@ pub struct ModelModel { pub created_at: Datetime, pub updated_at: Datetime, pub created_by: String, - pub updated_by: String + pub updated_by: String, } #[derive(Serialize, Debug, Deserialize, Clone, Default)] @@ -20,20 +20,18 @@ pub struct ModelPagination { pub pagination: Pagination, } - #[derive(Serialize, Deserialize, Debug, Clone)] pub struct PutModelIdentifierModel { pub id: String, pub identifier: String, - pub logged_in_username: String + pub logged_in_username: String, } - #[derive(Serialize, Debug, Deserialize, Clone)] pub struct UpdatableModelModel { pub id: String, pub name: String, - pub logged_in_username: String + pub logged_in_username: String, } impl TryFrom for ModelModel { @@ -54,7 +52,7 @@ impl TryFrom for ModelModel { created_at, updated_at, created_by, - updated_by + updated_by, }) } } diff --git a/src/models/page_model.rs b/src/models/page_model.rs index 97702f2c..b79db2c5 100644 --- a/src/models/page_model.rs +++ b/src/models/page_model.rs @@ -1,9 +1,8 @@ -use std::collections::BTreeMap; +use super::{BaseModel, Pagination}; use crate::error::{Error, Result}; use serde::{Deserialize, Serialize}; +use std::collections::BTreeMap; use surrealdb::sql::{Datetime, Object, Value}; -use super::{BaseModel, Pagination}; - // region: Page model structs and enums @@ -24,10 +23,9 @@ pub struct PageModel { pub enum PageStatus { #[default] Draft, - Published + Published, } - #[derive(Serialize, Debug, Deserialize, Clone, Default)] pub struct PageFieldModel { pub name: String, @@ -35,16 +33,15 @@ pub struct PageFieldModel { pub data_type: PageDataType, pub field_type: PageFieldType, pub field_content: PageFieldContentType, - pub field_data: PageFieldData + pub field_data: PageFieldData, } #[derive(Deserialize, Debug, Clone, Serialize)] #[serde(untagged)] pub enum PageDataType { - Text(String) + Text(String), } - #[derive(Deserialize, Debug, Clone, Serialize, Default)] pub enum PageFieldType { #[default] @@ -54,98 +51,84 @@ pub enum PageFieldType { TextEditor, Radio, Checkbox, - SingleImage + SingleImage, } #[derive(Deserialize, Debug, Clone, Serialize)] #[serde(untagged)] pub enum PageFieldContentType { - TextContentType { - text_value: TextContentType - }, - IntegerContentType { - integer_value: IntegerContentType - }, - ArrayContentType { - array_value: ArrayContentType - }, + TextContentType { text_value: TextContentType }, + IntegerContentType { integer_value: IntegerContentType }, + ArrayContentType { array_value: ArrayContentType }, } - #[derive(Deserialize, Debug, Clone, Serialize, Default)] pub struct TextContentType { - pub text_value: String + pub text_value: String, } - #[derive(Deserialize, Debug, Clone, Serialize, Default)] pub struct IntegerContentType { - pub integer_value: i64 + pub integer_value: i64, } - #[derive(Deserialize, Debug, Clone, Serialize, Default)] pub struct ArrayContentType { - pub array_value: Vec + pub array_value: Vec, } - - - #[derive(Deserialize, Debug, Clone, Serialize)] #[serde(untagged)] pub enum PageFieldData { SelectFieldData { - select_field_options: Vec + select_field_options: Vec, }, RadioFieldData { - radio_field_options: Vec + radio_field_options: Vec, }, CheckboxFieldData { - checkbox_field_options: Vec + checkbox_field_options: Vec, }, NoneFieldData { - none: String - } + none: String, + }, } - #[derive(Deserialize, Debug, Clone, Serialize)] pub struct PageSelectFieldData { pub label: String, - pub value: String + pub value: String, } #[derive(Deserialize, Debug, Clone, Serialize)] pub struct PageRadioFieldData { pub label: String, - pub value: String + pub value: String, } - #[derive(Deserialize, Debug, Clone, Serialize)] pub struct PageCheckboxFieldData { pub label: String, - pub value: String + pub value: String, } - - // endregion: Page model structs and enums - // region: impl Default for page model enums impl Default for PageFieldContentType { fn default() -> PageFieldContentType { - PageFieldContentType::TextContentType { text_value: TextContentType::default() } + PageFieldContentType::TextContentType { + text_value: TextContentType::default(), + } } } - impl Default for PageFieldData { fn default() -> PageFieldData { - PageFieldData::NoneFieldData { none: String::from("") } + PageFieldData::NoneFieldData { + none: String::from(""), + } } } @@ -164,29 +147,23 @@ impl Default for PageDataType { // endregion: impl Default for page model enums - // region: impl surreal Value for page model structs impl TryFrom for Value { type Error = Error; fn try_from(val: TextContentType) -> Result { - - let val_val: BTreeMap = [ - ("text_value".into(), val.text_value.into()), - ].into(); + let val_val: BTreeMap = + [("text_value".into(), val.text_value.into())].into(); Ok(val_val.into()) } } - impl TryFrom for Value { type Error = Error; fn try_from(val: IntegerContentType) -> Result { - - let val_val: BTreeMap = [ - ("integer_value".into(), val.integer_value.into()), - ].into(); + let val_val: BTreeMap = + [("integer_value".into(), val.integer_value.into())].into(); Ok(val_val.into()) } @@ -195,10 +172,8 @@ impl TryFrom for Value { impl TryFrom for Value { type Error = Error; fn try_from(val: ArrayContentType) -> Result { - - let val_val: BTreeMap = [ - ("array_value".into(), val.array_value.into()), - ].into(); + let val_val: BTreeMap = + [("array_value".into(), val.array_value.into())].into(); Ok(val_val.into()) } @@ -207,11 +182,11 @@ impl TryFrom for Value { impl TryFrom for Value { type Error = Error; fn try_from(val: PageSelectFieldData) -> Result { - let val_val: BTreeMap = [ ("label".into(), val.label.into()), ("value".into(), val.value.into()), - ].into(); + ] + .into(); Ok(val_val.into()) } @@ -220,11 +195,11 @@ impl TryFrom for Value { impl TryFrom for Value { type Error = Error; fn try_from(val: PageRadioFieldData) -> Result { - let val_val: BTreeMap = [ ("label".into(), val.label.into()), ("value".into(), val.value.into()), - ].into(); + ] + .into(); Ok(val_val.into()) } @@ -233,11 +208,11 @@ impl TryFrom for Value { impl TryFrom for Value { type Error = Error; fn try_from(val: PageCheckboxFieldData) -> Result { - let val_val: BTreeMap = [ ("label".into(), val.label.into()), ("value".into(), val.value.into()), - ].into(); + ] + .into(); Ok(val_val.into()) } @@ -245,51 +220,41 @@ impl TryFrom for Value { // endregion: impl surreal Value for page model structs - // region: impl surreal Object for page model structs impl TryFrom for PageModel { type Error = Error; fn try_from(val: Object) -> Result { - let id = val.get("id").get_id()?; let name = val.get("name").get_string()?; let identifier = val.get("identifier").get_string()?; let status = match val.get("status").get_string()?.as_str() { - "Draft" => { - PageStatus::Draft - }, - "Published" => { - PageStatus::Published - }, - _ => PageStatus::default() + "Draft" => PageStatus::Draft, + "Published" => PageStatus::Published, + _ => PageStatus::default(), }; let page_fields = match val.get("page_fields") { - Some(val) => { - - match val.clone() { - Value::Array(v) => { - let mut arr = Vec::new(); + Some(val) => match val.clone() { + Value::Array(v) => { + let mut arr = Vec::new(); - for array in v.into_iter() { - let object = match array.clone() { - Value::Object(v) => v, - _ => Object::default(), - }; + for array in v.into_iter() { + let object = match array.clone() { + Value::Object(v) => v, + _ => Object::default(), + }; - let page_field: PageFieldModel = object.try_into()?; + let page_field: PageFieldModel = object.try_into()?; - arr.push(page_field) - } - arr + arr.push(page_field) } - _ => Vec::new(), + arr } - } + _ => Vec::new(), + }, None => Vec::new(), }; - let created_at = val.get("created_at").get_datetime()?; let updated_at = val.get("updated_at").get_datetime()?; let created_by = val.get("created_by").get_string()?; @@ -317,47 +282,28 @@ impl TryFrom for PageFieldModel { let data_type_str = val.get("data_type").get_string()?; let data_type = match data_type_str.as_str() { - "TEXT" => { - PageDataType::Text("TEXT".to_string()) - }, - "Array_Text" => { - PageDataType::Text("Array_Text".to_string()) - }, + "TEXT" => PageDataType::Text("TEXT".to_string()), + "Array_Text" => PageDataType::Text("Array_Text".to_string()), - _ => PageDataType::default() + _ => PageDataType::default(), }; let field_type_str = val.get("field_type").get_string()?; let field_type = match field_type_str.as_str() { - "Text" => { - PageFieldType::Text - }, - "Textarea" => { - PageFieldType::Textarea - }, - "Select" => { - PageFieldType::Select - }, - "TextEditor" => { - PageFieldType::TextEditor - }, - "Radio" => { - PageFieldType::Radio - }, - "Checkbox" => { - PageFieldType::Checkbox - }, - "SingleImage" => { - PageFieldType::SingleImage - }, - - _ => PageFieldType::default() + "Text" => PageFieldType::Text, + "Textarea" => PageFieldType::Textarea, + "Select" => PageFieldType::Select, + "TextEditor" => PageFieldType::TextEditor, + "Radio" => PageFieldType::Radio, + "Checkbox" => PageFieldType::Checkbox, + "SingleImage" => PageFieldType::SingleImage, + + _ => PageFieldType::default(), }; let field_content = match data_type_str.as_str() { "TEXT" => { let options = match val.get("field_content") { - Some(val) => { let object = match val.clone() { Value::Object(v) => v, @@ -369,19 +315,16 @@ impl TryFrom for PageFieldModel { // println!("test {:?}", option); option - }, - None => { - TextContentType::default() - }, + } + None => TextContentType::default(), }; PageFieldContentType::TextContentType { - text_value: options + text_value: options, } } "INT" => { let options = match val.get("field_content") { - Some(val) => { let object = match val.clone() { Value::Object(v) => v, @@ -391,22 +334,17 @@ impl TryFrom for PageFieldModel { let option: IntegerContentType = object.try_into()?; option - }, - None => { - IntegerContentType { - integer_value: 0 - } - }, + } + None => IntegerContentType { integer_value: 0 }, }; PageFieldContentType::IntegerContentType { - integer_value: options + integer_value: options, } - }, + } "Array_Text" => { let array_val = match val.get("field_content") { Some(val) => { - let object = match val.clone() { Value::Object(v) => v, _ => Object::default(), @@ -415,133 +353,133 @@ impl TryFrom for PageFieldModel { let option: ArrayContentType = object.try_into()?; option - }, - None => { - ArrayContentType { - array_value: vec![] - } + } + None => ArrayContentType { + array_value: vec![], }, }; // let array_obj = ArrayContentType { // array_value: array_val // }; - PageFieldContentType::ArrayContentType { - array_value: array_val + PageFieldContentType::ArrayContentType { + array_value: array_val, } } - _ => PageFieldContentType::default() + _ => PageFieldContentType::default(), }; let field_data = match field_type_str.as_str() { - "Select" => { let options = match val.get("field_data") { - Some(val) => { - match val.clone() { - Value::Array(v) => { - let mut arr = Vec::new(); + Some(val) => match val.clone() { + Value::Array(v) => { + let mut arr = Vec::new(); - for array in v.into_iter() { - let object = match array.clone() { - Value::Object(v) => v, - _ => Object::default(), - }; + for array in v.into_iter() { + let object = match array.clone() { + Value::Object(v) => v, + _ => Object::default(), + }; - let option: PageSelectFieldData = object.try_into()?; + let option: PageSelectFieldData = object.try_into()?; - arr.push(option) - } + arr.push(option) + } - PageFieldData::SelectFieldData { - select_field_options: arr - } + PageFieldData::SelectFieldData { + select_field_options: arr, } - _ => PageFieldData::NoneFieldData { none: String::from("") } } - } - None => { - PageFieldData::NoneFieldData { none: String::from("") } + _ => PageFieldData::NoneFieldData { + none: String::from(""), + }, + }, + None => PageFieldData::NoneFieldData { + none: String::from(""), }, }; options - }, + } "Radio" => { let options = match val.get("field_data") { - Some(val) => { - match val.clone() { - Value::Array(v) => { - let mut arr = Vec::new(); + Some(val) => match val.clone() { + Value::Array(v) => { + let mut arr = Vec::new(); - for array in v.into_iter() { - let object = match array.clone() { - Value::Object(v) => v, - _ => Object::default(), - }; + for array in v.into_iter() { + let object = match array.clone() { + Value::Object(v) => v, + _ => Object::default(), + }; - let option: PageRadioFieldData = object.try_into()?; + let option: PageRadioFieldData = object.try_into()?; - arr.push(option) - } + arr.push(option) + } - PageFieldData::RadioFieldData { - radio_field_options: arr - } + PageFieldData::RadioFieldData { + radio_field_options: arr, } - _ => PageFieldData::NoneFieldData { none: String::from("") }, } - } - None => PageFieldData::NoneFieldData { none: String::from("") }, + _ => PageFieldData::NoneFieldData { + none: String::from(""), + }, + }, + None => PageFieldData::NoneFieldData { + none: String::from(""), + }, }; options - }, + } "Checkbox" => { let options = match val.get("field_data") { - Some(val) => { - match val.clone() { - Value::Array(v) => { - let mut arr = Vec::new(); + Some(val) => match val.clone() { + Value::Array(v) => { + let mut arr = Vec::new(); - for array in v.into_iter() { - let object = match array.clone() { - Value::Object(v) => v, - _ => Object::default(), - }; + for array in v.into_iter() { + let object = match array.clone() { + Value::Object(v) => v, + _ => Object::default(), + }; - let option: PageCheckboxFieldData = object.try_into()?; + let option: PageCheckboxFieldData = object.try_into()?; - arr.push(option) - } + arr.push(option) + } - PageFieldData::CheckboxFieldData { - checkbox_field_options: arr - } + PageFieldData::CheckboxFieldData { + checkbox_field_options: arr, } - _ => PageFieldData::NoneFieldData { none: String::from("") }, } - } - None => PageFieldData::NoneFieldData { none: String::from("") }, + _ => PageFieldData::NoneFieldData { + none: String::from(""), + }, + }, + None => PageFieldData::NoneFieldData { + none: String::from(""), + }, }; options - }, + } - _ => PageFieldData::NoneFieldData { none: String::from("") } + _ => PageFieldData::NoneFieldData { + none: String::from(""), + }, }; - - - Ok(PageFieldModel { name, identifier, data_type, field_type, field_content, - field_data + field_data, }) } } @@ -550,9 +488,7 @@ impl TryFrom for TextContentType { type Error = Error; fn try_from(val: Object) -> Result { let value = val.get("text_value").get_string()?; - Ok(TextContentType { - text_value: value - }) + Ok(TextContentType { text_value: value }) } } @@ -561,7 +497,7 @@ impl TryFrom for IntegerContentType { fn try_from(val: Object) -> Result { let value = val.get("value").get_int()?; Ok(IntegerContentType { - integer_value: value + integer_value: value, }) } } @@ -569,27 +505,21 @@ impl TryFrom for IntegerContentType { impl TryFrom for ArrayContentType { type Error = Error; fn try_from(val: Object) -> Result { - let array_value = match val.get("array_value") { - Some(val) => { - match val.clone() { - Value::Array(v) => { - let mut arr = Vec::new(); - for array in v.into_iter() { - arr.push(array.as_string()) - } - arr + Some(val) => match val.clone() { + Value::Array(v) => { + let mut arr = Vec::new(); + for array in v.into_iter() { + arr.push(array.as_string()) } - _ => Vec::new(), + arr } - } + _ => Vec::new(), + }, None => Vec::new(), }; - Ok(ArrayContentType { - array_value - }) - + Ok(ArrayContentType { array_value }) } } @@ -598,10 +528,7 @@ impl TryFrom for PageSelectFieldData { fn try_from(val: Object) -> Result { let label = val.get("label").get_string()?; let value = val.get("value").get_string()?; - Ok(PageSelectFieldData { - label, - value - }) + Ok(PageSelectFieldData { label, value }) } } @@ -610,10 +537,7 @@ impl TryFrom for PageCheckboxFieldData { fn try_from(val: Object) -> Result { let label = val.get("label").get_string()?; let value = val.get("value").get_string()?; - Ok(PageCheckboxFieldData { - label, - value - }) + Ok(PageCheckboxFieldData { label, value }) } } @@ -622,16 +546,12 @@ impl TryFrom for PageRadioFieldData { fn try_from(val: Object) -> Result { let label = val.get("label").get_string()?; let value = val.get("value").get_string()?; - Ok(PageRadioFieldData { - label, - value - }) + Ok(PageRadioFieldData { label, value }) } } // endregion: impl surreal Value for page model structs - // region: page model creatable and updatable structs #[derive(Serialize, Debug, Deserialize, Clone, Default)] @@ -646,7 +566,7 @@ pub struct CreatablePageModel { pub identifier: String, pub status: PageStatus, pub logged_in_username: String, - pub page_fields: Vec + pub page_fields: Vec, } #[derive(Serialize, Debug, Deserialize, Clone)] @@ -656,7 +576,7 @@ pub struct CreatablePageField { pub data_type: PageDataType, pub field_type: PageFieldType, pub field_content: PageFieldContentType, - pub field_data: PageFieldData + pub field_data: PageFieldData, } #[derive(Serialize, Debug, Deserialize, Clone)] @@ -668,7 +588,7 @@ pub struct UpdatablePageModel { pub logged_in_username: String, pub created_at: Datetime, pub created_by: String, - pub page_fields: Vec + pub page_fields: Vec, } #[derive(Serialize, Debug, Deserialize, Clone)] @@ -678,14 +598,14 @@ pub struct UpdatablePageField { pub data_type: PageDataType, pub field_type: PageFieldType, pub field_content: PageFieldContentType, - pub field_data: PageFieldData + pub field_data: PageFieldData, } #[derive(Serialize, Deserialize, Debug, Clone)] pub struct PutPageIdentifierModel { pub id: String, pub identifier: String, - pub logged_in_username: String + pub logged_in_username: String, } -// endregion: page model creatable and updatable structs \ No newline at end of file +// endregion: page model creatable and updatable structs diff --git a/src/models/password_rest_model.rs b/src/models/password_rest_model.rs index 0fc5454d..b10b005f 100644 --- a/src/models/password_rest_model.rs +++ b/src/models/password_rest_model.rs @@ -1,7 +1,7 @@ use crate::error::{Error, Result}; +use crate::models::BaseModel; use serde::{Deserialize, Serialize}; use surrealdb::sql::{Datetime, Object}; -use crate::models::BaseModel; #[derive(Serialize, Debug, Deserialize, Clone, Default, PartialEq)] pub struct PasswordResetModel { @@ -16,7 +16,7 @@ pub struct PasswordResetModel { pub enum PasswordResetTokenStatus { Active, #[default] - Expire + Expire, } impl TryFrom for PasswordResetModel { @@ -29,7 +29,7 @@ impl TryFrom for PasswordResetModel { let status = match val.get("status").get_string()?.as_str() { "Active" => PasswordResetTokenStatus::Active, "Expire" => PasswordResetTokenStatus::Active, - _ => PasswordResetTokenStatus::Expire + _ => PasswordResetTokenStatus::Expire, }; Ok(PasswordResetModel { @@ -37,13 +37,11 @@ impl TryFrom for PasswordResetModel { email, token, created_at, - status + status, }) } } - - #[derive(Serialize, Debug, Deserialize, Clone, Default)] pub struct CreatablePasswordResetModel { pub email: String, diff --git a/src/models/role_model.rs b/src/models/role_model.rs index 055016b5..64971c68 100644 --- a/src/models/role_model.rs +++ b/src/models/role_model.rs @@ -22,10 +22,9 @@ pub struct RoleModel { #[derive(Serialize, Debug, Deserialize, Clone, Default)] pub struct RoleOptionModel { pub label: String, - pub value: String + pub value: String, } - impl TryFrom for RoleModel { type Error = Error; fn try_from(val: Object) -> Result { @@ -37,20 +36,17 @@ impl TryFrom for RoleModel { let created_by = val.get("created_by").get_string()?; let updated_by = val.get("updated_by").get_string()?; let permissions = match val.get("permissions") { - Some(val) => { - - match val.clone() { - Value::Array(v) => { - let mut arr = Vec::new(); + Some(val) => match val.clone() { + Value::Array(v) => { + let mut arr = Vec::new(); - for array in v.into_iter() { - arr.push(array.as_string()) - } - arr + for array in v.into_iter() { + arr.push(array.as_string()) } - _ => Vec::new(), + arr } - } + _ => Vec::new(), + }, None => Vec::new(), }; @@ -87,7 +83,7 @@ pub struct UpdatableRoleModel { pub struct PutRoleIdentifierModel { pub id: String, pub identifier: String, - pub logged_in_username: String + pub logged_in_username: String, } #[derive(Serialize, Debug, Deserialize, Clone, Default)] diff --git a/src/models/setting_model.rs b/src/models/setting_model.rs index ed2d4326..4e682b07 100644 --- a/src/models/setting_model.rs +++ b/src/models/setting_model.rs @@ -1,7 +1,7 @@ use crate::error::{Error, Result}; +use crate::models::BaseModel; use serde::{Deserialize, Serialize}; use surrealdb::sql::{Datetime, Object}; -use crate::models::BaseModel; #[derive(Serialize, Debug, Deserialize, Clone, Default)] pub struct SettingModel { @@ -14,7 +14,6 @@ pub struct SettingModel { pub updated_by: String, } - impl TryFrom for SettingModel { type Error = Error; fn try_from(val: Object) -> Result { @@ -43,4 +42,4 @@ pub struct UpdatableSettingModel { pub id: String, pub value: String, pub logged_in_username: String, -} \ No newline at end of file +} diff --git a/src/models/token_claim_model.rs b/src/models/token_claim_model.rs index bac4082b..9ec19a99 100644 --- a/src/models/token_claim_model.rs +++ b/src/models/token_claim_model.rs @@ -1,5 +1,5 @@ -use serde::{Deserialize, Serialize}; use crate::models::admin_user_model::AdminUserModel; +use serde::{Deserialize, Serialize}; #[derive(Debug, Serialize, Deserialize)] pub struct TokenClaims { @@ -17,5 +17,5 @@ pub struct LoggedInUser { pub name: String, pub email: String, pub demo_data_status: bool, - pub admin_user_model: AdminUserModel -} \ No newline at end of file + pub admin_user_model: AdminUserModel, +} diff --git a/src/models/validation_error.rs b/src/models/validation_error.rs index 0c4141a7..b5af8aac 100644 --- a/src/models/validation_error.rs +++ b/src/models/validation_error.rs @@ -4,13 +4,13 @@ use serde::Serialize; #[derive(Debug, Serialize, Clone)] pub struct ErrorMessage { pub key: String, - pub message: String + pub message: String, } #[derive(Debug, Serialize, Clone)] pub struct ErrorResponse { pub status: bool, - pub errors: Vec + pub errors: Vec, } pub trait Validate { @@ -33,4 +33,4 @@ impl Validate for String { } Ok(true) } -} \ No newline at end of file +} diff --git a/src/providers/avored_config_provider.rs b/src/providers/avored_config_provider.rs index 91953f33..eb343da5 100644 --- a/src/providers/avored_config_provider.rs +++ b/src/providers/avored_config_provider.rs @@ -1,6 +1,6 @@ -use std::env; -use dotenvy::dotenv; use crate::error::{Error, Result}; +use dotenvy::dotenv; +use std::env; #[derive(Debug, Clone)] pub struct AvoRedConfigProvider { diff --git a/src/providers/avored_graphql_provider.rs b/src/providers/avored_graphql_provider.rs deleted file mode 100644 index a61aad09..00000000 --- a/src/providers/avored_graphql_provider.rs +++ /dev/null @@ -1,5 +0,0 @@ -use juniper::{EmptyMutation, EmptySubscription, RootNode}; -use crate::avored_state::AvoRedState; -use crate::query::AvoRedQuery; - -pub type AvoRedGraphqlSchema = RootNode<'static, AvoRedQuery, EmptyMutation, EmptySubscription>; diff --git a/src/providers/avored_template_provider.rs b/src/providers/avored_template_provider.rs index 57f60c27..7fba0083 100644 --- a/src/providers/avored_template_provider.rs +++ b/src/providers/avored_template_provider.rs @@ -1,17 +1,16 @@ -use handlebars::Handlebars; -use lettre::{AsyncSmtpTransport, Tokio1Executor}; -use lettre::transport::smtp::authentication::Credentials; use crate::error::Result; use crate::providers::avored_config_provider::AvoRedConfigProvider; +use handlebars::Handlebars; +use lettre::transport::smtp::authentication::Credentials; +use lettre::{AsyncSmtpTransport, Tokio1Executor}; pub struct AvoRedTemplateProvider { pub handlebars: Handlebars<'static>, - pub mailer: AsyncSmtpTransport + pub mailer: AsyncSmtpTransport, } impl AvoRedTemplateProvider { pub async fn register(config: AvoRedConfigProvider) -> Result { - let mut reg = Handlebars::new(); reg.register_template_file("forgot-password", "./resources/mail/forgot-password.hbs")?; reg.register_template_file("contact-us-email", "./resources/mail/contact-us-email.hbs")?; @@ -25,6 +24,9 @@ impl AvoRedTemplateProvider { .credentials(creds) .build(); - Ok(AvoRedTemplateProvider { handlebars: reg, mailer }) + Ok(AvoRedTemplateProvider { + handlebars: reg, + mailer, + }) } } diff --git a/src/providers/mod.rs b/src/providers/mod.rs index 7974bb27..0e68fe9b 100644 --- a/src/providers/mod.rs +++ b/src/providers/mod.rs @@ -1,4 +1,3 @@ pub mod avored_config_provider; pub mod avored_database_provider; pub mod avored_template_provider; -pub mod avored_graphql_provider; diff --git a/src/query/misc/api_version.rs b/src/query/misc/api_version.rs deleted file mode 100644 index b28e095c..00000000 --- a/src/query/misc/api_version.rs +++ /dev/null @@ -1,87 +0,0 @@ -use juniper::{ - FieldResult, - graphql_object, - GraphQLObject, - GraphQLScalar, - InputValue, - ParseScalarResult, - ParseScalarValue, - ScalarToken, - ScalarValue, - Value -}; -use surrealdb::sql::Datetime; -use crate::avored_state::AvoRedState; -use crate::query::AvoRedQuery; - -#[graphql_object] -#[graphql(context = AvoRedState)] -impl AvoRedQuery { - pub async fn api_version( - context: &AvoRedState - ) -> FieldResult { - let mut test = TestRoleModel::default(); - let user = UserId(Datetime::default()); - test.id = user; - let _test_state = context; - // let current_page: i64= 0; - // let order = String::from(""); - // let admin_user_pagination = context - // .admin_user_service - // .test(&context.db, current_page, order).await.unwrap(); - // println!("ctx : {:?}", context); - // - // "1.0" - Ok(test) - // Ok(admin_user_pagination.as_str()) - } -} - - -#[derive(Debug, Default, GraphQLObject)] -pub struct TestRoleModel { - pub id: UserId, - pub name: String, - pub created: String -} - - -#[derive(GraphQLScalar, Debug, Default)] -#[graphql( - to_output_with = to_output, - parse_token_with = parse_token, - from_input_with = from_input, - transparent -)] -pub struct UserId(Datetime); - -fn to_output(v: &UserId) -> Value { - println!("{:?}", v); - let test = &v.0; - // let inc = v.0 + 1; - Value::from(test.to_string()) -} - - -fn parse_token(value: ScalarToken<'_>) -> ParseScalarResult { - >::from_str(value) - .or_else(|_| >::from_str(value)) -} - -fn from_input(_input: &InputValue) -> Result -where - S: ScalarValue -{ - // Datetime::default() - // input.as_string_value() - // .and_then(|s| { - // Datetime::try_from(s).map_err(|_e| format!("invalid date")) }) - // - Ok(UserId(Datetime::default())) - // input.as_string_value() - // .ok_or_else(|| format!("Expected `String`, found: {input}")) - // .and_then(|s| { - // let date = Datetime::try_from(s); - // // UserId().map_err(|_e| format!("invalid date")) - // }) -} diff --git a/src/query/misc/mod.rs b/src/query/misc/mod.rs deleted file mode 100644 index 7a63fac0..00000000 --- a/src/query/misc/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod api_version; \ No newline at end of file diff --git a/src/query/mod.rs b/src/query/mod.rs deleted file mode 100644 index 8b2da77f..00000000 --- a/src/query/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod misc; - -#[derive(Clone, Copy, Debug)] -pub struct AvoRedQuery; \ No newline at end of file diff --git a/src/repositories/admin_user_repository.rs b/src/repositories/admin_user_repository.rs index 0181d15d..4ec30bd2 100644 --- a/src/repositories/admin_user_repository.rs +++ b/src/repositories/admin_user_repository.rs @@ -1,13 +1,15 @@ use std::collections::BTreeMap; use crate::error::{Error, Result}; -use crate::models::admin_user_model::{AdminUserModel, CreatableAdminUserModel, UpdatableAdminUserModel}; +use crate::models::admin_user_model::{ + AdminUserModel, CreatableAdminUserModel, UpdatableAdminUserModel, +}; +use crate::models::token_claim_model::LoggedInUser; use crate::models::ModelCount; +use crate::PER_PAGE; use surrealdb::dbs::Session; use surrealdb::kvs::Datastore; use surrealdb::sql::{Datetime, Value}; -use crate::models::token_claim_model::LoggedInUser; -use crate::PER_PAGE; use super::into_iter_objects; @@ -25,7 +27,8 @@ impl AdminUserRepository { database_session: &Session, email: String, ) -> Result { - let sql = "SELECT *, ->admin_user_role->roles.* as roles FROM admin_users WHERE email=$data;"; + let sql = + "SELECT *, ->admin_user_role->roles.* as roles FROM admin_users WHERE email=$data;"; let data: BTreeMap = [("data".into(), email.into())].into(); let responses = datastore.execute(sql, database_session, Some(data)).await?; @@ -72,17 +75,35 @@ impl AdminUserRepository { let sql = "CREATE admin_users CONTENT $data"; let data: BTreeMap = [ - ("full_name".into(), creatable_admin_user_model.full_name.into(),), + ( + "full_name".into(), + creatable_admin_user_model.full_name.into(), + ), ("email".into(), creatable_admin_user_model.email.into()), - ("password".into(), creatable_admin_user_model.password.into(),), - ("profile_image".into(), creatable_admin_user_model.profile_image.into(),), - ("is_super_admin".into(), creatable_admin_user_model.is_super_admin.into(),), - ("created_by".into(), creatable_admin_user_model.logged_in_username.clone().into(),), - ("updated_by".into(), creatable_admin_user_model.logged_in_username.into(),), + ( + "password".into(), + creatable_admin_user_model.password.into(), + ), + ( + "profile_image".into(), + creatable_admin_user_model.profile_image.into(), + ), + ( + "is_super_admin".into(), + creatable_admin_user_model.is_super_admin.into(), + ), + ( + "created_by".into(), + creatable_admin_user_model.logged_in_username.clone().into(), + ), + ( + "updated_by".into(), + creatable_admin_user_model.logged_in_username.into(), + ), ("created_at".into(), Datetime::default().into()), ("updated_at".into(), Datetime::default().into()), ] - .into(); + .into(); let vars: BTreeMap = [("data".into(), data.into())].into(); let ress = datastore.execute(sql, database_session, Some(vars)).await?; @@ -97,7 +118,6 @@ impl AdminUserRepository { admin_user_model } - pub async fn update_admin_user( &self, datastore: &Datastore, @@ -143,13 +163,12 @@ impl AdminUserRepository { admin_user_model } - pub async fn update_password_by_email( &self, datastore: &Datastore, database_session: &Session, new_password: String, - email: String + email: String, ) -> Result { let sql = " UPDATE type::table($table) SET password=$password WHERE email=$email"; @@ -172,7 +191,9 @@ impl AdminUserRepository { return Ok(true); } - Err(Error::Generic(format!("issue while updating password by email: {email}"))) + Err(Error::Generic(format!( + "issue while updating password by email: {email}" + ))) } // pub async fn delete_admin_user( @@ -223,23 +244,28 @@ impl AdminUserRepository { database_session: &Session, start: i64, order_column: String, - order_type: String + order_type: String, ) -> Result> { - - let sql = format!("\ + let sql = format!( + "\ SELECT *, ->admin_user_role->roles.* as roles \ FROM admin_users \ ORDER {} {} \ LIMIT $limit \ START $start;\ - ", order_column, order_type); + ", + order_column, order_type + ); let vars = BTreeMap::from([ ("limit".into(), PER_PAGE.into()), ("start".into(), start.into()), ("order_type".into(), "id".into()), ]); - let responses = datastore.execute(&sql, database_session, Some(vars)).await.unwrap(); + let responses = datastore + .execute(&sql, database_session, Some(vars)) + .await + .unwrap(); let mut admin_user_list: Vec = Vec::new(); for object in into_iter_objects(responses)? { @@ -258,7 +284,7 @@ impl AdminUserRepository { database_session: &Session, admin_user_id: String, role_id: String, - logged_in_user: LoggedInUser + logged_in_user: LoggedInUser, ) -> Result { let sql = format!( "RELATE {}:{}->{}->{}:{} CONTENT $attached_data;", @@ -271,7 +297,7 @@ impl AdminUserRepository { ("created_at".into(), Datetime::default().into()), ("updated_at".into(), Datetime::default().into()), ] - .into(); + .into(); let vars: BTreeMap = [("attached_data".into(), attached_data.into())].into(); @@ -293,7 +319,7 @@ impl AdminUserRepository { datastore: &Datastore, database_session: &Session, admin_user_id: String, - role_id: String + role_id: String, ) -> Result { let sql = format!( "DELETE {}:{}->{} WHERE {}:{};", diff --git a/src/repositories/asset_repository.rs b/src/repositories/asset_repository.rs index 3827505b..c2c53b10 100644 --- a/src/repositories/asset_repository.rs +++ b/src/repositories/asset_repository.rs @@ -4,7 +4,7 @@ use surrealdb::kvs::Datastore; use surrealdb::sql::{Datetime, Value}; use crate::error::{Error, Result}; -use crate::models::asset_model::{CreatableAssetModel, AssetModel}; +use crate::models::asset_model::{AssetModel, CreatableAssetModel}; use crate::models::ModelCount; use crate::PER_PAGE; @@ -24,9 +24,8 @@ impl AssetRepository { datastore: &Datastore, database_session: &Session, start: i64, - parent_id: String + parent_id: String, ) -> Result> { - let sql = "SELECT * FROM type::table($table) WHERE parent_id=$parent_id LIMIT $limit START $start;"; let vars = BTreeMap::from([ ("limit".into(), PER_PAGE.into()), @@ -49,7 +48,9 @@ impl AssetRepository { // Not sure do we need to add some kind a loop limit to prevent infinite loop?? while !t.parent_id.is_empty() { - let parent_model = self.find_by_id(datastore, database_session, &t.parent_id).await?; + let parent_model = self + .find_by_id(datastore, database_session, &t.parent_id) + .await?; t = parent_model.clone(); new_path = format!("{}/{}", parent_model.name, new_path); } @@ -66,9 +67,12 @@ impl AssetRepository { &self, datastore: &Datastore, database_session: &Session, - parent_id: String + parent_id: String, ) -> Result { - let sql = format!("SELECT count() FROM assets WHERE parent_id = '{}' GROUP ALL;", parent_id); + let sql = format!( + "SELECT count() FROM assets WHERE parent_id = '{}' GROUP ALL;", + parent_id + ); let responses = datastore.execute(&sql, database_session, None).await?; @@ -77,7 +81,6 @@ impl AssetRepository { Some(object) => object, None => Err(Error::Generic("no record found".to_string())), }; - match result_object { Ok(obj) => obj.try_into(), @@ -93,20 +96,26 @@ impl AssetRepository { ) -> Result { let sql = "CREATE assets CONTENT $data"; let meta = creatable_asset_model.metadata.get_file_metadata(); - let metadata: BTreeMap = [ - ("file_type".into(), meta.file_type.into()), - ].into(); + let metadata: BTreeMap = + [("file_type".into(), meta.file_type.into())].into(); let data: BTreeMap = [ ("name".into(), creatable_asset_model.name.into()), ("parent_id".into(), creatable_asset_model.parent_id.into()), ("asset_type".into(), creatable_asset_model.asset_type.into()), ("metadata".into(), metadata.into()), - ("created_by".into(), creatable_asset_model.logged_in_username.clone().into(),), - ("updated_by".into(), creatable_asset_model.logged_in_username.into(),), + ( + "created_by".into(), + creatable_asset_model.logged_in_username.clone().into(), + ), + ( + "updated_by".into(), + creatable_asset_model.logged_in_username.into(), + ), ("created_at".into(), Datetime::default().into()), ("updated_at".into(), Datetime::default().into()), - ].into(); + ] + .into(); let vars: BTreeMap = [("data".into(), data.into())].into(); @@ -115,7 +124,9 @@ impl AssetRepository { let result_object_option = into_iter_objects(responses)?.next(); let result_object = match result_object_option { Some(object) => object, - None => Err(Error::CreateModel("cannot create assets record".to_string())), + None => Err(Error::CreateModel( + "cannot create assets record".to_string(), + )), }; let asset_model: Result = result_object?.try_into(); @@ -130,21 +141,25 @@ impl AssetRepository { ) -> Result { let sql = "CREATE assets CONTENT $data"; let meta = creatable_asset_model.metadata.get_folder_metadata(); - let metadata: BTreeMap = [ - ("color".into(), meta.color.into()), - ].into(); + let metadata: BTreeMap = [("color".into(), meta.color.into())].into(); let data: BTreeMap = [ ("name".into(), creatable_asset_model.name.into()), - ("asset_type".into(), creatable_asset_model.asset_type.into(),), + ("asset_type".into(), creatable_asset_model.asset_type.into()), ("parent_id".into(), creatable_asset_model.parent_id.into()), ("metadata".into(), metadata.into()), - ("created_by".into(), creatable_asset_model.logged_in_username.clone().into(),), - ("updated_by".into(), creatable_asset_model.logged_in_username.into(),), + ( + "created_by".into(), + creatable_asset_model.logged_in_username.clone().into(), + ), + ( + "updated_by".into(), + creatable_asset_model.logged_in_username.into(), + ), ("created_at".into(), Datetime::default().into()), ("updated_at".into(), Datetime::default().into()), ] - .into(); + .into(); let vars: BTreeMap = [("data".into(), data.into())].into(); let responses = datastore.execute(sql, database_session, Some(vars)).await?; @@ -152,7 +167,9 @@ impl AssetRepository { let result_object_option = into_iter_objects(responses)?.next(); let result_object = match result_object_option { Some(object) => object, - None => Err(Error::CreateModel("cannot create assets record".to_string())), + None => Err(Error::CreateModel( + "cannot create assets record".to_string(), + )), }; let asset_model: Result = result_object?.try_into(); @@ -165,12 +182,12 @@ impl AssetRepository { database_session: &Session, asset_id: &str, ) -> Result { - let sql = - "SELECT * FROM type::thing($table, $id);"; + let sql = "SELECT * FROM type::thing($table, $id);"; let vars: BTreeMap = [ ("id".into(), asset_id.into()), ("table".into(), ASSET_TABLE.into()), - ].into(); + ] + .into(); let responses = datastore.execute(sql, database_session, Some(vars)).await?; @@ -189,9 +206,8 @@ impl AssetRepository { // Not sure do we need to add some kind a loop limit to prevent infinite loop?? while !t.parent_id.is_empty() { - let parent_model = Box::pin(self - .find_by_id(datastore, database_session, &t.parent_id) - ).await?; + let parent_model = + Box::pin(self.find_by_id(datastore, database_session, &t.parent_id)).await?; t = parent_model.clone(); new_path = format!("{}/{}", parent_model.name, new_path); @@ -209,23 +225,20 @@ impl AssetRepository { database_session: &Session, asset_id: &str, ) -> Result { - let sql = - "DELETE type::thing($table, $id);"; + let sql = "DELETE type::thing($table, $id);"; let vars: BTreeMap = [ ("id".into(), asset_id.into()), ("table".into(), ASSET_TABLE.into()), - ].into(); + ] + .into(); // find a way to get a response from query let responses = datastore.execute(sql, database_session, Some(vars)).await?; - let response = responses - .into_iter() - .next() - .map(|rp| rp.output()); + let response = responses.into_iter().next().map(|rp| rp.output()); let query_result = match response { Some(object) => object.is_ok(), - None => false + None => false, }; Ok(query_result) @@ -237,7 +250,7 @@ impl AssetRepository { database_session: &Session, name: &str, asset_id: &str, - logged_in_username: &str + logged_in_username: &str, ) -> Result { let sql = " UPDATE type::thing($table, $id) MERGE { diff --git a/src/repositories/collection_repository.rs b/src/repositories/collection_repository.rs new file mode 100644 index 00000000..fff0a96d --- /dev/null +++ b/src/repositories/collection_repository.rs @@ -0,0 +1,247 @@ +use std::collections::BTreeMap; + +use super::into_iter_objects; +use crate::error::{Error, Result}; +use crate::models::collection_model::{CollectionModel, CreatableCollection, PutCollectionIdentifierModel, UpdatableCollection}; +use crate::models::ModelCount; +use crate::PER_PAGE; +use surrealdb::dbs::Session; +use surrealdb::kvs::Datastore; +use surrealdb::sql::{Datetime, Value}; + +#[derive(Clone)] +pub struct CollectionRepository {} + +impl CollectionRepository { + pub(crate) async fn all_collection( + &self, + datastore: &Datastore, + database_session: &Session + ) -> Result> { + let sql = "SELECT * FROM collections"; + + let responses = datastore.execute(sql, database_session, None).await?; + + let mut collection_list: Vec = Vec::new(); + + for object in into_iter_objects(responses)? { + let model_object = object?; + + let model_model: Result = model_object.try_into(); + collection_list.push(model_model?); + } + Ok(collection_list) + } +} + +impl CollectionRepository { + pub fn new() -> Self { + CollectionRepository {} + } + + pub async fn get_total_count( + &self, + datastore: &Datastore, + database_session: &Session, + ) -> Result { + let sql = "SELECT count() FROM collections GROUP ALL;"; + let responses = datastore.execute(sql, database_session, None).await?; + + let result_object_option = into_iter_objects(responses)?.next(); + let result_object = match result_object_option { + Some(object) => object, + None => Err(Error::Generic("no record found".to_string())), + }; + let model_count: Result = result_object?.try_into(); + + model_count + } + + pub async fn paginate( + &self, + datastore: &Datastore, + database_session: &Session, + start: i64, + order_column: String, + order_type: String, + ) -> Result> { + let sql = format!( + "\ + SELECT * \ + FROM collections \ + ORDER {} {} \ + LIMIT $limit \ + START $start;\ + ", + order_column, order_type + ); + let vars = BTreeMap::from([ + ("limit".into(), PER_PAGE.into()), + ("start".into(), start.into()), + ]); + let responses = datastore + .execute(&sql, database_session, Some(vars)) + .await?; + + let mut paginate_models: Vec = Vec::new(); + + for object in into_iter_objects(responses)? { + let object = object?; + + let model_model: Result = object.try_into(); + paginate_models.push(model_model?); + } + Ok(paginate_models) + } + + pub async fn find_by_id( + &self, + datastore: &Datastore, + database_session: &Session, + model_id: String, + ) -> Result { + let sql = "SELECT * FROM type::thing($table, $id);"; + let vars: BTreeMap = [ + ("id".into(), model_id.into()), + ("table".into(), "collections".into()), + ] + .into(); + + let responses = datastore.execute(sql, database_session, Some(vars)).await?; + + let result_object_option = into_iter_objects(responses)?.next(); + let result_object = match result_object_option { + Some(object) => object, + None => Err(Error::Generic("no record found".to_string())), + }; + let model_model: Result = result_object?.try_into(); + + model_model + } + // + // + pub async fn update_collection_identifier( + &self, + datastore: &Datastore, + database_session: &Session, + put_model_identifier_model: PutCollectionIdentifierModel + ) -> Result { + let sql = "UPDATE type::thing($table, $id) + SET + identifier = $identifier, + updated_at = $updated_at, + updated_by = $updated_by + ; + "; + + let vars: BTreeMap = [ + ("identifier".into(), put_model_identifier_model.identifier.into()), + ("table".into(), "collections".into()), + ("updated_at".into(), Datetime::default().into()), + ("updated_by".into(), put_model_identifier_model.logged_in_username.into()), + ("id".into(), put_model_identifier_model.id.into()) + ].into(); + let responses = datastore.execute(sql, database_session, Some(vars)).await?; + + let result_object_option = into_iter_objects(responses)?.next(); + let result_object = match result_object_option { + Some(object) => object, + None => Err(Error::Generic("no record found".to_string())), + }; + let updated_model: Result = result_object?.try_into(); + + updated_model + } + + pub async fn count_of_identifier( + &self, + datastore: &Datastore, + database_session: &Session, + identifier: String + ) -> Result { + let sql = "SELECT count(identifier=$identifier) FROM collections GROUP ALL"; + + let vars: BTreeMap = [("identifier".into(), identifier.into())].into(); + let responses = datastore.execute(sql, database_session, Some(vars)).await?; + + let result_object_option = into_iter_objects(responses)?.next(); + let result_object = match result_object_option { + Some(object) => object, + None => Err(Error::Generic("no record found".to_string())), + }; + let model_count: Result = result_object?.try_into(); + + model_count + } + + pub async fn update_collection( + &self, + datastore: &Datastore, + database_session: &Session, + updatable_model: UpdatableCollection, + ) -> Result { + let sql = " + UPDATE type::thing($table, $id) MERGE { + name: $name, + updated_by: $logged_in_user_name, + updated_at: time::now() + };"; + + let vars = BTreeMap::from([ + ("name".into(), updatable_model.name.into()), + ( + "logged_in_user_name".into(), + updatable_model.logged_in_username.into(), + ), + ("id".into(), updatable_model.id.into()), + ("table".into(), "collections".into()), + ]); + let responses = datastore.execute(sql, database_session, Some(vars)).await?; + + let result_object_option = into_iter_objects(responses)?.next(); + let result_object = match result_object_option { + Some(object) => object, + None => Err(Error::Generic("no record found".to_string())), + }; + let model_model: Result = result_object?.try_into(); + + model_model + } + + pub async fn create_collection( + &self, + datastore: &Datastore, + database_session: &Session, + creatable_model: CreatableCollection, + ) -> Result { + let sql = "CREATE collections CONTENT $data"; + + let data: BTreeMap = [ + ("name".into(), creatable_model.name.into()), + ("identifier".into(), creatable_model.identifier.into()), + ( + "created_by".into(), + creatable_model.logged_in_username.clone().into(), + ), + ( + "updated_by".into(), + creatable_model.logged_in_username.into(), + ), + ("created_at".into(), Datetime::default().into()), + ("updated_at".into(), Datetime::default().into()), + ] + .into(); + let vars: BTreeMap = [("data".into(), data.into())].into(); + + let responses = datastore.execute(sql, database_session, Some(vars)).await?; + + let result_object_option = into_iter_objects(responses)?.next(); + let result_object = match result_object_option { + Some(object) => object, + None => Err(Error::Generic("no record found".to_string())), + }; + let created_model: Result = result_object?.try_into(); + + created_model + } +} diff --git a/src/repositories/component_repository.rs b/src/repositories/component_repository.rs index c83fa550..f11c73f1 100644 --- a/src/repositories/component_repository.rs +++ b/src/repositories/component_repository.rs @@ -1,13 +1,16 @@ use std::collections::BTreeMap; +use super::into_iter_objects; use crate::error::{Error, Result}; -use crate::models::component_model::{ComponentElementDataModel, ComponentModel, CreatableComponent, PutComponentIdentifierModel, UpdatableComponentModel}; +use crate::models::component_model::{ + ComponentElementDataModel, ComponentModel, CreatableComponent, PutComponentIdentifierModel, + UpdatableComponentModel, +}; use crate::models::ModelCount; use crate::PER_PAGE; use surrealdb::dbs::Session; use surrealdb::kvs::Datastore; use surrealdb::sql::{Datetime, Value}; -use super::into_iter_objects; #[derive(Clone)] pub struct ComponentRepository {} @@ -23,20 +26,25 @@ impl ComponentRepository { database_session: &Session, start: i64, order_column: String, - order_type: String + order_type: String, ) -> Result> { - let sql = format!("\ + let sql = format!( + "\ SELECT * \ FROM components \ ORDER {} {} \ LIMIT $limit \ START $start;\ - ", order_column, order_type); + ", + order_column, order_type + ); let vars = BTreeMap::from([ ("limit".into(), PER_PAGE.into()), ("start".into(), start.into()), ]); - let responses = datastore.execute(&sql, database_session, Some(vars)).await?; + let responses = datastore + .execute(&sql, database_session, Some(vars)) + .await?; let mut component_list: Vec = Vec::new(); @@ -52,7 +60,7 @@ impl ComponentRepository { pub async fn all( &self, datastore: &Datastore, - database_session: &Session + database_session: &Session, ) -> Result> { let sql = "SELECT *, ->component_field->fields.* as fields FROM components"; @@ -75,12 +83,12 @@ impl ComponentRepository { database_session: &Session, creatable_component_model: CreatableComponent, ) -> Result { - let mut element_sql = String::from(""); for element in creatable_component_model.elements { let mut element_data_sql = String::from(""); - let update_element_data_model: Vec = element.element_data.unwrap_or_default(); + let update_element_data_model: Vec = + element.element_data.unwrap_or_default(); for create_element_data in update_element_data_model { element_data_sql.push_str(&format!( @@ -96,24 +104,25 @@ impl ComponentRepository { )); } - element_sql.push_str( - &format!("{open_brace} \ + element_sql.push_str(&format!( + "{open_brace} \ name: '{name}', \ identifier: '{identifier}', \ element_type: '{element_type}', \ element_data_type: '{element_data_type}', \ element_data: [{element_data_sql}], {close_brace},", - open_brace = String::from("{"), - name = element.name, - identifier = element.identifier, - element_type = element.element_type, - element_data_type = element.element_data_type, - close_brace = String::from("}") - )); + open_brace = String::from("{"), + name = element.name, + identifier = element.identifier, + element_type = element.element_type, + element_data_type = element.element_data_type, + close_brace = String::from("}") + )); } - let sql = format!("\ + let sql = format!( + "\ CREATE components \ CONTENT {open_brace} \ name: '{name}', @@ -129,8 +138,8 @@ impl ComponentRepository { identifier = creatable_component_model.identifier, element_sql = element_sql, logged_in_user_email = creatable_component_model.logged_in_username, - close_brace = String::from("}")); - + close_brace = String::from("}") + ); println!("CREATE COMPONENT: SQL: {sql}"); @@ -152,8 +161,7 @@ impl ComponentRepository { database_session: &Session, component_id: String, ) -> Result { - let sql = - "SELECT *, ->component_field->fields.* as fields FROM type::thing($table, $id);"; + let sql = "SELECT *, ->component_field->fields.* as fields FROM type::thing($table, $id);"; let vars: BTreeMap = [ ("id".into(), component_id.into()), ("table".into(), "components".into()), @@ -179,13 +187,12 @@ impl ComponentRepository { database_session: &Session, updatable_component_model: UpdatableComponentModel, ) -> Result { - let mut element_sql = String::from(""); for element in updatable_component_model.elements { - let mut element_data_sql = String::from(""); - let create_element_data_model: Vec = element.element_data.unwrap_or_default(); + let create_element_data_model: Vec = + element.element_data.unwrap_or_default(); for creatable_element_data in create_element_data_model { element_data_sql.push_str(&format!( @@ -201,25 +208,26 @@ impl ComponentRepository { )); } - element_sql.push_str( - &format!("{open_brace} \ + element_sql.push_str(&format!( + "{open_brace} \ name: '{name}', \ identifier: '{identifier}', \ element_type: '{element_type}', \ element_data_type: '{element_data_type}', \ element_data: [{element_data_sql}], \ {close_brace},", - open_brace = String::from("{"), - name = element.name, - identifier = element.identifier, - element_type = element.element_type, - element_data_type = element.element_data_type, - element_data_sql = element_data_sql, - close_brace = String::from("}") - )); + open_brace = String::from("{"), + name = element.name, + identifier = element.identifier, + element_type = element.element_type, + element_data_type = element.element_data_type, + element_data_sql = element_data_sql, + close_brace = String::from("}") + )); } - let sql = format!("\ + let sql = format!( + "\ UPDATE components:{id} \ MERGE {open_brace} \ name: '{name}', @@ -227,13 +235,13 @@ impl ComponentRepository { updated_by: '{logged_in_user_email}', updated_at: time::now(), {close_brace}", - id = updatable_component_model.id, - open_brace = String::from("{"), - name = updatable_component_model.name, - element_sql = element_sql, - logged_in_user_email = updatable_component_model.logged_in_username, - close_brace = String::from("}")); - + id = updatable_component_model.id, + open_brace = String::from("{"), + name = updatable_component_model.name, + element_sql = element_sql, + logged_in_user_email = updatable_component_model.logged_in_username, + close_brace = String::from("}") + ); let responses = datastore.execute(&sql, database_session, None).await?; @@ -271,7 +279,7 @@ impl ComponentRepository { &self, datastore: &Datastore, database_session: &Session, - identifier: String + identifier: String, ) -> Result { let sql = "SELECT count(identifier=$identifier) FROM components GROUP ALL"; @@ -292,7 +300,7 @@ impl ComponentRepository { &self, datastore: &Datastore, database_session: &Session, - put_component_identifier_model: PutComponentIdentifierModel + put_component_identifier_model: PutComponentIdentifierModel, ) -> Result { let sql = "UPDATE type::thing($table, $id) SET @@ -303,12 +311,19 @@ impl ComponentRepository { "; let vars: BTreeMap = [ - ("identifier".into(), put_component_identifier_model.identifier.into()), + ( + "identifier".into(), + put_component_identifier_model.identifier.into(), + ), ("table".into(), "components".into()), ("updated_at".into(), Datetime::default().into()), - ("updated_by".into(), put_component_identifier_model.logged_in_username.into()), - ("id".into(), put_component_identifier_model.id.into()) - ].into(); + ( + "updated_by".into(), + put_component_identifier_model.logged_in_username.into(), + ), + ("id".into(), put_component_identifier_model.id.into()), + ] + .into(); let responses = datastore.execute(sql, database_session, Some(vars)).await?; let result_object_option = into_iter_objects(responses)?.next(); diff --git a/src/repositories/content_repository.rs b/src/repositories/content_repository.rs new file mode 100644 index 00000000..6ab2f44c --- /dev/null +++ b/src/repositories/content_repository.rs @@ -0,0 +1,312 @@ +use std::collections::BTreeMap; +use surrealdb::dbs::Session; +use surrealdb::kvs::Datastore; +use surrealdb::sql::{Datetime, Value}; +use crate::error::Error; +use crate::models::content_model::{ContentDataType, ContentFieldContentType, ContentFieldType, ContentModel, CreatableContentModel, PutContentIdentifierModel, UpdatableContentModel}; +use crate::repositories::into_iter_objects; +use crate::error::Result; +use crate::models::ModelCount; +use crate::PER_PAGE; + +#[derive(Clone)] +pub struct ContentRepository {} + +impl ContentRepository { + + pub(crate) async fn find_by_id( + &self, + datastore: &Datastore, + database_session: &Session, + content_type: String, + id: &str, + ) -> Result { + let sql = "SELECT * FROM type::thing($table, $id);"; + let vars: BTreeMap = [ + ("id".into(), id.into()), + ("table".into(), content_type.into()), + ] + .into(); + + let responses = datastore.execute(sql, database_session, Some(vars)).await?; + + let result_object_option = into_iter_objects(responses)?.next(); + let result_object = match result_object_option { + Some(object) => object, + None => Err(Error::Generic("no record found".to_string())), + }; + + let model: Result = result_object?.try_into(); + + model + } + + pub(crate) async fn paginate( + &self, + datastore: &Datastore, + database_session: &Session, + content_type: &str, + start: i64, + order_column: String, + order_type: String, + ) -> Result> { + let sql = format!( + "\ + SELECT * \ + FROM type::table($table) \ + ORDER {} {} + LIMIT $limit \ + START $start;\ + ", + order_column, order_type + ); + let vars = BTreeMap::from([ + ("limit".into(), PER_PAGE.into()), + ("start".into(), start.into()), + ("table".into(), content_type.into()), + ]); + let responses = datastore + .execute(&sql, database_session, Some(vars)) + .await?; + + let mut content_list: Vec = Vec::new(); + + for object in into_iter_objects(responses)? { + let content_object = object?; + + let content_model: Result = content_object.try_into(); + content_list.push(content_model?); + } + Ok(content_list) + } + pub(crate) async fn get_total_count( + &self, + datastore: &Datastore, + database_session: &Session, + content_type: &str + ) -> Result { + let sql = format!("SELECT count() FROM {content_type} GROUP ALL;"); + let responses = datastore.execute(&sql, database_session, None).await?; + + let result_object_option = into_iter_objects(responses)?.next(); + let result_object = match result_object_option { + Some(object) => object, + None => Err(Error::Generic("no record found".to_string())), + }; + let model_count: Result = result_object?.try_into(); + + model_count + } + +} + +impl ContentRepository { + pub(crate) async fn create_content( + &self, + datastore: &Datastore, + database_session: &Session, + creatable_content_model: CreatableContentModel, + ) -> Result{ + let sql = "CREATE type::table($table) CONTENT $data"; + + let mut content_fields: Vec = vec![]; + + for created_content_field in creatable_content_model.content_fields { + let data_type_value: Value = match created_content_field.data_type { + ContentDataType::Text(v) => v.into(), + }; + + let field_type_value: Value = match created_content_field.field_type { + ContentFieldType::Text => "Text".into(), + }; + + let field_content_value: Value = match created_content_field.field_content { + ContentFieldContentType::ContentTextType { text_value } => text_value.try_into()?, + }; + + + let content_field: BTreeMap = [ + ("name".into(), created_content_field.name.into()), + ("identifier".into(), created_content_field.identifier.into()), + ("data_type".into(), data_type_value), + ("field_type".into(), field_type_value), + ("field_content".into(), field_content_value), + ] + .into(); + + content_fields.push(content_field.into()); + } + + + let data: BTreeMap = [ + ("name".into(), creatable_content_model.name.into()), + ("identifier".into(), creatable_content_model.identifier.into()), + ( + "created_by".into(), + creatable_content_model.logged_in_username.clone().into(), + ), + ( + "updated_by".into(), + creatable_content_model.logged_in_username.into(), + ), + ("content_fields".into(), content_fields.into()), + ("created_at".into(), Datetime::default().into()), + ("updated_at".into(), Datetime::default().into()), + ] + .into(); + + let vars: BTreeMap = [ + ("data".into(), data.into()), + ("table".into(), creatable_content_model.content_type.into()), + ] + .into(); + + let responses = datastore.execute(sql, database_session, Some(vars)).await?; + + let result_object_option = into_iter_objects(responses)?.next(); + let result_object = match result_object_option { + Some(object) => object, + None => Err(Error::Generic("no record found".to_string())), + }; + + let model: Result = result_object?.try_into(); + + model + } + + pub(crate) async fn update_content( + &self, + datastore: &Datastore, + database_session: &Session, + updatable_model: UpdatableContentModel, + ) -> Result { + let sql = "UPDATE type::thing($table, $id) CONTENT $data"; + + let mut content_fields: Vec = vec![]; + + for updatable_content_field in updatable_model.content_fields { + let data_type_value: Value = match updatable_content_field.data_type { + ContentDataType::Text(v) => v.into(), + }; + + let field_type_value: Value = match updatable_content_field.field_type { + ContentFieldType::Text => "Text".into(), + + }; + let field_content_value: Value = match updatable_content_field.field_content { + ContentFieldContentType::ContentTextType { text_value } => text_value.try_into()?, + }; + + let content_field: BTreeMap = [ + ("name".into(), updatable_content_field.name.into()), + ("identifier".into(), updatable_content_field.identifier.into()), + ("data_type".into(), data_type_value), + ("field_type".into(), field_type_value), + ("field_content".into(), field_content_value), + ] + .into(); + + content_fields.push(content_field.into()); + } + + + let data: BTreeMap = [ + ("name".into(), updatable_model.name.into()), + ("identifier".into(), updatable_model.identifier.into()), + ( + "updated_by".into(), + updatable_model.logged_in_username.clone().into(), + ), + ("created_by".into(), updatable_model.created_by.into()), + ("content_fields".into(), content_fields.into()), + ("updated_at".into(), Datetime::default().into()), + ("created_at".into(), updatable_model.created_at.into()), + ] + .into(); + + let vars: BTreeMap = [ + ("data".into(), data.into()), + ("table".into(), updatable_model.content_type.into()), + ("id".into(), updatable_model.id.into()), + ] + .into(); + + let responses = datastore.execute(sql, database_session, Some(vars)).await?; + + let result_object_option = into_iter_objects(responses)?.next(); + let result_object = match result_object_option { + Some(object) => object, + None => Err(Error::Generic("no record found".to_string())), + }; + + let model: Result = result_object?.try_into(); + + model + } + + pub(crate) async fn count_of_identifier( + &self, + datastore: &Datastore, + database_session: &Session, + collection_type: &str, + identifier: &str + ) -> Result { + let sql = format!("SELECT count(identifier=$identifier) FROM {collection_type} GROUP ALL"); + + let vars: BTreeMap = [("identifier".into(), identifier.into())].into(); + let responses = datastore.execute(&sql, database_session, Some(vars)).await?; + + let result_object_option = into_iter_objects(responses)?.next(); + let result_object = match result_object_option { + Some(object) => object, + None => Err(Error::Generic("no record found".to_string())), + }; + let model_count: Result = result_object?.try_into(); + + model_count + } + + pub(crate) async fn update_content_identifier( + &self, + datastore: &Datastore, + database_session: &Session, + put_content_identifier_model: PutContentIdentifierModel, + ) -> Result { + let sql = "UPDATE type::thing($table, $id) + SET + identifier = $identifier, + updated_at = $updated_at, + updated_by = $updated_by + ; + "; + + let vars: BTreeMap = [ + ( + "identifier".into(), + put_content_identifier_model.identifier.into(), + ), + ("table".into(), put_content_identifier_model.collection_type.into()), + ("updated_at".into(), Datetime::default().into()), + ( + "updated_by".into(), + put_content_identifier_model.logged_in_username.into(), + ), + ("id".into(), put_content_identifier_model.id.into()), + ] + .into(); + let responses = datastore.execute(sql, database_session, Some(vars)).await?; + + let result_object_option = into_iter_objects(responses)?.next(); + let result_object = match result_object_option { + Some(object) => object, + None => Err(Error::Generic("no record found".to_string())), + }; + let updated_model: Result = result_object?.try_into(); + + updated_model + } + + pub fn new() -> Self { + ContentRepository {} + } +} diff --git a/src/repositories/mod.rs b/src/repositories/mod.rs index 8bc14dcd..3c183537 100644 --- a/src/repositories/mod.rs +++ b/src/repositories/mod.rs @@ -3,14 +3,16 @@ use surrealdb::dbs::Response; use surrealdb::sql::{Object, Value}; pub mod admin_user_repository; -pub mod password_reset_repository; pub mod component_repository; pub mod page_repository; +pub mod password_reset_repository; pub mod role_repository; pub mod asset_repository; -pub mod setting_repository; +pub mod collection_repository; pub mod model_repository; +pub mod setting_repository; +pub mod content_repository; pub fn into_iter_objects(responses: Vec) -> Result>> { let response = responses diff --git a/src/repositories/model_repository.rs b/src/repositories/model_repository.rs index 3c9214d9..9185f7ce 100644 --- a/src/repositories/model_repository.rs +++ b/src/repositories/model_repository.rs @@ -1,13 +1,15 @@ use std::collections::BTreeMap; +use super::into_iter_objects; use crate::error::{Error, Result}; +use crate::models::model_model::{ + CreatableModel, ModelModel, PutModelIdentifierModel, UpdatableModelModel, +}; +use crate::models::ModelCount; +use crate::PER_PAGE; use surrealdb::dbs::Session; use surrealdb::kvs::Datastore; use surrealdb::sql::{Datetime, Value}; -use crate::models::model_model::{CreatableModel, ModelModel, PutModelIdentifierModel, UpdatableModelModel}; -use crate::models::ModelCount; -use crate::PER_PAGE; -use super::into_iter_objects; #[derive(Clone)] pub struct ModelRepository {} @@ -17,7 +19,6 @@ impl ModelRepository { ModelRepository {} } - pub async fn create_model( &self, datastore: &Datastore, @@ -29,8 +30,14 @@ impl ModelRepository { let data: BTreeMap = [ ("name".into(), creatable_model.name.into()), ("identifier".into(), creatable_model.identifier.into()), - ("created_by".into(), creatable_model.logged_in_username.clone().into()), - ("updated_by".into(), creatable_model.logged_in_username.into()), + ( + "created_by".into(), + creatable_model.logged_in_username.clone().into(), + ), + ( + "updated_by".into(), + creatable_model.logged_in_username.into(), + ), ("created_at".into(), Datetime::default().into()), ("updated_at".into(), Datetime::default().into()), ] @@ -73,20 +80,25 @@ impl ModelRepository { database_session: &Session, start: i64, order_column: String, - order_type: String + order_type: String, ) -> Result> { - let sql = format!("\ + let sql = format!( + "\ SELECT * \ FROM models \ ORDER {} {} \ LIMIT $limit \ START $start;\ - ", order_column, order_type); + ", + order_column, order_type + ); let vars = BTreeMap::from([ ("limit".into(), PER_PAGE.into()), ("start".into(), start.into()), ]); - let responses = datastore.execute(&sql, database_session, Some(vars)).await?; + let responses = datastore + .execute(&sql, database_session, Some(vars)) + .await?; let mut paginate_models: Vec = Vec::new(); @@ -110,7 +122,7 @@ impl ModelRepository { ("id".into(), model_id.into()), ("table".into(), "models".into()), ] - .into(); + .into(); let responses = datastore.execute(sql, database_session, Some(vars)).await?; @@ -124,12 +136,11 @@ impl ModelRepository { model_model } - pub async fn update_model_identifier( &self, datastore: &Datastore, database_session: &Session, - put_model_identifier_model: PutModelIdentifierModel + put_model_identifier_model: PutModelIdentifierModel, ) -> Result { let sql = "UPDATE type::thing($table, $id) SET @@ -140,12 +151,19 @@ impl ModelRepository { "; let vars: BTreeMap = [ - ("identifier".into(), put_model_identifier_model.identifier.into()), + ( + "identifier".into(), + put_model_identifier_model.identifier.into(), + ), ("table".into(), "models".into()), ("updated_at".into(), Datetime::default().into()), - ("updated_by".into(), put_model_identifier_model.logged_in_username.into()), - ("id".into(), put_model_identifier_model.id.into()) - ].into(); + ( + "updated_by".into(), + put_model_identifier_model.logged_in_username.into(), + ), + ("id".into(), put_model_identifier_model.id.into()), + ] + .into(); let responses = datastore.execute(sql, database_session, Some(vars)).await?; let result_object_option = into_iter_objects(responses)?.next(); @@ -162,7 +180,7 @@ impl ModelRepository { &self, datastore: &Datastore, database_session: &Session, - identifier: String + identifier: String, ) -> Result { let sql = "SELECT count(identifier=$identifier) FROM models GROUP ALL"; @@ -194,7 +212,10 @@ impl ModelRepository { let vars = BTreeMap::from([ ("name".into(), updatable_model.name.into()), - ("logged_in_user_name".into(), updatable_model.logged_in_username.into()), + ( + "logged_in_user_name".into(), + updatable_model.logged_in_username.into(), + ), ("id".into(), updatable_model.id.into()), ("table".into(), "models".into()), ]); @@ -209,5 +230,4 @@ impl ModelRepository { model_model } - } diff --git a/src/repositories/page_repository.rs b/src/repositories/page_repository.rs index 36cdbc4f..9fac0c1e 100644 --- a/src/repositories/page_repository.rs +++ b/src/repositories/page_repository.rs @@ -4,7 +4,10 @@ use surrealdb::kvs::Datastore; use surrealdb::sql::{Datetime, Value}; use crate::error::{Error, Result}; -use crate::models::page_model::{CreatablePageModel, PageModel, UpdatablePageModel, PageDataType, PageFieldContentType, PageFieldData, PageFieldType, PageStatus, PutPageIdentifierModel}; +use crate::models::page_model::{ + CreatablePageModel, PageDataType, PageFieldContentType, PageFieldData, PageFieldType, + PageModel, PageStatus, PutPageIdentifierModel, UpdatablePageModel, +}; use crate::models::ModelCount; use crate::PER_PAGE; @@ -27,19 +30,24 @@ impl PageRepository { order_column: String, order_type: String, ) -> Result> { - let sql = format!("\ + let sql = format!( + "\ SELECT * \ FROM type::table($table) \ ORDER {} {} LIMIT $limit \ START $start;\ - ", order_column, order_type); + ", + order_column, order_type + ); let vars = BTreeMap::from([ ("limit".into(), PER_PAGE.into()), ("start".into(), start.into()), ("table".into(), PAGE_TABLE.into()), ]); - let responses = datastore.execute(&sql, database_session, Some(vars)).await?; + let responses = datastore + .execute(&sql, database_session, Some(vars)) + .await?; let mut page_list: Vec = Vec::new(); @@ -74,17 +82,14 @@ impl PageRepository { &self, datastore: &Datastore, database_session: &Session, - page_id: &String) -> Result - { + page_id: &String, + ) -> Result { let sql = format!("DELETE pages:{page_id}"); let responses = datastore.execute(&sql, database_session, None).await?; - let response = responses - .into_iter() - .next() - .map(|rp| rp.output()); + let response = responses.into_iter().next().map(|rp| rp.output()); let query_result = match response { Some(object) => object.is_ok(), // there is another method is_err() as well - None => false + None => false, }; Ok(query_result) } @@ -95,13 +100,12 @@ impl PageRepository { database_session: &Session, page_id: String, ) -> Result { - let sql = - "SELECT * FROM type::thing($table, $id);"; + let sql = "SELECT * FROM type::thing($table, $id);"; let vars: BTreeMap = [ ("id".into(), page_id.into()), ("table".into(), "pages".into()), ] - .into(); + .into(); let responses = datastore.execute(sql, database_session, Some(vars)).await?; @@ -121,12 +125,8 @@ impl PageRepository { datastore: &Datastore, database_session: &Session, ) -> Result> { - let sql = - "SELECT * FROM type::table($table);"; - let vars: BTreeMap = [ - ("table".into(), PAGE_TABLE.into()), - ] - .into(); + let sql = "SELECT * FROM type::table($table);"; + let vars: BTreeMap = [("table".into(), PAGE_TABLE.into())].into(); let responses = datastore.execute(sql, database_session, Some(vars)).await?; @@ -177,12 +177,19 @@ impl PageRepository { "; let vars: BTreeMap = [ - ("identifier".into(), put_page_identifier_model.identifier.into()), + ( + "identifier".into(), + put_page_identifier_model.identifier.into(), + ), ("table".into(), "pages".into()), ("updated_at".into(), Datetime::default().into()), - ("updated_by".into(), put_page_identifier_model.logged_in_username.into()), - ("id".into(), put_page_identifier_model.id.into()) - ].into(); + ( + "updated_by".into(), + put_page_identifier_model.logged_in_username.into(), + ), + ("id".into(), put_page_identifier_model.id.into()), + ] + .into(); let responses = datastore.execute(sql, database_session, Some(vars)).await?; let result_object_option = into_iter_objects(responses)?.next(); @@ -195,41 +202,43 @@ impl PageRepository { updated_model } - pub async fn create_page ( + pub async fn create_page( &self, datastore: &Datastore, database_session: &Session, - creatable_page_model: CreatablePageModel + creatable_page_model: CreatablePageModel, ) -> Result { let sql = "CREATE type::table($table) CONTENT $data"; let mut page_fields: Vec = vec![]; for created_page_field in creatable_page_model.page_fields { - - let data_type_value: Value = match created_page_field.data_type { - PageDataType::Text(v) => v.into(), + PageDataType::Text(v) => v.into(), }; let field_type_value: Value = match created_page_field.field_type { - PageFieldType::Text => "Text".into(), + PageFieldType::Text => "Text".into(), PageFieldType::Textarea => "Textarea".into(), PageFieldType::Select => "Select".into(), PageFieldType::TextEditor => "TextEditor".into(), PageFieldType::Radio => "Radio".into(), PageFieldType::Checkbox => "Checkbox".into(), - PageFieldType::SingleImage => "SingleImage".into() + PageFieldType::SingleImage => "SingleImage".into(), }; let field_content_value: Value = match created_page_field.field_content { - PageFieldContentType::TextContentType { text_value } => text_value.try_into()?, - PageFieldContentType::IntegerContentType { integer_value } => integer_value.try_into()?, + PageFieldContentType::TextContentType { text_value } => text_value.try_into()?, + PageFieldContentType::IntegerContentType { integer_value } => { + integer_value.try_into()? + } PageFieldContentType::ArrayContentType { array_value } => array_value.try_into()?, }; let field_data_value: Value = match created_page_field.field_data { - PageFieldData::SelectFieldData { select_field_options } => { + PageFieldData::SelectFieldData { + select_field_options, + } => { let mut options: Vec = vec![]; for option in select_field_options { let val: Value = option.try_into()?; @@ -237,8 +246,10 @@ impl PageRepository { } options.into() - }, - PageFieldData::RadioFieldData { radio_field_options } => { + } + PageFieldData::RadioFieldData { + radio_field_options, + } => { let mut options: Vec = vec![]; for option in radio_field_options { let val: Value = option.try_into()?; @@ -246,8 +257,10 @@ impl PageRepository { } options.into() - }, - PageFieldData::CheckboxFieldData { checkbox_field_options } => { + } + PageFieldData::CheckboxFieldData { + checkbox_field_options, + } => { let mut options: Vec = vec![]; for option in checkbox_field_options { let val: Value = option.try_into()?; @@ -255,8 +268,8 @@ impl PageRepository { } options.into() - }, - PageFieldData::NoneFieldData {none: _} => "null".into(), + } + PageFieldData::NoneFieldData { none: _ } => "null".into(), }; let page_field: BTreeMap = [ @@ -266,13 +279,14 @@ impl PageRepository { ("field_type".into(), field_type_value), ("field_content".into(), field_content_value), ("field_data".into(), field_data_value), - ].into(); + ] + .into(); page_fields.push(page_field.into()); } let status: Value = match creatable_page_model.status { - PageStatus::Draft => "Draft".into(), + PageStatus::Draft => "Draft".into(), PageStatus::Published => "Published".into(), }; @@ -280,19 +294,25 @@ impl PageRepository { ("name".into(), creatable_page_model.name.into()), ("identifier".into(), creatable_page_model.identifier.into()), ("status".into(), status), - ("created_by".into(), creatable_page_model.logged_in_username.clone().into()), - ("updated_by".into(), creatable_page_model.logged_in_username.into()), + ( + "created_by".into(), + creatable_page_model.logged_in_username.clone().into(), + ), + ( + "updated_by".into(), + creatable_page_model.logged_in_username.into(), + ), ("page_fields".into(), page_fields.into()), ("created_at".into(), Datetime::default().into()), ("updated_at".into(), Datetime::default().into()), ] - .into(); + .into(); let vars: BTreeMap = [ ("data".into(), data.into()), - ("table".into(), PAGE_TABLE.into()) + ("table".into(), PAGE_TABLE.into()), ] - .into(); + .into(); let responses = datastore.execute(sql, database_session, Some(vars)).await?; @@ -307,39 +327,42 @@ impl PageRepository { model } - pub async fn update_page ( + pub async fn update_page( &self, datastore: &Datastore, database_session: &Session, - updatable_page_model: UpdatablePageModel + updatable_page_model: UpdatablePageModel, ) -> Result { let sql = "UPDATE type::thing($table, $id) CONTENT $data"; let mut page_fields: Vec = vec![]; for updatable_page_field in updatable_page_model.page_fields { - let data_type_value: Value = match updatable_page_field.data_type { - PageDataType::Text(v) => v.into(), + PageDataType::Text(v) => v.into(), }; let field_type_value: Value = match updatable_page_field.field_type { - PageFieldType::Text => "Text".into(), + PageFieldType::Text => "Text".into(), PageFieldType::Textarea => "Textarea".into(), PageFieldType::Select => "Select".into(), PageFieldType::TextEditor => "TextEditor".into(), PageFieldType::Radio => "Radio".into(), PageFieldType::Checkbox => "Checkbox".into(), - PageFieldType::SingleImage => "SingleImage".into() + PageFieldType::SingleImage => "SingleImage".into(), }; let field_content_value: Value = match updatable_page_field.field_content { - PageFieldContentType::TextContentType { text_value } => text_value.try_into()?, - PageFieldContentType::IntegerContentType { integer_value } => integer_value.try_into()?, + PageFieldContentType::TextContentType { text_value } => text_value.try_into()?, + PageFieldContentType::IntegerContentType { integer_value } => { + integer_value.try_into()? + } PageFieldContentType::ArrayContentType { array_value } => array_value.try_into()?, }; let field_data_value: Value = match updatable_page_field.field_data { - PageFieldData::SelectFieldData { select_field_options } => { + PageFieldData::SelectFieldData { + select_field_options, + } => { let mut options: Vec = vec![]; for option in select_field_options { let val: Value = option.try_into()?; @@ -347,8 +370,10 @@ impl PageRepository { } options.into() - }, - PageFieldData::RadioFieldData { radio_field_options } => { + } + PageFieldData::RadioFieldData { + radio_field_options, + } => { let mut options: Vec = vec![]; for option in radio_field_options { let val: Value = option.try_into()?; @@ -356,8 +381,10 @@ impl PageRepository { } options.into() - }, - PageFieldData::CheckboxFieldData { checkbox_field_options } => { + } + PageFieldData::CheckboxFieldData { + checkbox_field_options, + } => { let mut options: Vec = vec![]; for option in checkbox_field_options { let val: Value = option.try_into()?; @@ -365,7 +392,7 @@ impl PageRepository { } options.into() - }, + } PageFieldData::NoneFieldData { none: _ } => "null".into(), }; @@ -376,13 +403,14 @@ impl PageRepository { ("field_type".into(), field_type_value), ("field_content".into(), field_content_value), ("field_data".into(), field_data_value), - ].into(); + ] + .into(); page_fields.push(page_field.into()); } let status: Value = match updatable_page_model.status { - PageStatus::Draft => "Draft".into(), + PageStatus::Draft => "Draft".into(), PageStatus::Published => "Published".into(), }; @@ -390,20 +418,23 @@ impl PageRepository { ("name".into(), updatable_page_model.name.into()), ("identifier".into(), updatable_page_model.identifier.into()), ("status".into(), status), - ("updated_by".into(), updatable_page_model.logged_in_username.clone().into()), + ( + "updated_by".into(), + updatable_page_model.logged_in_username.clone().into(), + ), ("created_by".into(), updatable_page_model.created_by.into()), ("page_fields".into(), page_fields.into()), ("updated_at".into(), Datetime::default().into()), ("created_at".into(), updatable_page_model.created_at.into()), ] - .into(); + .into(); let vars: BTreeMap = [ ("data".into(), data.into()), ("table".into(), PAGE_TABLE.into()), - ("id".into(), updatable_page_model.id.into()) + ("id".into(), updatable_page_model.id.into()), ] - .into(); + .into(); let responses = datastore.execute(sql, database_session, Some(vars)).await?; diff --git a/src/repositories/password_reset_repository.rs b/src/repositories/password_reset_repository.rs index 0e7200c3..fc629767 100644 --- a/src/repositories/password_reset_repository.rs +++ b/src/repositories/password_reset_repository.rs @@ -1,10 +1,10 @@ +use crate::error::Error; +use crate::models::password_rest_model::{CreatablePasswordResetModel, PasswordResetModel}; +use crate::repositories::into_iter_objects; use std::collections::BTreeMap; use surrealdb::dbs::Session; use surrealdb::kvs::Datastore; use surrealdb::sql::{Datetime, Value}; -use crate::error::Error; -use crate::models::password_rest_model::{CreatablePasswordResetModel, PasswordResetModel}; -use crate::repositories::into_iter_objects; const PASSWORD_RESET_TABLE: &str = "password_reset"; @@ -29,7 +29,8 @@ impl PasswordResetRepository { ("token".into(), creatable_password_reset_model.token.into()), ("status".into(), "Active".into()), ("created_at".into(), Datetime::default().into()), - ].into(); + ] + .into(); let vars: BTreeMap = [("data".into(), data.into())].into(); @@ -40,7 +41,8 @@ impl PasswordResetRepository { Some(object) => object, None => Err(Error::Generic("no record found".to_string())), }; - let password_reset_model: crate::error::Result = result_object?.try_into(); + let password_reset_model: crate::error::Result = + result_object?.try_into(); password_reset_model } @@ -57,16 +59,21 @@ impl PasswordResetRepository { ("password_token".into(), token.clone().into()), ("email".into(), email.clone().into()), ("table".into(), PASSWORD_RESET_TABLE.into()), - ].into(); + ] + .into(); let responses = datastore.execute(sql, database_session, Some(vars)).await?; let result_object_option = into_iter_objects(responses)?.next(); let result_object = match result_object_option { Some(object) => object, - None => Err(Error::NotFound(format!("no record found to reset password for email {}", email))), + None => Err(Error::NotFound(format!( + "no record found to reset password for email {}", + email + ))), }; - let password_reset_model: crate::error::Result = result_object?.try_into(); + let password_reset_model: crate::error::Result = + result_object?.try_into(); password_reset_model } @@ -76,7 +83,7 @@ impl PasswordResetRepository { datastore: &Datastore, database_session: &Session, email: String, - token: String + token: String, ) -> crate::error::Result { let sql = " UPDATE type::table($table) SET status=$status WHERE email=$email and token=$password_token"; @@ -100,6 +107,8 @@ impl PasswordResetRepository { return Ok(true); } - Err(Error::Generic(format!("issue while updating password by email: {email}"))) + Err(Error::Generic(format!( + "issue while updating password by email: {email}" + ))) } } diff --git a/src/repositories/role_repository.rs b/src/repositories/role_repository.rs index 4f9650a7..d11e3f80 100644 --- a/src/repositories/role_repository.rs +++ b/src/repositories/role_repository.rs @@ -1,7 +1,9 @@ use std::collections::BTreeMap; use crate::error::{Error, Result}; -use crate::models::role_model::{CreatableRole, PutRoleIdentifierModel, RoleModel, UpdatableRoleModel}; +use crate::models::role_model::{ + CreatableRole, PutRoleIdentifierModel, RoleModel, UpdatableRoleModel, +}; use crate::models::ModelCount; use crate::PER_PAGE; use surrealdb::dbs::Session; @@ -24,20 +26,25 @@ impl RoleRepository { database_session: &Session, start: i64, order_column: String, - order_type: String + order_type: String, ) -> Result> { - let sql = format!("\ + let sql = format!( + "\ SELECT * \ FROM roles \ ORDER {} {} \ LIMIT $limit \ START $start;\ - ", order_column, order_type); + ", + order_column, order_type + ); let vars = BTreeMap::from([ ("limit".into(), PER_PAGE.into()), ("start".into(), start.into()), ]); - let responses = datastore.execute(&sql, database_session, Some(vars)).await?; + let responses = datastore + .execute(&sql, database_session, Some(vars)) + .await?; let mut role_list: Vec = Vec::new(); @@ -54,7 +61,7 @@ impl RoleRepository { &self, datastore: &Datastore, database_session: &Session, - identifier: String + identifier: String, ) -> Result { let sql = "SELECT count(identifier=$identifier) FROM roles GROUP ALL"; @@ -75,7 +82,7 @@ impl RoleRepository { &self, datastore: &Datastore, database_session: &Session, - put_role_identifier_model: PutRoleIdentifierModel + put_role_identifier_model: PutRoleIdentifierModel, ) -> Result { let sql = "UPDATE type::thing($table, $id) SET @@ -86,12 +93,19 @@ impl RoleRepository { "; let vars: BTreeMap = [ - ("identifier".into(), put_role_identifier_model.identifier.into()), + ( + "identifier".into(), + put_role_identifier_model.identifier.into(), + ), ("table".into(), "roles".into()), ("updated_at".into(), Datetime::default().into()), - ("updated_by".into(), put_role_identifier_model.logged_in_username.into()), - ("id".into(), put_role_identifier_model.id.into()) - ].into(); + ( + "updated_by".into(), + put_role_identifier_model.logged_in_username.into(), + ), + ("id".into(), put_role_identifier_model.id.into()), + ] + .into(); let responses = datastore.execute(sql, database_session, Some(vars)).await?; let result_object_option = into_iter_objects(responses)?.next(); @@ -107,7 +121,7 @@ impl RoleRepository { pub async fn all( &self, datastore: &Datastore, - database_session: &Session + database_session: &Session, ) -> Result> { let sql = "SELECT * FROM roles"; @@ -135,9 +149,18 @@ impl RoleRepository { let data: BTreeMap = [ ("name".into(), createable_role_model.name.into()), ("identifier".into(), createable_role_model.identifier.into()), - ("permissions".into(), createable_role_model.permissions.into()), - ("created_by".into(), createable_role_model.logged_in_username.clone().into()), - ("updated_by".into(), createable_role_model.logged_in_username.into()), + ( + "permissions".into(), + createable_role_model.permissions.into(), + ), + ( + "created_by".into(), + createable_role_model.logged_in_username.clone().into(), + ), + ( + "updated_by".into(), + createable_role_model.logged_in_username.into(), + ), ("created_at".into(), Datetime::default().into()), ("updated_at".into(), Datetime::default().into()), ] @@ -197,8 +220,14 @@ impl RoleRepository { let vars = BTreeMap::from([ ("name".into(), updatable_admin_user.name.into()), - ("permissions".into(), updatable_admin_user.permissions.into()), - ("logged_in_user_name".into(), updatable_admin_user.logged_in_username.into()), + ( + "permissions".into(), + updatable_admin_user.permissions.into(), + ), + ( + "logged_in_user_name".into(), + updatable_admin_user.logged_in_username.into(), + ), ("id".into(), updatable_admin_user.id.into()), ("table".into(), "roles".into()), ]); diff --git a/src/repositories/setting_repository.rs b/src/repositories/setting_repository.rs index 89328a10..300e757a 100644 --- a/src/repositories/setting_repository.rs +++ b/src/repositories/setting_repository.rs @@ -1,10 +1,10 @@ +use crate::error::Error; +use crate::models::setting_model::{SettingModel, UpdatableSettingModel}; +use crate::repositories::into_iter_objects; use std::collections::BTreeMap; use surrealdb::dbs::Session; use surrealdb::kvs::Datastore; use surrealdb::sql::Value; -use crate::error::Error; -use crate::models::setting_model::{SettingModel, UpdatableSettingModel}; -use crate::repositories::into_iter_objects; #[derive(Clone)] pub struct SettingRepository {} @@ -17,7 +17,7 @@ impl SettingRepository { pub async fn all( &self, datastore: &Datastore, - database_session: &Session + database_session: &Session, ) -> crate::error::Result> { let sql = "SELECT * FROM settings"; @@ -38,7 +38,7 @@ impl SettingRepository { &self, datastore: &Datastore, database_session: &Session, - updatable_setting: UpdatableSettingModel + updatable_setting: UpdatableSettingModel, ) -> crate::error::Result { let sql = " UPDATE type::thing($table, $id) MERGE { @@ -51,7 +51,10 @@ impl SettingRepository { ("table".into(), "settings".into()), ("value".into(), updatable_setting.value.into()), ("id".into(), updatable_setting.id.into()), - ("logged_in_user_name".into(), updatable_setting.logged_in_username.into()), + ( + "logged_in_user_name".into(), + updatable_setting.logged_in_username.into(), + ), ]); let responses = datastore.execute(sql, database_session, Some(vars)).await?; @@ -61,7 +64,7 @@ impl SettingRepository { None => Err(Error::Generic("no record found".to_string())), }; if result_object.is_ok() { - return Ok(true) + return Ok(true); } Ok(false) @@ -71,9 +74,8 @@ impl SettingRepository { &self, datastore: &Datastore, database_session: &Session, - identifier: String + identifier: String, ) -> crate::error::Result { - let sql = "SELECT * FROM settings WHERE identifier=$data;"; let data: BTreeMap = [("data".into(), identifier.into())].into(); @@ -87,7 +89,5 @@ impl SettingRepository { let setting_model: crate::error::Result = result_object?.try_into(); setting_model - } - } diff --git a/src/responses/component/mod.rs b/src/responses/component/mod.rs index c7ea1d5a..c8657317 100644 --- a/src/responses/component/mod.rs +++ b/src/responses/component/mod.rs @@ -1,7 +1,7 @@ -use serde::Serialize; use crate::models::component_model::ComponentModel; +use serde::Serialize; #[derive(Serialize)] pub struct PutComponentIdentifierResponse { - pub component: ComponentModel -} \ No newline at end of file + pub component: ComponentModel, +} diff --git a/src/responses/mod.rs b/src/responses/mod.rs index 3f550517..f27067c2 100644 --- a/src/responses/mod.rs +++ b/src/responses/mod.rs @@ -1,12 +1,12 @@ use serde::Serialize; -pub mod role; -pub mod page; pub mod component; pub mod model; +pub mod page; +pub mod role; #[derive(Serialize)] pub struct ApiResponse { pub status: bool, - pub data: R -} \ No newline at end of file + pub data: R, +} diff --git a/src/responses/model/mod.rs b/src/responses/model/mod.rs index dc8c6414..80ce8608 100644 --- a/src/responses/model/mod.rs +++ b/src/responses/model/mod.rs @@ -1,7 +1,7 @@ -use serde::Serialize; use crate::models::model_model::ModelModel; +use serde::Serialize; #[derive(Serialize)] pub struct PutModelIdentifierResponse { - pub model: ModelModel -} \ No newline at end of file + pub model: ModelModel, +} diff --git a/src/responses/page/mod.rs b/src/responses/page/mod.rs index fc997fbe..3393c7e9 100644 --- a/src/responses/page/mod.rs +++ b/src/responses/page/mod.rs @@ -1,27 +1,25 @@ -use serde::Serialize; +use crate::error::Result; use crate::models::page_model::PageModel; use crate::responses::ApiResponse; -use crate::error::Result; +use serde::Serialize; #[derive(Serialize)] pub struct PutPageIdentifierResponse { - pub page: PageModel + pub page: PageModel, } - #[derive(Serialize)] pub struct FetchPageCmsResponse { - pub page_model: PageModel + pub page_model: PageModel, } impl PageModel { - pub fn convert_to_response(&self) -> Result> - { + pub fn convert_to_response(&self) -> Result> { Ok(ApiResponse { status: true, data: FetchPageCmsResponse { - page_model: self.to_owned() - } + page_model: self.to_owned(), + }, }) } -} \ No newline at end of file +} diff --git a/src/responses/role/mod.rs b/src/responses/role/mod.rs index d8a74ed9..7b0f051c 100644 --- a/src/responses/role/mod.rs +++ b/src/responses/role/mod.rs @@ -1,7 +1,7 @@ -use serde::Serialize; use crate::models::role_model::RoleModel; +use serde::Serialize; #[derive(Serialize)] pub struct PutRoleIdentifierResponse { - pub role: RoleModel -} \ No newline at end of file + pub role: RoleModel, +} diff --git a/src/services/admin_user_service.rs b/src/services/admin_user_service.rs index 06c2c81c..ef3b79ed 100644 --- a/src/services/admin_user_service.rs +++ b/src/services/admin_user_service.rs @@ -1,46 +1,44 @@ -use argon2::{Argon2, PasswordHash, PasswordHasher, PasswordVerifier}; -use argon2::password_hash::SaltString; -use lettre::{AsyncTransport, Message}; -use lettre::message::{header, MultiPart, SinglePart}; -use rand::distributions::{Alphanumeric, DistString}; +use crate::api::handlers::admin_user::admin_user_forgot_password_api_handler::ForgotPasswordViewModel; +use crate::error::Error; +use crate::models::admin_user_model::CreatableAdminUserModel; +use crate::models::password_rest_model::{CreatablePasswordResetModel, PasswordResetModel}; +use crate::models::token_claim_model::LoggedInUser; +use crate::models::ModelCount; +use crate::providers::avored_template_provider::AvoRedTemplateProvider; +use crate::repositories::password_reset_repository::PasswordResetRepository; +use crate::repositories::role_repository::RoleRepository; use crate::{ error::Result, models::{ - admin_user_model::{ - AdminUserModel, AdminUserPagination, UpdatableAdminUserModel, - }, + admin_user_model::{AdminUserModel, AdminUserPagination, UpdatableAdminUserModel}, Pagination, }, providers::avored_database_provider::DB, repositories::admin_user_repository::AdminUserRepository, PER_PAGE, }; -use crate::api::handlers::admin_user::admin_user_forgot_password_api_handler::ForgotPasswordViewModel; -use crate::error::Error; -use crate::models::admin_user_model::CreatableAdminUserModel; -use crate::models::ModelCount; -use crate::models::password_rest_model::{CreatablePasswordResetModel, PasswordResetModel}; -use crate::models::token_claim_model::LoggedInUser; -use crate::providers::avored_template_provider::AvoRedTemplateProvider; -use crate::repositories::password_reset_repository::PasswordResetRepository; -use crate::repositories::role_repository::RoleRepository; +use argon2::password_hash::SaltString; +use argon2::{Argon2, PasswordHash, PasswordHasher, PasswordVerifier}; +use lettre::message::{header, MultiPart, SinglePart}; +use lettre::{AsyncTransport, Message}; +use rand::distributions::{Alphanumeric, DistString}; pub struct AdminUserService { admin_user_repository: AdminUserRepository, role_repository: RoleRepository, - password_reset_repository: PasswordResetRepository + password_reset_repository: PasswordResetRepository, } impl AdminUserService { pub fn new( admin_user_repository: AdminUserRepository, role_repository: RoleRepository, - password_reset_repository: PasswordResetRepository + password_reset_repository: PasswordResetRepository, ) -> Result { Ok(AdminUserService { admin_user_repository, role_repository, - password_reset_repository + password_reset_repository, }) } } @@ -50,9 +48,8 @@ impl AdminUserService { (datastore, database_session): &DB, template: &AvoRedTemplateProvider, react_admin_url: &str, - to_address: String - ) -> Result - { + to_address: String, + ) -> Result { let from_address = String::from("info@avored.com"); let email_subject = "Forgot your password?"; @@ -70,10 +67,11 @@ impl AdminUserService { .create_password_reset(datastore, database_session, creatable_password_reset_model) .await?; - let link = format!("{react_admin_url}/admin/reset-password/{}", password_reset_model.token); - let data = ForgotPasswordViewModel { - link - }; + let link = format!( + "{react_admin_url}/admin/reset-password/{}", + password_reset_model.token + ); + let data = ForgotPasswordViewModel { link }; let forgot_password_email_content = template.handlebars.render("forgot-password", &data)?; @@ -102,22 +100,27 @@ impl AdminUserService { } } - pub fn compare_password(&self, plain_password: String, encrypted_password: String) -> Result - { + pub fn compare_password( + &self, + plain_password: String, + encrypted_password: String, + ) -> Result { let argon2 = Argon2::default(); let parsed_hash = PasswordHash::new(&encrypted_password)?; - Ok(argon2.verify_password(plain_password.as_bytes(), &parsed_hash).is_ok()) + Ok(argon2 + .verify_password(plain_password.as_bytes(), &parsed_hash) + .is_ok()) } pub async fn has_permission( &self, logged_in_user: LoggedInUser, - permission_identifier: String + permission_identifier: String, ) -> Result { if logged_in_user.admin_user_model.is_super_admin { - return Ok(true) + return Ok(true); } let mut has_permission = false; for role in logged_in_user.admin_user_model.roles { @@ -155,7 +158,7 @@ impl AdminUserService { &self, (datastore, database_session): &DB, email: String, - token: String + token: String, ) -> Result { self.password_reset_repository .get_password_reset_by_email(datastore, database_session, email, token) @@ -166,7 +169,7 @@ impl AdminUserService { &self, (datastore, database_session): &DB, new_password: String, - email: String + email: String, ) -> Result { self.admin_user_repository .update_password_by_email(datastore, database_session, new_password, email) @@ -177,7 +180,7 @@ impl AdminUserService { &self, (datastore, database_session): &DB, email: String, - token: String + token: String, ) -> Result { self.password_reset_repository .expire_password_token_by_email_and_token(datastore, database_session, email, token) @@ -188,22 +191,41 @@ impl AdminUserService { &self, (datastore, database_session): &DB, updatable_admin_user_model: UpdatableAdminUserModel, - logged_in_user: LoggedInUser + logged_in_user: LoggedInUser, ) -> Result { - let mut admin_user_model = self.admin_user_repository - .update_admin_user(datastore, database_session, updatable_admin_user_model.clone()) + let mut admin_user_model = self + .admin_user_repository + .update_admin_user( + datastore, + database_session, + updatable_admin_user_model.clone(), + ) .await?; for role_id in updatable_admin_user_model.clone().role_ids { self.admin_user_repository - .detach_admin_user_with_role(datastore, database_session, admin_user_model.clone().id, role_id) + .detach_admin_user_with_role( + datastore, + database_session, + admin_user_model.clone().id, + role_id, + ) .await?; } for role_id in updatable_admin_user_model.role_ids { - let role_model = self.role_repository.find_by_id(datastore, database_session, role_id).await?; + let role_model = self + .role_repository + .find_by_id(datastore, database_session, role_id) + .await?; self.admin_user_repository - .attach_admin_user_with_role(datastore, database_session, admin_user_model.clone().id, role_model.clone().id, logged_in_user.clone()) + .attach_admin_user_with_role( + datastore, + database_session, + admin_user_model.clone().id, + role_model.clone().id, + logged_in_user.clone(), + ) .await?; admin_user_model.roles.push(role_model); @@ -248,7 +270,7 @@ impl AdminUserService { }; let mut order_column = "id"; - let mut order_type = "ASC"; + let mut order_type = "ASC"; let mut parts = order.split(':'); if parts.clone().count() == 2 { order_column = parts.clone().nth(0).unwrap_or(""); @@ -257,7 +279,13 @@ impl AdminUserService { let admin_users = self .admin_user_repository - .paginate(datastore, database_session, start, order_column.to_string(), order_type.to_string()) + .paginate( + datastore, + database_session, + start, + order_column.to_string(), + order_type.to_string(), + ) .await?; Ok(AdminUserPagination { @@ -280,15 +308,29 @@ impl AdminUserService { &self, (datastore, database_session): &DB, creatable_admin_user_model: CreatableAdminUserModel, - logged_in_user: LoggedInUser + logged_in_user: LoggedInUser, ) -> Result { - let mut admin_user_model = self.admin_user_repository - .create_admin_user(datastore, database_session, creatable_admin_user_model.clone()) + let mut admin_user_model = self + .admin_user_repository + .create_admin_user( + datastore, + database_session, + creatable_admin_user_model.clone(), + ) .await?; for role_id in creatable_admin_user_model.role_ids { - let role_model = self.role_repository.find_by_id(datastore, database_session, role_id).await?; + let role_model = self + .role_repository + .find_by_id(datastore, database_session, role_id) + .await?; self.admin_user_repository - .attach_admin_user_with_role(datastore, database_session, admin_user_model.clone().id, role_model.clone().id, logged_in_user.clone()) + .attach_admin_user_with_role( + datastore, + database_session, + admin_user_model.clone().id, + role_model.clone().id, + logged_in_user.clone(), + ) .await?; admin_user_model.roles.push(role_model); @@ -300,15 +342,13 @@ impl AdminUserService { pub fn get_password_hash_from_raw_password( &self, raw_password: String, - password_salt: &String - ) -> Result{ - + password_salt: &String, + ) -> Result { let password = raw_password.as_bytes(); let salt = SaltString::from_b64(password_salt)?; let argon2 = Argon2::default(); - let password_hash = argon2 - .hash_password(password, &salt)?.to_string(); + let password_hash = argon2.hash_password(password, &salt)?.to_string(); Ok(password_hash) } diff --git a/src/services/asset_service.rs b/src/services/asset_service.rs index 46e0e19f..ecee1ca5 100644 --- a/src/services/asset_service.rs +++ b/src/services/asset_service.rs @@ -1,7 +1,10 @@ -use crate::{error::Result, PER_PAGE, providers::avored_database_provider::DB, repositories::asset_repository::AssetRepository}; -use crate::models::asset_model::{AssetPagination, CreatableAssetModel, MetaDataType, AssetModel}; -use crate::models::Pagination; +use crate::models::asset_model::{AssetModel, AssetPagination, CreatableAssetModel, MetaDataType}; use crate::models::token_claim_model::LoggedInUser; +use crate::models::Pagination; +use crate::{ + error::Result, providers::avored_database_provider::DB, + repositories::asset_repository::AssetRepository, PER_PAGE, +}; pub struct AssetService { asset_repository: AssetRepository, @@ -17,7 +20,7 @@ impl AssetService { &self, (datastore, database_session): &DB, current_page: i64, - parent_id: String + parent_id: String, ) -> Result { let start = (current_page - 1) * PER_PAGE; let to = start + PER_PAGE; @@ -82,7 +85,7 @@ impl AssetService { pub async fn find_by_id( &self, (datastore, database_session): &DB, - asset_id: &str + asset_id: &str, ) -> Result { self.asset_repository .find_by_id(datastore, database_session, asset_id) @@ -104,30 +107,28 @@ impl AssetService { db: &DB, name: String, parent_id: String, - logged_in_user: LoggedInUser + logged_in_user: LoggedInUser, ) -> Result { let (datastore, database_session) = db; let mut full_path = format!("public/upload/{}", name.clone()); if !parent_id.is_empty() { - let asset = self - .find_by_id(db, &parent_id) - .await?; + let asset = self.find_by_id(db, &parent_id).await?; - full_path = format!("{}/{}",asset.new_path, name.clone()); + full_path = format!("{}/{}", asset.new_path, name.clone()); } tokio::fs::create_dir_all(&format!("./{}", full_path.clone())).await?; // @todo fix this default color???? - let color= String::from("text-gray-400"); + let color = String::from("text-gray-400"); let creatable_asset_model = CreatableAssetModel { logged_in_username: logged_in_user.email, parent_id, name: name.clone(), asset_type: "FOLDER".to_string(), - metadata: MetaDataType::FolderTypeMetaData {color}, + metadata: MetaDataType::FolderTypeMetaData { color }, }; self.asset_repository @@ -140,10 +141,16 @@ impl AssetService { (datastore, database_session): &DB, name: &str, asset_id: &str, - logged_in_username: &str + logged_in_username: &str, ) -> Result { self.asset_repository - .update_asset_path(datastore, database_session, name, asset_id, logged_in_username) + .update_asset_path( + datastore, + database_session, + name, + asset_id, + logged_in_username, + ) .await } } diff --git a/src/services/cms_service.rs b/src/services/cms_service.rs index 735c5850..0a90067e 100644 --- a/src/services/cms_service.rs +++ b/src/services/cms_service.rs @@ -1,51 +1,50 @@ -use lettre::{AsyncTransport, Message}; -use lettre::message::{header, MultiPart, SinglePart}; -use tracing::error; use crate::api::handlers::cms::sent_contact_us_email_handler::SentContactUsEmailRequest; +use crate::error::Error; use crate::error::Result; use crate::providers::avored_template_provider::AvoRedTemplateProvider; -use crate::error::Error; +use lettre::message::{header, MultiPart, SinglePart}; +use lettre::{AsyncTransport, Message}; +use tracing::error; pub struct CmsService {} - impl CmsService { pub fn new() -> Result { - Ok(CmsService { }) + Ok(CmsService {}) } } impl CmsService { - pub async fn sent_contact_us_email ( + pub async fn sent_contact_us_email( &self, template: &AvoRedTemplateProvider, - payload: SentContactUsEmailRequest + payload: SentContactUsEmailRequest, ) -> Result { let from_address = String::from("info@avored.com"); let to_address = String::from("ind.purvesh@gmail.com"); let email_subject = String::from("Contact us message"); - let sent_contact_email_message_body = template.handlebars.render("contact-us-email", &payload)?; + let sent_contact_email_message_body = + template.handlebars.render("contact-us-email", &payload)?; let email = Message::builder() .from(from_address.parse()?) .to(to_address.parse()?) .subject(email_subject) .multipart( - MultiPart::alternative() - .singlepart( - SinglePart::builder() - .header(header::ContentType::TEXT_HTML) - .body(sent_contact_email_message_body), - ), + MultiPart::alternative().singlepart( + SinglePart::builder() + .header(header::ContentType::TEXT_HTML) + .body(sent_contact_email_message_body), + ), )?; // Send the email match template.mailer.send(email).await { Ok(_) => Ok(true), - Err(err) => { + Err(err) => { error!("there is an issue with sending an email via smtp: {err:?}"); Err(Error::Generic(String::from("error while sending an email"))) - }, + } } } -} \ No newline at end of file +} diff --git a/src/services/collection_service.rs b/src/services/collection_service.rs new file mode 100644 index 00000000..e1b296e7 --- /dev/null +++ b/src/services/collection_service.rs @@ -0,0 +1,140 @@ +use crate::error::Result; +use crate::models::collection_model::{CollectionModel, CollectionPagination, CreatableCollection, PutCollectionIdentifierModel, UpdatableCollection}; +use crate::models::{ModelCount, Pagination}; +use crate::providers::avored_database_provider::DB; +use crate::repositories::collection_repository::CollectionRepository; +use crate::PER_PAGE; + +pub struct CollectionService { + collection_repository: CollectionRepository, +} + +impl CollectionService { + pub(crate) async fn all_collections( + &self, + (datastore, database_session): &DB + ) -> Result> { + self.collection_repository + .all_collection(datastore, database_session) + .await + } +} + +impl CollectionService { + pub fn new(collection_repository: CollectionRepository) -> Result { + Ok(CollectionService { + collection_repository, + }) + } +} +impl CollectionService { + pub async fn create_collection( + &self, + (datastore, database_session): &DB, + creatable_collection_collection: CreatableCollection, + ) -> Result { + self.collection_repository + .create_collection(datastore, database_session, creatable_collection_collection) + .await + } + + pub async fn paginate( + &self, + (datastore, database_session): &DB, + current_page: i64, + order: String, + ) -> Result { + let start = current_page * PER_PAGE; + let to = start + PER_PAGE; + + let collection_count = self + .collection_repository + .get_total_count(datastore, database_session) + .await?; + + let mut has_next_page = false; + if collection_count.total > to { + has_next_page = true; + }; + let mut has_previous_page = false; + if current_page > 1 { + has_previous_page = true; + }; + + let pagination = Pagination { + total: collection_count.total, + per_page: PER_PAGE, + current_page, + from: (start + 1), + to, + has_previous_page, + next_page_number: (current_page + 1), + has_next_page, + previous_page_number: (current_page - 1), + }; + + let mut order_column = "id"; + let mut order_type = "ASC"; + let mut parts = order.split(':'); + if parts.clone().count() == 2 { + order_column = parts.clone().nth(0).unwrap_or(""); + order_type = parts.nth(1).unwrap_or(""); + } + + let paginated_collections = self + .collection_repository + .paginate( + datastore, + database_session, + start, + order_column.to_string(), + order_type.to_string(), + ) + .await?; + + Ok(CollectionPagination { + data: paginated_collections, + pagination, + }) + } + + pub async fn find_by_id( + &self, + (datastore, database_session): &DB, + id: String, + ) -> Result { + self.collection_repository + .find_by_id(datastore, database_session, id) + .await + } + + pub async fn update_collection_identifier( + &self, + (datastore, database_session): &DB, + put_collection_identifier_collection: PutCollectionIdentifierModel + ) -> Result { + self.collection_repository + .update_collection_identifier(datastore, database_session, put_collection_identifier_collection) + .await + } + + pub async fn count_of_identifier( + &self, + (datastore, database_session): &DB, + identifier: String, + ) -> Result { + self.collection_repository + .count_of_identifier(datastore, database_session, identifier) + .await + } + + pub async fn update_collection( + &self, + (datastore, database_session): &DB, + updatable_collection_collection: UpdatableCollection, + ) -> Result { + self.collection_repository + .update_collection(datastore, database_session, updatable_collection_collection) + .await + } +} diff --git a/src/services/component_service.rs b/src/services/component_service.rs index e40e7801..8bffdca8 100644 --- a/src/services/component_service.rs +++ b/src/services/component_service.rs @@ -1,3 +1,5 @@ +use crate::models::component_model::PutComponentIdentifierModel; +use crate::models::ModelCount; use crate::{ error::Result, models::{ @@ -10,8 +12,6 @@ use crate::{ repositories::component_repository::ComponentRepository, PER_PAGE, }; -use crate::models::component_model::PutComponentIdentifierModel; -use crate::models::ModelCount; pub struct ComponentService { component_repository: ComponentRepository, @@ -29,7 +29,7 @@ impl ComponentService { &self, (datastore, database_session): &DB, current_page: i64, - order: String + order: String, ) -> Result { let start = current_page * PER_PAGE; let to = start + PER_PAGE; @@ -61,7 +61,7 @@ impl ComponentService { }; let mut order_column = "id"; - let mut order_type = "ASC"; + let mut order_type = "ASC"; let mut parts = order.split(':'); if parts.clone().count() == 2 { order_column = parts.clone().nth(0).unwrap_or(""); @@ -70,7 +70,13 @@ impl ComponentService { let components = self .component_repository - .paginate(datastore, database_session, start, order_column.to_string(), order_type.to_string()) + .paginate( + datastore, + database_session, + start, + order_column.to_string(), + order_type.to_string(), + ) .await?; Ok(ComponentPagination { @@ -78,10 +84,7 @@ impl ComponentService { pagination, }) } - pub async fn all( - &self, - (datastore, database_session): &DB - ) -> Result> { + pub async fn all(&self, (datastore, database_session): &DB) -> Result> { self.component_repository .all(datastore, database_session) .await @@ -129,10 +132,14 @@ impl ComponentService { pub async fn update_component_identifier( &self, (datastore, database_session): &DB, - put_component_identifier_model: PutComponentIdentifierModel + put_component_identifier_model: PutComponentIdentifierModel, ) -> Result { self.component_repository - .update_component_identifier(datastore, database_session, put_component_identifier_model) + .update_component_identifier( + datastore, + database_session, + put_component_identifier_model, + ) .await } } diff --git a/src/services/content_service.rs b/src/services/content_service.rs new file mode 100644 index 00000000..4c4f2d11 --- /dev/null +++ b/src/services/content_service.rs @@ -0,0 +1,133 @@ +use crate::models::content_model::{ContentModel, ContentPagination, CreatableContentModel, PutContentIdentifierModel, UpdatableContentModel}; +use crate::providers::avored_database_provider::DB; +use crate::repositories::content_repository::ContentRepository; +use crate::error::Result; +use crate::models::{ModelCount, Pagination}; +use crate::models::page_model::{PageModel, PutPageIdentifierModel}; +use crate::PER_PAGE; + +pub struct ContentService { + content_repository: ContentRepository, +} + +impl ContentService { + + pub async fn update_content( + &self, + (datastore, database_session): &DB, + updatable_page_model: UpdatableContentModel, + ) -> Result { + self.content_repository + .update_content(datastore, database_session, updatable_page_model) + .await + } + + pub(crate) async fn count_of_identifier( + &self, + (datastore, database_session): &DB, + identifier: &str, + collection_type: &str + ) -> Result { + self.content_repository + .count_of_identifier(datastore, database_session, collection_type, identifier) + .await + } + + pub(crate) async fn update_content_identifier( + &self, + (datastore, database_session): &DB, + put_content_identifier_model: PutContentIdentifierModel, + ) -> Result { + self.content_repository + .update_content_identifier(datastore, database_session, put_content_identifier_model) + .await + } + + + pub(crate) async fn find_by_id( + &self, + (datastore, database_session): &DB, + content_type: String, + id: &str, + ) -> Result { + self.content_repository + .find_by_id(datastore, database_session, content_type, id) + .await + } + pub(crate) async fn paginate( + &self, + (datastore, database_session): &DB, + content_type: &str, + current_page: i64, + order: String, + ) -> Result { + let start = current_page * PER_PAGE; + let to = start + PER_PAGE; + + let admin_user_count = self + .content_repository + .get_total_count(datastore, database_session, content_type) + .await?; + + let mut has_next_page = false; + if admin_user_count.total > to { + has_next_page = true; + }; + let mut has_previous_page = false; + if current_page > 1 { + has_previous_page = true; + }; + + let pagination = Pagination { + total: admin_user_count.total, + per_page: PER_PAGE, + current_page, + from: (start + 1), + to, + has_previous_page, + next_page_number: (current_page + 1), + has_next_page, + previous_page_number: (current_page - 1), + }; + + let mut order_column = "id"; + let mut order_type = "ASC"; + let mut parts = order.split(':'); + if parts.clone().count() == 2 { + order_column = parts.clone().nth(0).unwrap_or(""); + order_type = parts.nth(1).unwrap_or(""); + } + let content = self + .content_repository + .paginate( + datastore, + database_session, + content_type, + start, + order_column.to_string(), + order_type.to_string(), + ) + .await?; + + Ok(ContentPagination { + data: content, + pagination, + }) + } + + pub(crate) async fn create_content( + &self, + (datastore, database_session): &DB, + creatable_page_model: CreatableContentModel, + ) -> Result { + self.content_repository + .create_content(datastore, database_session, creatable_page_model) + .await + } + + pub fn new(content_repository: ContentRepository) -> Result { + Ok(ContentService { content_repository }) + } +} + + diff --git a/src/services/mod.rs b/src/services/mod.rs index 52a8bf6c..ce0cb61d 100644 --- a/src/services/mod.rs +++ b/src/services/mod.rs @@ -1,8 +1,10 @@ pub mod admin_user_service; +pub mod asset_service; +pub mod cms_service; +pub mod collection_service; pub mod component_service; +pub mod model_service; pub mod page_service; pub mod role_service; -pub mod asset_service; pub mod setting_service; -pub mod model_service; -pub mod cms_service; +pub mod content_service; diff --git a/src/services/model_service.rs b/src/services/model_service.rs index 290fe654..d68d2e3f 100644 --- a/src/services/model_service.rs +++ b/src/services/model_service.rs @@ -1,8 +1,12 @@ -use crate::{error::Result, models::{ - model_model::{CreatableModel, ModelModel}, -}, PER_PAGE, providers::avored_database_provider::DB, repositories::model_repository::ModelRepository}; use crate::models::model_model::{ModelPagination, PutModelIdentifierModel, UpdatableModelModel}; use crate::models::{ModelCount, Pagination}; +use crate::{ + error::Result, + models::model_model::{CreatableModel, ModelModel}, + providers::avored_database_provider::DB, + repositories::model_repository::ModelRepository, + PER_PAGE, +}; pub struct ModelService { model_repository: ModelRepository, @@ -14,7 +18,6 @@ impl ModelService { } } impl ModelService { - pub async fn create_model( &self, (datastore, database_session): &DB, @@ -29,7 +32,7 @@ impl ModelService { &self, (datastore, database_session): &DB, current_page: i64, - order: String + order: String, ) -> Result { let start = current_page * PER_PAGE; let to = start + PER_PAGE; @@ -61,7 +64,7 @@ impl ModelService { }; let mut order_column = "id"; - let mut order_type = "ASC"; + let mut order_type = "ASC"; let mut parts = order.split(':'); if parts.clone().count() == 2 { order_column = parts.clone().nth(0).unwrap_or(""); @@ -70,7 +73,13 @@ impl ModelService { let paginated_models = self .model_repository - .paginate(datastore, database_session, start, order_column.to_string(), order_type.to_string()) + .paginate( + datastore, + database_session, + start, + order_column.to_string(), + order_type.to_string(), + ) .await?; Ok(ModelPagination { @@ -89,11 +98,10 @@ impl ModelService { .await } - pub async fn update_model_identifier( &self, (datastore, database_session): &DB, - put_model_identifier_model: PutModelIdentifierModel + put_model_identifier_model: PutModelIdentifierModel, ) -> Result { self.model_repository .update_model_identifier(datastore, database_session, put_model_identifier_model) @@ -110,7 +118,6 @@ impl ModelService { .await } - pub async fn update_model( &self, (datastore, database_session): &DB, diff --git a/src/services/page_service.rs b/src/services/page_service.rs index 3b6a54bf..b8c79673 100644 --- a/src/services/page_service.rs +++ b/src/services/page_service.rs @@ -1,3 +1,7 @@ +use crate::models::page_model::{ + CreatablePageModel, PageModel, PutPageIdentifierModel, UpdatablePageModel, +}; +use crate::models::ModelCount; use crate::{ error::Result, models::{page_model::PagePagination, Pagination}, @@ -5,8 +9,6 @@ use crate::{ repositories::page_repository::PageRepository, PER_PAGE, }; -use crate::models::ModelCount; -use crate::models::page_model::{CreatablePageModel, PageModel, UpdatablePageModel, PutPageIdentifierModel}; pub struct PageService { page_repository: PageRepository, @@ -22,7 +24,7 @@ impl PageService { &self, (datastore, database_session): &DB, current_page: i64, - order: String + order: String, ) -> Result { let start = current_page * PER_PAGE; let to = start + PER_PAGE; @@ -54,7 +56,7 @@ impl PageService { }; let mut order_column = "id"; - let mut order_type = "ASC"; + let mut order_type = "ASC"; let mut parts = order.split(':'); if parts.clone().count() == 2 { order_column = parts.clone().nth(0).unwrap_or(""); @@ -62,7 +64,13 @@ impl PageService { } let pages = self .page_repository - .paginate(datastore, database_session, start, order_column.to_string(), order_type.to_string()) + .paginate( + datastore, + database_session, + start, + order_column.to_string(), + order_type.to_string(), + ) .await?; Ok(PagePagination { @@ -71,14 +79,19 @@ impl PageService { }) } - pub async fn remove_by_id(&self, (datastore, database_session): &DB, id: &String) -> Result { - self.page_repository.remove_by_id(datastore,database_session, id).await?; + pub async fn remove_by_id( + &self, + (datastore, database_session): &DB, + id: &String, + ) -> Result { + self.page_repository + .remove_by_id(datastore, database_session, id) + .await?; Ok(true) } - pub async fn all(&self, (datastore, database_session): &DB) -> Result> { - self.page_repository.all(datastore,database_session).await + self.page_repository.all(datastore, database_session).await } pub async fn find_by_id( @@ -94,7 +107,7 @@ impl PageService { pub async fn create_page( &self, (datastore, database_session): &DB, - creatable_page_model: CreatablePageModel + creatable_page_model: CreatablePageModel, ) -> Result { self.page_repository .create_page(datastore, database_session, creatable_page_model) @@ -104,7 +117,7 @@ impl PageService { pub async fn update_page( &self, (datastore, database_session): &DB, - updatable_page_model: UpdatablePageModel + updatable_page_model: UpdatablePageModel, ) -> Result { self.page_repository .update_page(datastore, database_session, updatable_page_model) @@ -124,7 +137,7 @@ impl PageService { pub async fn update_page_identifier( &self, (datastore, database_session): &DB, - put_page_identifier_model: PutPageIdentifierModel + put_page_identifier_model: PutPageIdentifierModel, ) -> Result { self.page_repository .update_page_identifier(datastore, database_session, put_page_identifier_model) diff --git a/src/services/role_service.rs b/src/services/role_service.rs index 9f6eb809..e504a842 100644 --- a/src/services/role_service.rs +++ b/src/services/role_service.rs @@ -1,3 +1,5 @@ +use crate::models::role_model::{PutRoleIdentifierModel, RoleOptionModel}; +use crate::models::ModelCount; use crate::{ error::Result, models::{ @@ -8,8 +10,6 @@ use crate::{ repositories::role_repository::RoleRepository, PER_PAGE, }; -use crate::models::ModelCount; -use crate::models::role_model::{PutRoleIdentifierModel, RoleOptionModel}; pub struct RoleService { role_repository: RoleRepository, @@ -21,17 +21,17 @@ impl RoleService { } } impl RoleService { - pub async fn all( - &self, - (datastore, database_session): &DB, - ) -> Result> { - let roles = self.role_repository.all(datastore, database_session).await?; + pub async fn all(&self, (datastore, database_session): &DB) -> Result> { + let roles = self + .role_repository + .all(datastore, database_session) + .await?; let mut role_options: Vec = vec![]; for role in roles { let role_option_model = RoleOptionModel { value: role.id, - label: role.name + label: role.name, }; role_options.push(role_option_model); } @@ -43,7 +43,7 @@ impl RoleService { &self, (datastore, database_session): &DB, current_page: i64, - order: String + order: String, ) -> Result { let start = current_page * PER_PAGE; let to = start + PER_PAGE; @@ -75,7 +75,7 @@ impl RoleService { }; let mut order_column = "id"; - let mut order_type = "ASC"; + let mut order_type = "ASC"; let mut parts = order.split(':'); if parts.clone().count() == 2 { order_column = parts.clone().nth(0).unwrap_or(""); @@ -84,7 +84,13 @@ impl RoleService { let roles = self .role_repository - .paginate(datastore, database_session, start, order_column.to_string(), order_type.to_string()) + .paginate( + datastore, + database_session, + start, + order_column.to_string(), + order_type.to_string(), + ) .await?; Ok(RolePagination { @@ -106,7 +112,7 @@ impl RoleService { pub async fn update_role_identifier( &self, (datastore, database_session): &DB, - put_role_identifier_model: PutRoleIdentifierModel + put_role_identifier_model: PutRoleIdentifierModel, ) -> Result { self.role_repository .update_role_identifier(datastore, database_session, put_role_identifier_model) diff --git a/src/services/setting_service.rs b/src/services/setting_service.rs index e96d16a5..0935bf66 100644 --- a/src/services/setting_service.rs +++ b/src/services/setting_service.rs @@ -1,28 +1,17 @@ -use crate::{ - error::Result, - repositories::setting_repository::SettingRepository -}; use crate::models::setting_model::{SettingModel, UpdatableSettingModel}; use crate::providers::avored_database_provider::DB; - +use crate::{error::Result, repositories::setting_repository::SettingRepository}; pub struct SettingService { - setting_repository: SettingRepository + setting_repository: SettingRepository, } impl SettingService { - pub fn new( - setting_repository: SettingRepository, - ) -> Result { - Ok(SettingService { - setting_repository - }) + pub fn new(setting_repository: SettingRepository) -> Result { + Ok(SettingService { setting_repository }) } - pub async fn all( - &self, - (datastore, database_session): &DB - ) -> Result> { + pub async fn all(&self, (datastore, database_session): &DB) -> Result> { self.setting_repository .all(datastore, database_session) .await @@ -31,7 +20,7 @@ impl SettingService { pub async fn find_by_identifier( &self, (datastore, database_session): &DB, - identifier: String + identifier: String, ) -> Result { self.setting_repository .find_by_identifier(datastore, database_session, identifier) @@ -41,12 +30,11 @@ impl SettingService { pub async fn update_setting( &self, (datastore, database_session): &DB, - updatable_setting: UpdatableSettingModel + updatable_setting: UpdatableSettingModel, ) -> Result { self.setting_repository .update_setting(datastore, database_session, updatable_setting) .await } } -impl SettingService { -} +impl SettingService {}