Browse Source

many changes - mainly moving to refined newtypes

other changes -
add dependencies for implementing refined newtypes -
 derive-more/validators
rename Plain logger format to Pretty
rename regexs package to regex
main
Rohan Sircar 1 year ago
parent
commit
b6801e1821
  1. 2
      .env
  2. 397
      Cargo.lock
  3. 9
      Cargo.toml
  4. 20
      README.md
  5. 50
      src/actions/users.rs
  6. 6
      src/errors/domain_error.rs
  7. 6
      src/lib.rs
  8. 2
      src/main.rs
  9. 4
      src/models/api_response.rs
  10. 131
      src/models/users.rs
  11. 99
      src/routes/users.rs
  12. 128
      src/services/user_service.rs
  13. 4
      src/utils.rs
  14. 2
      src/utils/regex.rs

2
.env

@ -5,5 +5,5 @@ ACTIX_DEMO_RUST_LOG=debug
ACTIX_DEMO_TEST_RUST_LOG=debug
ACTIX_DEMO_HTTP_HOST=127.0.0.1
ACTIX_DEMO_HASH_COST=8
ACTIX_DEMO_LOGGER_FORMAT=plain
ACTIX_DEMO_LOGGER_FORMAT=pretty

397
Cargo.lock

@ -54,7 +54,9 @@ dependencies = [
"chrono",
"custom_error",
"derive-new",
"derive_more",
"diesel",
"diesel-derive-newtype",
"diesel-tracing",
"diesel_migrations",
"dotenv",
@ -77,8 +79,8 @@ dependencies = [
"tracing-log",
"tracing-subscriber",
"uuid",
"validator",
"validator_derive",
"validators",
"validators-derive",
]
[[package]]
@ -168,8 +170,8 @@ version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4ca8ce00b267af8ccebbd647de0d61e0674b6e61185cc7a592ff88772bed655"
dependencies = [
"quote",
"syn",
"quote 1.0.9",
"syn 1.0.69",
]
[[package]]
@ -347,9 +349,9 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad26f77093333e0e7c6ffe54ebe3582d908a104e448723eec6d43d08b07143fb"
dependencies = [
"proc-macro2",
"quote",
"syn",
"proc-macro2 1.0.26",
"quote 1.0.9",
"syn 1.0.69",
]
[[package]]
@ -447,15 +449,21 @@ version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28b2cd92db5cbd74e8e5028f7e27dd7aa3090e89e4f2a197cc7c8dfb69c7063b"
[[package]]
name = "arrayvec"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
[[package]]
name = "async-trait"
version = "0.1.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b98e84bbb4cbcdd97da190ba0c58a1bb0de2c1fdf67d159e192ed766aeca722"
dependencies = [
"proc-macro2",
"quote",
"syn",
"proc-macro2 1.0.26",
"quote 1.0.9",
"syn 1.0.69",
]
[[package]]
@ -642,10 +650,10 @@ dependencies = [
"num-traits",
"proc-macro-error",
"proc-macro-hack",
"proc-macro2",
"quote",
"proc-macro2 1.0.26",
"quote 1.0.9",
"serde_json",
"syn",
"syn 1.0.69",
"xz2",
]
@ -802,6 +810,12 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2df960f5d869b2dd8532793fde43eb5427cceb126c929747a26823ab0eeb536"
[[package]]
name = "cow-utils"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79bb3adfaf5f75d24b01aee375f7555907840fa2800e5ec8fa3b9e2031830173"
[[package]]
name = "cpuid-bool"
version = "0.1.2"
@ -839,8 +853,8 @@ version = "0.1.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e98e2ad1a782e33928b96fc3948e7c355e5af34ba4de7670fe8bac2a3b2006d"
dependencies = [
"quote",
"syn",
"quote 1.0.9",
"syn 1.0.69",
]
[[package]]
@ -858,15 +872,21 @@ version = "1.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f8a51dd197fa6ba5b4dc98a990a43cc13693c23eb0089ebb0fcc1f04152bca6"
[[package]]
name = "data-encoding"
version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57"
[[package]]
name = "derive-new"
version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3418329ca0ad70234b9735dc4ceed10af4df60eff9c8e7b06cb5e520d92c3535"
dependencies = [
"proc-macro2",
"quote",
"syn",
"proc-macro2 1.0.26",
"quote 1.0.9",
"syn 1.0.69",
]
[[package]]
@ -876,9 +896,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f82b1b72f1263f214c0f823371768776c4f5841b942c9883aa8e5ec584fd0ba6"
dependencies = [
"convert_case",
"proc-macro2",
"quote",
"syn",
"proc-macro2 1.0.26",
"quote 1.0.9",
"syn 1.0.69",
]
[[package]]
@ -896,6 +916,18 @@ dependencies = [
"r2d2",
]
[[package]]
name = "diesel-derive-newtype"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e844e8e6f65dcf27aa0b97d4234f974d93dfbf56816033d71b5e0c7eb701709f"
dependencies = [
"diesel",
"proc-macro2 0.4.30",
"quote 0.6.13",
"syn 0.14.9",
]
[[package]]
name = "diesel-tracing"
version = "0.1.4"
@ -913,9 +945,9 @@ version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45f5098f628d02a7a0f68ddba586fb61e80edec3bdc1be3b921f4ceec60858d3"
dependencies = [
"proc-macro2",
"quote",
"syn",
"proc-macro2 1.0.26",
"quote 1.0.9",
"syn 1.0.69",
]
[[package]]
@ -955,6 +987,18 @@ version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f"
[[package]]
name = "educe"
version = "0.4.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b6f648515c65974bcb893b286a5c4a35adfdcfbfd03c1bbf1108f40feec65d7"
dependencies = [
"enum-ordinalize",
"proc-macro2 1.0.26",
"quote 1.0.9",
"syn 1.0.69",
]
[[package]]
name = "either"
version = "1.6.1"
@ -977,9 +1021,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c5f0096a91d210159eceb2ff5e1c4da18388a170e1e3ce948aac9c8fdbbf595"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn",
"proc-macro2 1.0.26",
"quote 1.0.9",
"syn 1.0.69",
]
[[package]]
name = "enum-ordinalize"
version = "3.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b166c9e378360dd5a6666a9604bb4f54ae0cac39023ffbac425e917a2a04fef"
dependencies = [
"num-bigint",
"num-traits",
"proc-macro2 1.0.26",
"quote 1.0.9",
"syn 1.0.69",
]
[[package]]
@ -1114,9 +1171,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "668c6733a182cd7deb4f1de7ba3bf2120823835b3bcfbeacf7d2c4a773c1bb8b"
dependencies = [
"proc-macro-hack",
"proc-macro2",
"quote",
"syn",
"proc-macro2 1.0.26",
"quote 1.0.9",
"syn 1.0.69",
]
[[package]]
@ -1334,12 +1391,6 @@ dependencies = [
"unicode-normalization",
]
[[package]]
name = "if_chain"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f7280c75fb2e2fc47080ec80ccc481376923acb04501957fc38f935c3de5088"
[[package]]
name = "indexmap"
version = "1.6.2"
@ -1399,6 +1450,15 @@ dependencies = [
"phf_codegen",
]
[[package]]
name = "itertools"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "0.4.7"
@ -1445,6 +1505,19 @@ version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "lexical-core"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe"
dependencies = [
"arrayvec",
"bitflags",
"cfg-if 1.0.0",
"ryu",
"static_assertions",
]
[[package]]
name = "libc"
version = "0.2.93"
@ -1573,9 +1646,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9753f12909fd8d923f75ae5c3258cae1ed3c8ec052e1b38c93c21a6d157f789c"
dependencies = [
"migrations_internals",
"proc-macro2",
"quote",
"syn",
"proc-macro2 1.0.26",
"quote 1.0.9",
"syn 1.0.69",
]
[[package]]
@ -1676,6 +1749,17 @@ dependencies = [
"version_check 0.1.5",
]
[[package]]
name = "nom"
version = "5.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af"
dependencies = [
"lexical-core",
"memchr",
"version_check 0.9.3",
]
[[package]]
name = "num-bigint"
version = "0.4.0"
@ -1722,6 +1806,12 @@ version = "1.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3"
[[package]]
name = "oncemutex"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44d11de466f4a3006fe8a5e7ec84e93b79c70cb992ae0aa0eb631ad2df8abfe2"
[[package]]
name = "opaque-debug"
version = "0.3.0"
@ -1821,6 +1911,26 @@ dependencies = [
"siphasher",
]
[[package]]
name = "phonenumber"
version = "0.3.1+8.12.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1261a014e5f5e048bf2c6f1a72fa5e4c223009dc5f296a385b95fe19b464608f"
dependencies = [
"bincode",
"either",
"fnv",
"itertools",
"lazy_static",
"nom 5.1.2",
"quick-xml",
"regex",
"regex-cache",
"serde",
"serde_derive",
"thiserror",
]
[[package]]
name = "pin-project"
version = "0.4.28"
@ -1845,9 +1955,9 @@ version = "0.4.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3be26700300be6d9d23264c73211d8190e755b6b5ca7a1b28230025511b52a5e"
dependencies = [
"proc-macro2",
"quote",
"syn",
"proc-macro2 1.0.26",
"quote 1.0.9",
"syn 1.0.69",
]
[[package]]
@ -1856,9 +1966,9 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48c950132583b500556b1efd71d45b319029f2b71518d979fcc208e16b42426f"
dependencies = [
"proc-macro2",
"quote",
"syn",
"proc-macro2 1.0.26",
"quote 1.0.9",
"syn 1.0.69",
]
[[package]]
@ -1921,9 +2031,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn",
"proc-macro2 1.0.26",
"quote 1.0.9",
"syn 1.0.69",
"version_check 0.9.3",
]
@ -1933,8 +2043,8 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [
"proc-macro2",
"quote",
"proc-macro2 1.0.26",
"quote 1.0.9",
"version_check 0.9.3",
]
@ -1950,13 +2060,22 @@ version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086"
[[package]]
name = "proc-macro2"
version = "0.4.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
dependencies = [
"unicode-xid 0.1.0",
]
[[package]]
name = "proc-macro2"
version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec"
dependencies = [
"unicode-xid",
"unicode-xid 0.2.1",
]
[[package]]
@ -1965,13 +2084,31 @@ version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
[[package]]
name = "quick-xml"
version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3cc440ee4802a86e357165021e3e255a9143724da31db1e2ea540214c96a0f82"
dependencies = [
"memchr",
]
[[package]]
name = "quote"
version = "0.6.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1"
dependencies = [
"proc-macro2 0.4.30",
]
[[package]]
name = "quote"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
dependencies = [
"proc-macro2",
"proc-macro2 1.0.26",
]
[[package]]
@ -2211,6 +2348,18 @@ dependencies = [
"regex-syntax",
]
[[package]]
name = "regex-cache"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f7b62d69743b8b94f353b6b7c3deb4c5582828328bcb8d5fedf214373808793"
dependencies = [
"lru-cache",
"oncemutex",
"regex",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.6.23"
@ -2331,9 +2480,9 @@ version = "1.0.125"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d"
dependencies = [
"proc-macro2",
"quote",
"syn",
"proc-macro2 1.0.26",
"quote 1.0.9",
"syn 1.0.69",
]
[[package]]
@ -2447,6 +2596,12 @@ dependencies = [
"version_check 0.9.3",
]
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "stdweb"
version = "0.4.20"
@ -2467,11 +2622,11 @@ version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef"
dependencies = [
"proc-macro2",
"quote",
"proc-macro2 1.0.26",
"quote 1.0.9",
"serde",
"serde_derive",
"syn",
"syn 1.0.69",
]
[[package]]
@ -2481,13 +2636,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11"
dependencies = [
"base-x",
"proc-macro2",
"quote",
"proc-macro2 1.0.26",
"quote 1.0.9",
"serde",
"serde_derive",
"serde_json",
"sha1",
"syn",
"syn 1.0.69",
]
[[package]]
@ -2496,21 +2651,42 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0"
[[package]]
name = "str-utils"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1325fbe98e464a47c4b58d50de0245b6b0e47fba0d4487826c817cd8ac2e12b"
dependencies = [
"cow-utils",
"unicase",
]
[[package]]
name = "subtle"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e81da0851ada1f3e9d4312c704aa4f8806f0f9d69faaf8df2f3464b4a9437c2"
[[package]]
name = "syn"
version = "0.14.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "261ae9ecaa397c42b960649561949d69311f08eeaea86a65696e6e46517cf741"
dependencies = [
"proc-macro2 0.4.30",
"quote 0.6.13",
"unicode-xid 0.1.0",
]
[[package]]
name = "syn"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48fe99c6bd8b1cc636890bcc071842de909d902c81ac7dab53ba33c421ab8ffb"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
"proc-macro2 1.0.26",
"quote 1.0.9",
"unicode-xid 0.2.1",
]
[[package]]
@ -2528,9 +2704,9 @@ version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7765189610d8241a44529806d6fd1f2e0a08734313a35d5b3a556f92b381f3c0"
dependencies = [
"proc-macro2",
"quote",
"syn",
"proc-macro2 1.0.26",
"quote 1.0.9",
"syn 1.0.69",
]
[[package]]
@ -2594,10 +2770,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5c3be1edfad6027c69f5491cf4cb310d1a71ecd6af742788c6ff8bced86b8fa"
dependencies = [
"proc-macro-hack",
"proc-macro2",
"quote",
"proc-macro2 1.0.26",
"quote 1.0.9",
"standback",
"syn",
"syn 1.0.69",
]
[[package]]
@ -2691,9 +2867,9 @@ version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c42e6fa53307c8a17e4ccd4dc81cf5ec38db9209f59b222210375b54ee40d1e2"
dependencies = [
"proc-macro2",
"quote",
"syn",
"proc-macro2 1.0.26",
"quote 1.0.9",
"syn 1.0.69",
]
[[package]]
@ -2859,6 +3035,12 @@ version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796"
[[package]]
name = "unicode-xid"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
[[package]]
name = "unicode-xid"
version = "0.2.1"
@ -2885,6 +3067,7 @@ dependencies = [
"idna",
"matches",
"percent-encoding",
"serde",
]
[[package]]
@ -2913,10 +3096,10 @@ version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c860ad1273f4eee7006cee05db20c9e60e5d24cba024a32e1094aa8e574f3668"
dependencies = [
"nom",
"proc-macro2",
"quote",
"syn",
"nom 4.2.3",
"proc-macro2 1.0.26",
"quote 1.0.9",
"syn 1.0.69",
]
[[package]]
@ -2930,42 +3113,50 @@ dependencies = [
]
[[package]]
name = "validator"
version = "0.13.0"
name = "validators"
version = "0.22.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be110dc66fa015b8b1d2c4eae40c495a27fae55f82b9cae3efb8178241ed20eb"
checksum = "a3906f8c46738dcbdafee114a2014e58768b38b4a09d55a7e467e10375eac376"
dependencies = [
"data-encoding",
"idna",
"lazy_static",
"phonenumber",
"regex",
"semver 0.11.0",
"serde",
"serde_derive",
"serde_json",
"str-utils",
"thiserror",
"url",
"validator_types",
"validators-options",
]
[[package]]
name = "validator_derive"
version = "0.13.0"
name = "validators-derive"
version = "0.22.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68f14fe757e2894ce4271991901567be07fbc3eac6b24246122214e1d5a16554"
checksum = "182fe032784ade29986243f38f7d279974b86cf8c1ae12c7e0a31838fd7404f7"
dependencies = [
"if_chain",
"lazy_static",
"proc-macro-error",
"proc-macro2",
"quote",
"educe",
"enum-ordinalize",
"phonenumber",
"proc-macro2 1.0.26",
"quote 1.0.9",
"regex",
"syn",
"validator_types",
"syn 1.0.69",
"validators",
"validators-options",
]
[[package]]
name = "validator_types"
version = "0.12.0"
name = "validators-options"
version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad9680608df133af2c1ddd5eaf1ddce91d60d61b6bc51494ef326458365a470a"
checksum = "9d83e1f41c974aacfe85242bf905f01c80ed8af151869ca5a89e7c1c7ecb1685"
dependencies = [
"educe",
"enum-ordinalize",
]
[[package]]
name = "vcpkg"
@ -3016,9 +3207,9 @@ dependencies = [
"bumpalo",
"lazy_static",
"log",
"proc-macro2",
"quote",
"syn",
"proc-macro2 1.0.26",
"quote 1.0.9",
"syn 1.0.69",
"wasm-bindgen-shared",
]
@ -3028,7 +3219,7 @@ version = "0.2.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e734d91443f177bfdb41969de821e15c516931c3c3db3d318fa1b68975d0f6f"
dependencies = [
"quote",
"quote 1.0.9",
"wasm-bindgen-macro-support",
]
@ -3038,9 +3229,9 @@ version = "0.2.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d53739ff08c8a68b0fdbcd54c372b8ab800b1449ab3c9d706503bc7dd1621b2c"
dependencies = [
"proc-macro2",
"quote",
"syn",
"proc-macro2 1.0.26",
"quote 1.0.9",
"syn 1.0.69",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]

9
Cargo.toml

@ -16,8 +16,6 @@ serde_json = "1.0.64"
# listenfd = "0.3.3"
dotenv = "0.15.0"
r2d2 = "0.8.9"
validator = "0.13.0"
validator_derive = "0.13.0"
# jsonwebtoken = "7.2.0"
actix-identity = "0.3.1"
actix-web-httpauth = "0.5.1"
@ -41,6 +39,13 @@ tracing-futures = "0.2.5"
tracing-actix-web = "0.2.1"
tracing-bunyan-formatter = "0.2.4"
diesel-tracing = { version = "0.1.4", features = ["sqlite"] }
validators = "0.22.5"
diesel-derive-newtype = "0.1"
derive_more = "0.99.13"
[dependencies.validators-derive]
version = "0.22.5"
features = ["serde"]
[dependencies.build-info]
version = "=0.0.23"

20
README.md

@ -4,22 +4,22 @@ Testing out the Rust framework Actix-Web to create a JSON API CRUD Web App.
### Get Users
```
```
curl -X GET http://localhost:7800/api/users
```
```
```
{
"name": "user1",
"registration_date": "2020-05-09T06:17:26"
}
```
```
```
curl -X GET http://localhost:7800/api/users
```
```
```
[
{
"name": "user1",
@ -38,14 +38,14 @@ curl -X GET http://localhost:7800/api/users
### Create User
```
```
curl -H "content-type: application/json" \
-X POST \
-i http://localhost:7800/do_registration \
-i http://localhost:7800/api/users \
--data '{"name":"user4","password":"test"}'
```
```
```
[
{
"name": "user1",
@ -68,14 +68,14 @@ curl -H "content-type: application/json" \
### DTO Validation
```
```
curl -H "content-type: application/json" \
-X POST \
-i http://localhost:7800/do_registration \
-i http://localhost:7800/api/users \
--data '{"name":"abc","password":"test"}' # min length for name is 4
```
```
```
ValidationErrors({"name": Field([ValidationError { code: "length", message: None, params: {"value": String("abc"), "min": Number(4), "max": Number(10)} }])})
```

50
src/actions/users.rs

@ -1,19 +1,20 @@
use diesel::prelude::*;
use crate::errors;
use crate::models;
use crate::{errors, models::Password};
use bcrypt::{hash, verify, DEFAULT_COST};
use validators::prelude::*;
pub fn find_user_by_uid(
uid: i32,
conn: &impl diesel::Connection<Backend = diesel::sqlite::Sqlite>,
) -> Result<Option<models::UserDto>, errors::DomainError> {
) -> Result<Option<models::User>, errors::DomainError> {
use crate::schema::users::dsl::*;
let maybe_user = users
.select((name, created_at))
.select(users::all_columns())
.find(uid)
.first::<models::UserDto>(conn)
.first::<models::User>(conn)
.optional();
Ok(maybe_user?)
@ -22,9 +23,11 @@ pub fn find_user_by_uid(
pub fn _find_user_by_name(
user_name: String,
conn: &impl diesel::Connection<Backend = diesel::sqlite::Sqlite>,
) -> Result<Option<models::UserDto>, errors::DomainError> {
let maybe_user = query::_get_user_by_name(&user_name)
.first::<models::UserDto>(conn)
) -> Result<Option<models::User>, errors::DomainError> {
use crate::schema::users::dsl::*;
let maybe_user = query::_get_user_by_name()
.filter(name.eq(user_name))
.first::<models::User>(conn)
.optional();
Ok(maybe_user?)
@ -32,35 +35,38 @@ pub fn _find_user_by_name(
pub fn get_all(
conn: &impl diesel::Connection<Backend = diesel::sqlite::Sqlite>,
) -> Result<Vec<models::UserDto>, errors::DomainError> {
) -> Result<Vec<models::User>, errors::DomainError> {
use crate::schema::users::dsl::*;
Ok(users
.select((name, created_at))
.load::<models::UserDto>(conn)?)
.select(users::all_columns())
.load::<models::User>(conn)?)
}
/// Run query using Diesel to insert a new database row and return the result.
pub fn insert_new_user(
nu: models::NewUser,
conn: &impl diesel::Connection<Backend = diesel::sqlite::Sqlite>,
hash_cost: Option<u32>,
) -> Result<models::UserDto, errors::DomainError> {
// It is common when using Diesel with Actix web to import schema-related
// modules inside a function's scope (rather than the normal module's scope)
// to prevent import collisions and namespace pollution.
) -> Result<models::User, errors::DomainError> {
use crate::schema::users::dsl::*;
let nu = {
let mut nu2 = nu;
nu2.password = hash(&nu2.password, hash_cost.unwrap_or(DEFAULT_COST))?;
let hash =
hash(&nu2.password.as_str(), hash_cost.unwrap_or(DEFAULT_COST))?;
nu2.password = Password::parse_string(hash).map_err(|err| {
errors::DomainError::new_field_validation_error(err.to_string())
})?;
nu2
};
diesel::insert_into(users).values(&nu).execute(conn)?;
let user =
query::_get_user_by_name(&nu.name).first::<models::UserDto>(conn)?;
let user = query::_get_user_by_name()
.filter(name.eq(nu.name.as_str()))
.first::<models::User>(conn)?;
Ok(user)
}
//TODO: Add newtype here
pub fn verify_password(
user_name: &str,
given_password: &str,
@ -76,6 +82,7 @@ pub fn verify_password(
mod query {
use diesel::prelude::*;
use diesel::sql_types::Integer;
use diesel::sql_types::Text;
use diesel::sql_types::Timestamp;
use diesel::sqlite::Sqlite;
@ -84,12 +91,11 @@ mod query {
type Query<'a, B, T> = crate::schema::users::BoxedQuery<'a, B, T>;
pub fn _get_user_by_name(
user_name: &str,
) -> Query<Sqlite, (Text, Timestamp)> {
) -> Query<'static, Sqlite, (Integer, Text, Text, Timestamp)> {
use crate::schema::users::dsl::*;
users
.select((name, created_at))
.filter(name.eq(user_name))
.select(users::all_columns())
// .filter(name.eq(user_name))
.into_boxed()
}
}

6
src/errors/domain_error.rs

@ -24,6 +24,7 @@ use std::convert::From;
custom_error! { #[derive(new)] pub DomainError
PwdHashError {source: BcryptError} = "Failed to hash password",
FieldValidationError {message: String} = "Failed to validate one or more fields",
DbError {source: diesel::result::Error} = "Database error",
DbPoolError {source: r2d2::Error} = "Failed to get connection from pool",
PasswordError {cause: String} = "Failed to validate password - {cause}",
@ -67,6 +68,11 @@ impl ResponseError for DomainError {
}
DomainError::AuthError { message: _ } => HttpResponse::Forbidden()
.json(ApiResponse::failure(err.to_string())),
DomainError::FieldValidationError { message: _ } => {
tracing::error!("{}", err);
HttpResponse::BadRequest()
.json(ApiResponse::failure(err.to_string()))
}
}
}
}

6
src/lib.rs

@ -3,6 +3,10 @@
extern crate diesel;
#[macro_use]
extern crate derive_new;
#[macro_use]
extern crate validators_derive;
#[macro_use]
extern crate diesel_derive_newtype;
mod actions;
mod errors;
@ -31,7 +35,7 @@ build_info::build_info!(pub fn get_build_info);
#[serde(rename_all = "lowercase")]
pub enum LoggerFormat {
Json,
Plain,
Pretty,
}
#[derive(Deserialize, Debug, Clone)]

2
src/main.rs

@ -106,7 +106,7 @@ pub fn setup_logger(format: LoggerFormat) -> io::Result<()> {
})?;
}
LoggerFormat::Plain => {
LoggerFormat::Pretty => {
let subscriber = FmtSubscriber::builder()
.pretty()
.with_span_events(FmtSpan::NEW)

4
src/models/api_response.rs

@ -6,8 +6,8 @@ pub struct ApiResponse<T> {
response: T,
}
impl<T> ApiResponse<T> {
pub fn success(&self) -> bool {
impl<T: Serialize> ApiResponse<T> {
pub fn is_success(&self) -> bool {
self.success
}
pub fn response(&self) -> &T {

131
src/models/users.rs

@ -1,27 +1,130 @@
use serde::{Deserialize, Serialize};
use crate::schema::users;
use crate::utils::regexs;
use validator_derive::*;
use crate::utils::regex;
use derive_more::{Display, Into};
use std::convert::TryFrom;
use std::{convert::TryInto, str::FromStr};
use validators::prelude::*;
#[derive(Debug, Clone, Queryable, Identifiable, Deserialize)]
#[derive(
Debug,
Clone,
Eq,
Hash,
PartialEq,
Deserialize,
Display,
Into,
Serialize,
DieselNewType,
)]
#[serde(try_from = "u32", into = "u32")]
pub struct UserId(i32);
impl From<UserId> for u32 {
fn from(s: UserId) -> u32 {
//this should be safe to unwrap since our newtype
//does not allow negative values
s.0.try_into().unwrap()
}
}
impl FromStr for UserId {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if let Ok(num) = s.parse::<u32>() {
(num as u32)
.try_into()
.map_err(|err| {
format!("error while converting user_id: {}", err)
})
.map(UserId)
} else {
Err("negative values are not allowed".to_owned())
}
}
}
impl TryFrom<u32> for UserId {
type Error = String;
fn try_from(value: u32) -> Result<Self, Self::Error> {
value
.try_into()
.map_err(|err| format!("error while converting user_id: {}", err))
.map(UserId)
}
}
#[derive(Validator, Debug, Clone, DieselNewType)]
#[validator(regex(regex::USERNAME_REG))]
pub struct Username(String);
impl Username {
pub fn as_str(&self) -> &str {
&self.0
}
}
#[derive(Validator, Debug, Clone, DieselNewType)]
#[validator(line(char_length(max = 200)))]
pub struct Password(String);
impl Password {
pub fn as_str(&self) -> &str {
&self.0
}
}
#[derive(Debug, Clone, Deserialize, Serialize, Queryable, Identifiable)]
#[table_name = "users"]
pub struct User {
pub id: i32,
pub name: String,
pub password: String,
pub id: UserId,
pub name: Username,
#[serde(skip_serializing)]
pub password: Password,
pub created_at: chrono::NaiveDateTime,
}
#[derive(Debug, Clone, Insertable, Deserialize, Validate)]
#[derive(Debug, Clone, Insertable, Deserialize)]
#[table_name = "users"]
pub struct NewUser {
#[validate(regex = "regexs::USERNAME_REG", length(min = 4, max = 10))]
pub name: String,
pub password: String,
pub name: Username,
#[serde(skip_serializing)]
pub password: Password,
}
#[derive(Debug, Clone, Serialize, Deserialize, Queryable)]
pub struct UserDto {
pub name: String,
pub registration_date: chrono::NaiveDateTime,
#[cfg(test)]
mod test {
use super::*;
#[test]
fn user_model_refinement_test() {
//yes I had been watching a lot of star wars lately
let mb_user = serde_json::from_str::<User>(
r#"{"id":1,"name":"chewbacca","password":"aeqfq3fq","created_at":"2021-05-12T12:37:56"}"#,
);
// println!("{:?}", mb_user);
assert_eq!(mb_user.is_ok(), true);
let mb_user = serde_json::from_str::<User>(
r#"{"id":1,"name":"chew-bacca","password":"aeqfq3fq","created_at":"2021-05-12T12:37:56"}"#,
);
assert_eq!(mb_user.is_ok(), true);
let mb_user = serde_json::from_str::<User>(
r#"{"id":1,"name":"chew.bacca","password":"aeqfq3fq","created_at":"2021-05-12T12:37:56"}"#,
);
assert_eq!(mb_user.is_ok(), false);
let mb_user = serde_json::from_str::<User>(
r#"{"id":-1,"name":"chewbacca","password":"aeqfq3fq","created_at":"2021-05-12T12:37:56"}"#,
);
assert_eq!(mb_user.is_ok(), false);
let mb_user = serde_json::from_str::<User>(
r#"{"id":1,"name":"ch","password":"aeqfq3fq","created_at":"2021-05-12T12:37:56"}"#,
);
assert_eq!(mb_user.is_ok(), false);
let mb_user = serde_json::from_str::<User>(
r#"{"id":1,"name":"chaegw;eaef","password":"aeqfq3fq","created_at":"2021-05-12T12:37:56"}"#,
);
assert_eq!(mb_user.is_ok(), false);
let mb_user = serde_json::from_str::<User>(
r#"{"id":1,"name":"chaegw_eaef","password":"aeqfq3fq","created_at":"2021-05-12T12:37:56"}"#,
);
assert_eq!(mb_user.is_ok(), false);
}
}

99
src/routes/users.rs

@ -1,10 +1,11 @@
use actix_web::{get, web, HttpResponse};
use actix_web::{web, HttpResponse};
use crate::services::UserService;
use crate::{actions, models};
use crate::{
actions,
models::{self, ApiResponse},
};
use crate::{errors::DomainError, AppData};
use actix_web::error::ResponseError;
use validator::Validate;
/// Finds user by UID.
#[tracing::instrument(
@ -30,7 +31,7 @@ pub async fn get_user(
.map_err(|err| DomainError::new_thread_pool_error(err.to_string()))?;
tracing::trace!("{:?}", res);
if let Some(user) = res {
Ok(HttpResponse::Ok().json(user))
Ok(HttpResponse::Ok().json(ApiResponse::successful(user)))
} else {
let err = DomainError::new_entity_does_not_exist_error(format!(
"No user found with uid: {}",
@ -40,23 +41,23 @@ pub async fn get_user(
}
}
#[get("/get/users/{user_id}")]
pub async fn get_user2(
user_service: web::Data<dyn UserService>,
user_id: web::Path<i32>,
) -> Result<HttpResponse, DomainError> {
let u_id = user_id.into_inner();
let user = user_service.find_user_by_uid(u_id)?;
if let Some(user) = user {
Ok(HttpResponse::Ok().json(user))
} else {
let err = DomainError::new_entity_does_not_exist_error(format!(
"No user found with uid: {}",
u_id
));
Err(err)
}
}
// #[get("/get/users/{user_id}")]
// pub async fn get_user2(
// user_service: web::Data<dyn UserService>,
// user_id: web::Path<i32>,
// ) -> Result<HttpResponse, DomainError> {
// let u_id = user_id.into_inner();
// let user = user_service.find_user_by_uid(u_id)?;
// if let Some(user) = user {
// Ok(HttpResponse::Ok().json(user))
// } else {
// let err = DomainError::new_entity_does_not_exist_error(format!(
// "No user found with uid: {}",
// u_id
// ));
// Err(err)
// }
// }
///List all users
#[tracing::instrument(level = "debug", skip(app_data))]
@ -75,14 +76,14 @@ pub async fn get_all_users(
tracing::trace!("{:?}", users);
if !users.is_empty() {
Ok(HttpResponse::Ok().json(users))
Ok(HttpResponse::Ok().json(ApiResponse::successful(users)))
} else {
Err(DomainError::new_entity_does_not_exist_error(
"No users available".to_owned(),
))
}
}
//TODO: Add refinement here
/// Inserts new user with name defined in form.
#[tracing::instrument(level = "debug", skip(app_data))]
pub async fn add_user(
@ -90,44 +91,14 @@ pub async fn add_user(
form: web::Json<models::NewUser>,
) -> Result<HttpResponse, impl ResponseError> {
// use web::block to offload blocking Diesel code without blocking server thread
let res = match form.0.validate() {
Ok(_) => web::block(move || {
let pool = &app_data.pool;
let conn = pool.get()?;
actions::insert_new_user(
form.0,
&conn,
Some(app_data.config.hash_cost),
)
})
.await
.map(|user| {
tracing::debug!("{:?}", user);
HttpResponse::Created().json(user)
}),
Err(e) => {
// let err = e.to_string();
// web::block(move || {
// Err(crate::errors::DomainError::new_generic_error(err))
// })
// .await
// let res2 =
// crate::errors::DomainError::new_generic_error(e.to_string());
// Err(res2)
// let res2 = crate::errors::DomainError::GenericError {
// cause: e.to_string(),
// };
// Err(res2)
let res = HttpResponse::BadRequest().json(e);
// .json(models::ErrorModel::new(
// 40,
// "Error registering user due to validation errors",
// ));
Ok(res)
}
};
res
web::block(move || {
let pool = &app_data.pool;
let conn = pool.get()?;
actions::insert_new_user(form.0, &conn, Some(app_data.config.hash_cost))
})
.await
.map(|user| {
tracing::trace!("{:?}", user);
HttpResponse::Ok().json(ApiResponse::successful(user))
})
}

128
src/services/user_service.rs

@ -1,76 +1,76 @@
use crate::{actions, errors, models, types::DbPool};
// use crate::{actions, errors, models, types::DbPool};
pub trait UserService {
fn find_user_by_uid(
&self,
uid: i32,
) -> Result<Option<models::UserDto>, errors::DomainError>;
fn _find_user_by_name(
&self,
user_name: String,
) -> Result<Option<models::UserDto>, errors::DomainError>;
// pub trait UserService {
// fn find_user_by_uid(
// &self,
// uid: i32,
// ) -> Result<Option<models::UserDto>, errors::DomainError>;
// fn _find_user_by_name(
// &self,
// user_name: String,
// ) -> Result<Option<models::UserDto>, errors::DomainError>;
fn get_all(&self) -> Result<Vec<models::UserDto>, errors::DomainError>;
// fn get_all(&self) -> Result<Vec<models::UserDto>, errors::DomainError>;
fn insert_new_user(
&self,
nu: models::NewUser,
) -> Result<models::UserDto, errors::DomainError>;
// fn insert_new_user(
// &self,
// nu: models::NewUser,
// ) -> Result<models::UserDto, errors::DomainError>;
// fn woot(&self) -> i32;
// // fn woot(&self) -> i32;
fn verify_password(
&self,
user_name: &str,
given_password: &str,
) -> Result<bool, errors::DomainError>;
}
// fn verify_password(
// &self,
// user_name: &str,
// given_password: &str,
// ) -> Result<bool, errors::DomainError>;
// }
#[derive(Clone)]
pub struct UserServiceImpl {
pub pool: DbPool,
}
// #[derive(Clone)]
// pub struct UserServiceImpl {
// pub pool: DbPool,
// }
impl UserService for UserServiceImpl {
fn find_user_by_uid(
&self,
uid: i32,
) -> Result<Option<models::UserDto>, errors::DomainError> {
let conn = self.pool.get()?;
actions::find_user_by_uid(uid, &conn)
}
// impl UserService for UserServiceImpl {
// fn find_user_by_uid(
// &self,
// uid: i32,
// ) -> Result<Option<models::UserDto>, errors::DomainError> {
// let conn = self.pool.get()?;
// actions::find_user_by_uid(uid, &conn)
// }
fn _find_user_by_name(
&self,
user_name: String,
) -> Result<Option<models::UserDto>, errors::DomainError> {
let conn = self.pool.get()?;
actions::_find_user_by_name(user_name, &conn)
}
// fn _find_user_by_name(
// &self,
// user_name: String,
// ) -> Result<Option<models::UserDto>, errors::DomainError> {
// let conn = self.pool.get()?;
// actions::_find_user_by_name(user_name, &conn)
// }
fn get_all(&self) -> Result<Vec<models::UserDto>, errors::DomainError> {
let conn = self.pool.get()?;
actions::get_all(&conn)
}
// fn get_all(&self) -> Result<Vec<models::UserDto>, errors::DomainError> {
// let conn = self.pool.get()?;
// actions::get_all(&conn)
// }
fn insert_new_user(
&self,
nu: models::NewUser,
) -> Result<models::UserDto, errors::DomainError> {
let conn = self.pool.get()?;
actions::insert_new_user(nu, &conn, Some(8))
}
// fn insert_new_user(
// &self,
// nu: models::NewUser,
// ) -> Result<models::UserDto, errors::DomainError> {
// let conn = self.pool.get()?;
// actions::insert_new_user(nu, &conn, Some(8))
// }
fn verify_password(
&self,
user_name: &str,
given_password: &str,
) -> Result<bool, errors::DomainError> {
let conn = self.pool.get()?;
actions::verify_password(user_name, given_password, &conn)
}
// fn verify_password(
// &self,
// user_name: &str,
// given_password: &str,
// ) -> Result<bool, errors::DomainError> {
// let conn = self.pool.get()?;
// actions::verify_password(user_name, given_password, &conn)
// }
// async fn woot(&self) -> i32 {
// 1
// }
}
// // async fn woot(&self) -> i32 {
// // 1
// // }
// }

4
src/utils.rs

@ -1,4 +1,4 @@
pub mod auth;
pub mod regexs;
pub mod regex;
pub use self::auth::*;
pub use self::regexs::*;
pub use self::regex::*;

2
src/utils/regexs.rs → src/utils/regex.rs

@ -2,5 +2,5 @@ use lazy_static::lazy_static;
use regex::Regex;
lazy_static! {
pub static ref USERNAME_REG: Regex =
Regex::new(r"^([a-z\d]+-)*[a-z\d]+$").unwrap();
Regex::new(r"^([a-z\d]+-)*[a-z\d]+{5,35}$").unwrap();
}
Loading…
Cancel
Save