diff --git a/package-lock.json b/package-lock.json index eb38213..777979c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -885,6 +885,15 @@ "dev": true, "optional": true }, + "ansi-align": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz", + "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=", + "dev": true, + "requires": { + "string-width": "^2.0.0" + } + }, "ansi-escapes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", @@ -911,7 +920,6 @@ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", "dev": true, - "optional": true, "requires": { "micromatch": "^3.1.4", "normalize-path": "^2.1.1" @@ -998,8 +1006,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=", - "dev": true, - "optional": true + "dev": true }, "asynckit": { "version": "0.4.0", @@ -1537,8 +1544,7 @@ "version": "1.12.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.12.0.tgz", "integrity": "sha512-DYWGk01lDcxeS/K9IHPGWfT8PsJmbXRtRd2Sx72Tnb8pcYZQFF1oSDb8hJtS1vhp212q1Rzi5dUf9+nq0o9UIg==", - "dev": true, - "optional": true + "dev": true }, "bluebird": { "version": "3.5.3", @@ -1572,6 +1578,29 @@ } } }, + "boxen": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz", + "integrity": "sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==", + "dev": true, + "requires": { + "ansi-align": "^2.0.0", + "camelcase": "^4.0.0", + "chalk": "^2.0.1", + "cli-boxes": "^1.0.0", + "string-width": "^2.0.0", + "term-size": "^1.2.0", + "widest-line": "^2.0.0" + }, + "dependencies": { + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + } + } + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -1697,6 +1726,12 @@ "integrity": "sha512-ogq4NbUWf1uG/j66k0AmiO3GjqJAlQyF8n4w8a954cbCyFKmYGvRtgz6qkq2fWuduTXHibX7GyYL5Pg58Aks2g==", "dev": true }, + "capture-stack-trace": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz", + "integrity": "sha512-mYQLZnx5Qt1JgB1WEiMCf2647plpGeQ2NMR/5L0HNZzGQo4fuSPnK+wjfPnKZV0aiJDgzmWqqkV/g7JD+DW0qw==", + "dev": true + }, "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", @@ -1760,7 +1795,6 @@ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz", "integrity": "sha512-z9n7yt9rOvIJrMhvDtDictKrkFHeihkNl6uWMmZlmL6tJtX9Cs+87oK+teBx+JIgzvbX3yZHT3eF8vpbDxHJXQ==", "dev": true, - "optional": true, "requires": { "anymatch": "^2.0.0", "async-each": "^1.0.0", @@ -1777,6 +1811,12 @@ "upath": "^1.0.5" } }, + "ci-info": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz", + "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==", + "dev": true + }, "circular-json": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", @@ -1806,6 +1846,12 @@ } } }, + "cli-boxes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz", + "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM=", + "dev": true + }, "cli-cursor": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", @@ -1927,6 +1973,20 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, + "configstore": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.2.tgz", + "integrity": "sha512-vtv5HtGjcYUgFrXc6Kx747B83MRRVS5R1VTEQoXvuP+kMI+if6uywV0nDGoiydJRy4yk7h9od5Og0kxx4zUXmw==", + "dev": true, + "requires": { + "dot-prop": "^4.1.0", + "graceful-fs": "^4.1.2", + "make-dir": "^1.0.0", + "unique-string": "^1.0.0", + "write-file-atomic": "^2.0.0", + "xdg-basedir": "^3.0.0" + } + }, "contains-path": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", @@ -2007,6 +2067,15 @@ } } }, + "create-error-class": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", + "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=", + "dev": true, + "requires": { + "capture-stack-trace": "^1.0.0" + } + }, "cross-spawn": { "version": "6.0.5", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", @@ -2020,6 +2089,12 @@ "which": "^1.2.9" } }, + "crypto-random-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz", + "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=", + "dev": true + }, "dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", @@ -2058,6 +2133,12 @@ "type-detect": "^4.0.0" } }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true + }, "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", @@ -2145,11 +2226,26 @@ "esutils": "^2.0.2" } }, + "dot-prop": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", + "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==", + "dev": true, + "requires": { + "is-obj": "^1.0.0" + } + }, "dotenv": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-6.2.0.tgz", "integrity": "sha512-HygQCKUBSFl8wKQZBSemMywRWcEDNidvNbjGVyZu3nbZ8qq9ubiPoGLMdRDpfSrpkkm9BXYFkpKxxFX38o/76w==" }, + "duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", + "dev": true + }, "ecc-jsbn": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", @@ -2563,6 +2659,34 @@ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" }, + "execa": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", + "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", + "dev": true, + "requires": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "dev": true, + "requires": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + } + } + }, "expand-brackets": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", @@ -3529,6 +3653,12 @@ "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", "dev": true }, + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "dev": true + }, "get-value": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", @@ -3563,7 +3693,6 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", "dev": true, - "optional": true, "requires": { "is-glob": "^3.1.0", "path-dirname": "^1.0.0" @@ -3574,19 +3703,46 @@ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", "dev": true, - "optional": true, "requires": { "is-extglob": "^2.1.0" } } } }, + "global-dirs": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", + "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=", + "dev": true, + "requires": { + "ini": "^1.3.4" + } + }, "globals": { "version": "11.9.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.9.0.tgz", "integrity": "sha512-5cJVtyXWH8PiJPVLZzzoIizXx944O4OmRro5MWKx5fT4MgcN7OfaMutPeaTdJCCURwbWdhhcCWcKIffPnmTzBg==", "dev": true }, + "got": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz", + "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=", + "dev": true, + "requires": { + "create-error-class": "^3.0.0", + "duplexer3": "^0.1.4", + "get-stream": "^3.0.0", + "is-redirect": "^1.0.0", + "is-retry-allowed": "^1.0.0", + "is-stream": "^1.0.0", + "lowercase-keys": "^1.0.0", + "safe-buffer": "^5.0.1", + "timed-out": "^4.0.0", + "unzip-response": "^2.0.1", + "url-parse-lax": "^1.0.0" + } + }, "graceful-fs": { "version": "4.1.15", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", @@ -3768,6 +3924,18 @@ "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", "dev": true }, + "ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=", + "dev": true + }, + "import-lazy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", + "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", + "dev": true + }, "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -3789,6 +3957,12 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "dev": true + }, "inquirer": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.2.1.tgz", @@ -3882,7 +4056,6 @@ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", "dev": true, - "optional": true, "requires": { "binary-extensions": "^1.0.0" } @@ -3907,6 +4080,15 @@ "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", "dev": true }, + "is-ci": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.1.tgz", + "integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==", + "dev": true, + "requires": { + "ci-info": "^1.5.0" + } + }, "is-data-descriptor": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", @@ -3975,11 +4157,20 @@ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", "dev": true, - "optional": true, "requires": { "is-extglob": "^2.1.1" } }, + "is-installed-globally": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz", + "integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=", + "dev": true, + "requires": { + "global-dirs": "^0.1.0", + "is-path-inside": "^1.0.0" + } + }, "is-ip": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-ip/-/is-ip-2.0.0.tgz", @@ -3989,6 +4180,12 @@ "ip-regex": "^2.0.0" } }, + "is-npm": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz", + "integrity": "sha1-8vtjpl5JBbQGyGBydloaTceTufQ=", + "dev": true + }, "is-number": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", @@ -4009,6 +4206,21 @@ } } }, + "is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", + "dev": true + }, + "is-path-inside": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", + "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", + "dev": true, + "requires": { + "path-is-inside": "^1.0.1" + } + }, "is-plain-obj": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", @@ -4030,6 +4242,12 @@ "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", "dev": true }, + "is-redirect": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", + "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=", + "dev": true + }, "is-regex": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", @@ -4039,6 +4257,18 @@ "has": "^1.0.1" } }, + "is-retry-allowed": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz", + "integrity": "sha1-EaBgVotnM5REAz0BJaYaINVk+zQ=", + "dev": true + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, "is-symbol": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", @@ -4321,6 +4551,15 @@ "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", "dev": true }, + "latest-version": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz", + "integrity": "sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU=", + "dev": true, + "requires": { + "package-json": "^4.0.0" + } + }, "lcid": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", @@ -4389,8 +4628,7 @@ "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", - "dev": true, - "optional": true + "dev": true }, "lodash.includes": { "version": "4.3.0", @@ -4442,6 +4680,22 @@ "js-tokens": "^3.0.0 || ^4.0.0" } }, + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "dev": true + }, + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, "make-dir": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", @@ -4693,6 +4947,24 @@ "semver": "^5.3.0" } }, + "nodemon": { + "version": "1.18.9", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-1.18.9.tgz", + "integrity": "sha512-oj/eEVTEI47pzYAjGkpcNw0xYwTl4XSTUQv2NPQI6PpN3b75PhpuYk3Vb3U80xHCyM2Jm+1j68ULHXl4OR3Afw==", + "dev": true, + "requires": { + "chokidar": "^2.0.4", + "debug": "^3.1.0", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.0.4", + "pstree.remy": "^1.1.6", + "semver": "^5.5.0", + "supports-color": "^5.2.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.2", + "update-notifier": "^2.5.0" + } + }, "nopt": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", @@ -4722,6 +4994,15 @@ "remove-trailing-separator": "^1.0.1" } }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "requires": { + "path-key": "^2.0.0" + } + }, "number-is-nan": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", @@ -6045,6 +6326,12 @@ "mkdirp": "^0.5.1" } }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, "p-limit": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", @@ -6069,6 +6356,18 @@ "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", "dev": true }, + "package-json": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz", + "integrity": "sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0=", + "dev": true, + "requires": { + "got": "^6.7.1", + "registry-auth-token": "^3.0.1", + "registry-url": "^3.0.3", + "semver": "^5.1.0" + } + }, "packet-reader": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-0.3.1.tgz", @@ -6103,8 +6402,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", - "dev": true, - "optional": true + "dev": true }, "path-exists": { "version": "3.0.0", @@ -6304,6 +6602,12 @@ "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", "dev": true }, + "prepend-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", + "dev": true + }, "private": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", @@ -6331,12 +6635,24 @@ "ipaddr.js": "1.8.0" } }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true + }, "psl": { "version": "1.1.31", "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz", "integrity": "sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==", "dev": true }, + "pstree.remy": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.6.tgz", + "integrity": "sha512-NdF35+QsqD7EgNEI5mkI/X+UwaxVEbQaz9f4IooEmMUv6ZPmlTQYGjBPJGgrlzNdjSvIy4MWMg6Q6vCgBO2K+w==", + "dev": true + }, "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", @@ -6363,6 +6679,26 @@ "unpipe": "1.0.0" } }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, "read-pkg": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", @@ -6404,7 +6740,6 @@ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", "dev": true, - "optional": true, "requires": { "graceful-fs": "^4.1.11", "micromatch": "^3.1.10", @@ -6470,6 +6805,25 @@ "unicode-match-property-value-ecmascript": "^1.0.2" } }, + "registry-auth-token": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.3.2.tgz", + "integrity": "sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==", + "dev": true, + "requires": { + "rc": "^1.1.6", + "safe-buffer": "^5.0.1" + } + }, + "registry-url": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", + "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=", + "dev": true, + "requires": { + "rc": "^1.0.1" + } + }, "regjsgen": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.0.tgz", @@ -6660,6 +7014,15 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==" }, + "semver-diff": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz", + "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=", + "dev": true, + "requires": { + "semver": "^5.0.3" + } + }, "send": { "version": "0.16.2", "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", @@ -7064,6 +7427,12 @@ "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", "dev": true }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true + }, "strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", @@ -7119,6 +7488,15 @@ "string-width": "^2.1.1" } }, + "term-size": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz", + "integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=", + "dev": true, + "requires": { + "execa": "^0.7.0" + } + }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -7130,6 +7508,12 @@ "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" }, + "timed-out": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", + "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=", + "dev": true + }, "tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -7195,6 +7579,26 @@ "hoek": "6.x.x" } }, + "touch": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", + "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", + "dev": true, + "requires": { + "nopt": "~1.0.10" + }, + "dependencies": { + "nopt": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", + "dev": true, + "requires": { + "abbrev": "1" + } + } + } + }, "tough-cookie": { "version": "2.4.3", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", @@ -7291,6 +7695,26 @@ } } }, + "undefsafe": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.2.tgz", + "integrity": "sha1-Il9rngM3Zj4Njnz9aG/Cg2zKznY=", + "dev": true, + "requires": { + "debug": "^2.2.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, "unicode-canonical-property-names-ecmascript": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", @@ -7354,6 +7778,15 @@ } } }, + "unique-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz", + "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=", + "dev": true, + "requires": { + "crypto-random-string": "^1.0.0" + } + }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -7399,12 +7832,35 @@ } } }, + "unzip-response": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz", + "integrity": "sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c=", + "dev": true + }, "upath": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.0.tgz", "integrity": "sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw==", + "dev": true + }, + "update-notifier": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-2.5.0.tgz", + "integrity": "sha512-gwMdhgJHGuj/+wHJJs9e6PcCszpxR1b236igrOkUofGhqJuG+amlIKwApH1IW1WWl7ovZxsX49lMBWLxSdm5Dw==", "dev": true, - "optional": true + "requires": { + "boxen": "^1.2.1", + "chalk": "^2.0.1", + "configstore": "^3.0.0", + "import-lazy": "^2.1.0", + "is-ci": "^1.0.10", + "is-installed-globally": "^0.1.0", + "is-npm": "^1.0.0", + "latest-version": "^3.0.0", + "semver-diff": "^2.0.0", + "xdg-basedir": "^3.0.0" + } }, "uri-js": { "version": "4.2.2", @@ -7421,6 +7877,15 @@ "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", "dev": true }, + "url-parse-lax": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", + "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", + "dev": true, + "requires": { + "prepend-http": "^1.0.1" + } + }, "use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", @@ -7466,6 +7931,11 @@ "resolved": "https://registry.npmjs.org/validator/-/validator-10.10.0.tgz", "integrity": "sha512-DyZyLJlMXM3CGdVaVHE/EDzCagMRoPI3mmGdxxNQbqkGqh56+M3d1i0ZAWd69En8U21DHbPTn12aOdhO+hfm5w==" }, + "validatorjs": { + "version": "3.15.1", + "resolved": "https://registry.npmjs.org/validatorjs/-/validatorjs-3.15.1.tgz", + "integrity": "sha512-9hJWC4sYaEjf3a6MPKrERzRU69btS/pDy+t5IX8pRFqkI8UI2qj8nR3n2XlVzxRZMQGrf6HrtfDkoRcJavkLlg==" + }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -7496,6 +7966,15 @@ "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=" }, + "widest-line": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.1.tgz", + "integrity": "sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==", + "dev": true, + "requires": { + "string-width": "^2.1.1" + } + }, "window-size": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.2.0.tgz", @@ -7564,6 +8043,23 @@ "mkdirp": "^0.5.1" } }, + "write-file-atomic": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.2.tgz", + "integrity": "sha512-s0b6vB3xIVRLWywa6X9TOMA7k9zio0TMOsl9ZnDkliA/cfJlpHXAscj0gbHVJiTdIuAYpIyqS5GW91fqm6gG5g==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" + } + }, + "xdg-basedir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz", + "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=", + "dev": true + }, "xtend": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", @@ -7574,6 +8070,12 @@ "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=" }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + }, "yargs": { "version": "4.8.1", "resolved": "https://registry.npmjs.org/yargs/-/yargs-4.8.1.tgz", diff --git a/package.json b/package.json index f864ee3..e005235 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "eslint-plugin-import": "^2.14.0", "istanbul": "^0.4.5", "mocha": "^5.2.0", + "nodemon": "^1.18.9", "nyc": "^13.1.0", "supertest": "^3.3.0" }, @@ -63,6 +64,7 @@ "moment": "^2.23.0", "morgan": "^1.9.1", "pg": "^7.8.0", - "uuid": "^3.3.2" + "uuid": "^3.3.2", + "validatorjs": "^3.15.1" } -} \ No newline at end of file +} diff --git a/server/app.js b/server/app.js index 883dbf4..ff72e49 100644 --- a/server/app.js +++ b/server/app.js @@ -7,28 +7,30 @@ import bodyParser from 'body-parser'; // Set up express app const app = express(); -import expressValidation from 'express-validation'; -import userRoutes from './routes/user'; -import AdminRoutes from './routes/admin'; -import meetupRoutes from './routes/meetups'; -import questionsRoute from './routes/questions'; -// import commentRoutes from './routes/comments'; +import routes from './routes'; // Log requests to the console app.use(logger('dev')); - // Parse incoming request data app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: false, })); -app.use('/api/v1/admin', AdminRoutes); -app.use('/api/v1', userRoutes); -app.use('/api/v1/meetups', meetupRoutes); -app.use('/api/v1/questions', questionsRoute); -// app.use('api/v1/comments', commentRoutes); +app.use('/api/v1', routes); + +app.all('/api/v1', (req, res) => { + res.status(200).json({ + status: 200, + message: 'Welcome to the Questioner API.', + }); +}); + +app.all('/*', (req, res) => res.status(404).json({ + status: 404, + message: 'This route does not exist you might want to check your route specification on postman.', +})); app.use((req, res, next) => { const error = new Error('Invalid route'); @@ -36,18 +38,6 @@ app.use((req, res, next) => { next(error); }); -app.use((error, req, res, next) => { - if (error instanceof expressValidation.ValidationError) { - res.status(error.status).json(error); - } else { - res.status(500) - .json({ - status: error.status, - message: error.messages, - }); - } -}); - app.use((error, req, res) => { res.status(error.status || 500); res.json({ diff --git a/server/controller/admin.js b/server/controller/admin.js deleted file mode 100644 index 68fec91..0000000 --- a/server/controller/admin.js +++ /dev/null @@ -1,79 +0,0 @@ -/* eslint-disable eol-last */ -import jwt from '../utilities/jwt'; -import bcrypt from '../utilities/bcrypt'; -import AdminModel from '../models/admin'; - - -const AdminController = { - async createnewAdmin(req, res) { - if (req.body.password !== req.body.confirmPassword) { - return res.status(400).json({ - status: 400, - error: 'Passwords do not match', - }); - } - - const newAdmin = new AdminModel(req.body); - const isExistingAdminMail = await AdminModel.findAdminByEmail(newAdmin.email); - if (isExistingAdminMail) { - return res.status(409).json({ - status: 409, - error: 'This email address is already taken', - }); - } - - const isExistingAdminName = await AdminModel.findAdminByUsername(newAdmin.username); - if (isExistingAdminName) { - return res.status(409).json({ - status: 409, - error: 'This username is already taken', - }); - } - newAdmin.password = bcrypt.hashPassword(newAdmin.password); - - const createdUser = await newAdmin.newAdminSignUp(); - - return res.status(201).json({ - status: 201, - message: 'Admin created', - data: createdUser, - }); - }, - - async loginAdmin(req, res) { - const { - email, - password, - } = req.body; - - const isExistingAdminMail = await AdminModel.findAdminByEmail(email); - if (!isExistingAdminMail) { - return res.status(404).json({ - status: 404, - error: 'The credentials you provided is incorrect', - }); - } - - if (!bcrypt.comparePassword(isExistingAdminMail.password, password)) { - return res.status(404).json({ - status: 404, - error: 'The credentials you provided is incorrect', - }); - } - - const tokenData = { - id: isExistingAdminMail.id, - username: isExistingAdminMail.username, - admin: isExistingAdminMail.admin, - }; - - const token = await jwt.generateToken(tokenData); - return res.status(200).send({ - status: 200, - message: 'You are logged in as an admin', - token, - }); - }, -}; - -export default AdminController; \ No newline at end of file diff --git a/server/controller/comments.js b/server/controller/comments.js index 2bf1787..1a4864a 100644 --- a/server/controller/comments.js +++ b/server/controller/comments.js @@ -1,24 +1,26 @@ /* eslint-disable eol-last */ // import CommentsModel from '../models/comments'; -// import QuestionModel from '../models/question'; +import QuestionModel from '../models/question'; -// const commentsController = { +const commentsController = { -// async createComment(req, res) { -// const comment = req.body; -// const checkquestion = await QuestionModel.getcommentById(comment.commentId); -// if (!checkquestion) { -// res.status(404).json({ -// status: 404, -// error: 'comment does not exist', -// }); + async createComment(req, res) { + const comment = req.body; + const checkquestion = await QuestionModel.getcommentById(comment.commentId); + if (!checkquestion) { + res.status(404).json({ + status: 404, + error: 'comment does not exist', + }); -// comment.title = checkquestion.title; -// comment.body = checkquestion.body; -// comment.userid = req.user.id; + comment.title = checkquestion.title; + comment.body = checkquestion.body; + comment.userid = req.user.id; -// const newComment = new CommentsModel(comment); -// const createdComment = await CommentsModel.createComment(newComment); -// } -// }, -// }; \ No newline at end of file + const newComment = new CommentsModel(comment); + const createdComment = await CommentsModel.createComment(newComment); + } + }, +}; + +export default commentsController; \ No newline at end of file diff --git a/server/controller/meetups.js b/server/controller/meetups.js index 2f62fb4..0d7ca5b 100644 --- a/server/controller/meetups.js +++ b/server/controller/meetups.js @@ -1,51 +1,27 @@ /* eslint-disable eol-last */ import MeetupModels from '../models/meetup'; +import { + errorResponse, + successResponse, +} from '../utilities/responseformat'; const MeetupController = { async createMeetup(req, res) { - const { - topic, - location, - date, - image, - tags, - } = req.body; - - const formatedTags = tags.trim().split(''); - - const newMeetUp = { - topic, - location, - date, - image, - formatedTags, - }; - - const meetupData = new MeetupModels(newMeetUp); + const meetupData = new MeetupModels(req.body); const createdMeetup = await meetupData.createMeetup(); - res.status(201).json({ - status: 201, - message: 'Meetup created', - data: createdMeetup, - }); + return successResponse(res, 201, 'Meetup created', createdMeetup); }, async getAllMeetups(req, res) { const allMeetups = await MeetupModels.retrieveAllMeetups(); if (allMeetups.length === 0) { - res.status(204).json({ - status: 204, - error: 'Empty resource', - }); + return errorResponse(res, 204, 'Empty Resource'); } - return res.status(200).json({ - status: 200, - data: allMeetups, - }); + return successResponse(res, 200, 'All available meetups', allMeetups); }, async getSingleMeetup(req, res) { @@ -55,37 +31,35 @@ const MeetupController = { const retrievedMeetup = await MeetupModels.retrieveSingleMeetup(id); if (!retrievedMeetup) { - res.status(404).json({ - status: 404, - error: 'Meetup not found', - }); + return errorResponse(res, 404, 'Meetup not found'); } - return res.status(200).json({ - status: 200, - message: 'Meetup Found!', - data: retrievedMeetup, - }); + return successResponse(res, 200, 'Meetup Found', retrievedMeetup); }, async getUpcomingMeetups(req, res) { - const currentDate = new Date(Date.now() / 1000) + 39; + const currentDate = new Date(Date.now()); const upcomingMeetups = await MeetupModels.retrieveUpcomingMeetups(currentDate); if (upcomingMeetups.length === 0) { - return res.status(404).json({ - status: 404, - err0r: 'No upcoming meetups found', - }); + return errorResponse(res, 404, 'No upcoming meetups'); } - return res.status(200).json({ - status: 200, - message: 'These are pending meetups', - data: upcomingMeetups, - }); + + return successResponse(res, 200, 'These are the upcoming meetups', upcomingMeetups); }, + + async deleteMeetup(req, res) { + const { + id, + } = req.params; + const meetup = await MeetupModels.retrieveSingleMeetup(id); + + if (!meetup) return errorResponse(res, 404, 'Meetup not found.'); + await MeetupModels.deleteMeetup(id); + return successResponse(res, 200, 'Meetup deleted successfully.', null); + }, }; diff --git a/server/controller/questions.js b/server/controller/questions.js index 2761eaf..8dc597b 100644 --- a/server/controller/questions.js +++ b/server/controller/questions.js @@ -1,6 +1,10 @@ /* eslint-disable eol-last */ import Question from '../models/question'; import Meetup from '../models/meetup'; +import { + errorResponse, + successResponse, +} from '../utilities/responseformat'; export default { async createQuestion(req, res) { @@ -13,17 +17,10 @@ export default { const meetupExists = await Meetup.retrieveSingleMeetup(question.meetupid); if (!meetupExists) { - return res.status(404).json({ - status: 404, - error: 'Meetup not found', - }); + return errorResponse(res, 404, 'Meetup not found'); } const newQuestion = await question.postQuestion(); - return res.status(201).json({ - status: 201, - message: 'Question created', - data: [newQuestion], - }); + return successResponse(res, 201, 'Question created', newQuestion); }, }; \ No newline at end of file diff --git a/server/controller/user.js b/server/controller/user.js index 0c638e7..dc45b89 100644 --- a/server/controller/user.js +++ b/server/controller/user.js @@ -2,42 +2,34 @@ import jwt from '../utilities/jwt'; import bcrypt from '../utilities/bcrypt'; import UserModel from '../models/user'; +import { + successResponse, + errorResponse, + successfullLogin, +} from '../utilities/responseformat'; const UserController = { async createNewUser(req, res) { if (req.body.password !== req.body.confirmPassword) { - return res.status(400).json({ - status: 400, - error: 'Passwords do not match', - }); + return errorResponse(res, 400, 'Passwords do not match'); } const newUser = new UserModel(req.body); const isExistingUserMail = await UserModel.findUserByEmail(newUser.email); if (isExistingUserMail) { - return res.status(409).json({ - status: 409, - error: 'This email address is already taken', - }); + return errorResponse(res, 409, 'This email address is not available'); } const isExistingUsername = await UserModel.findUserByUsername(newUser.username); if (isExistingUsername) { - return res.status(409).json({ - status: 409, - error: 'This username is already taken', - }); + return errorResponse(res, 409, 'This username is not available'); } newUser.password = bcrypt.hashPassword(newUser.password); const createdUser = await newUser.newUserSignUp(); - return res.status(201).json({ - status: 201, - message: 'User created', - data: createdUser, - }); + return successResponse(res, 201, 'User Created', createdUser); }, async loginUser(req, res) { @@ -47,18 +39,15 @@ const UserController = { } = req.body; const isExistingUserMail = await UserModel.findUserByEmail(email); + const { + admin, + } = isExistingUserMail; if (!isExistingUserMail) { - return res.status(404).json({ - status: 404, - error: 'The credentials you provided is incorrect', - }); + return errorResponse(res, 404, 'The credentials you provided is incorrect'); } if (!bcrypt.comparePassword(isExistingUserMail.password, password)) { - return res.status(404).json({ - status: 404, - error: 'The credentials you provided is incorrect', - }); + return errorResponse(res, 401, 'The credentials you provided is incorrect'); } const tokenData = { @@ -68,11 +57,8 @@ const UserController = { }; const token = await jwt.generateToken(tokenData); - return res.status(200).send({ - status: 200, - message: 'You are logged in', - token, - }); + const loginData = await UserModel.logIn(email); + return successfullLogin(res, 200, admin ? 'You are logged in as an admin' : 'You are logged in as a normal user', token, loginData); }, }; diff --git a/server/db/migrationsCreateTables.js b/server/db/migrationsCreateTables.js index 3ac5c2c..9aa6231 100644 --- a/server/db/migrationsCreateTables.js +++ b/server/db/migrationsCreateTables.js @@ -1,11 +1,16 @@ /* eslint-disable indent */ /* eslint-disable eol-last */ +import dotenv from 'dotenv'; import pool from './index'; +import bcrypt from '../utilities/bcrypt'; + +dotenv.config(); console.log('Creating tables...'); (async () => { try { + console.log('Creating users table...'); await pool.query(`CREATE TABLE IF NOT EXISTS users( id SERIAL PRIMARY KEY, firstname VARCHAR(50) NOT NULL, @@ -17,26 +22,39 @@ console.log('Creating tables...'); admin BOOLEAN DEFAULT FALSE, createdon TIMESTAMPTZ DEFAULT NOW())`); - await pool.query(`CREATE TABLE IF NOT EXISTS admin( - id SERIAL PRIMARY KEY, - firstname VARCHAR(50) NOT NULL, - lastname VARCHAR(50) NOT NULL, - username VARCHAR(50) UNIQUE NOT NULL, - email VARCHAR(100) UNIQUE NOT NULL, - phonenumber VARCHAR(11) NOT NULL, - password VARCHAR(100) NOT NULL, - admin BOOLEAN DEFAULT TRUE, - createdon TIMESTAMPTZ DEFAULT NOW())`); + const password = bcrypt.hashPassword(process.env.ADMIN_PASSWORD); + const Admin = { + firstname: 'legolas', + lastname: 'Aragorn', + username: 'ShirePrince', + email: 'legolaslegit@gmail.com', + password, + phonenumber: '08135266484', + }; + + const queryPlaceholder = `INSERT INTO users (firstname, lastname, username, email, password, phonenumber) + VALUES ($1, $2, $3, $4, $5, $6) RETURNING id, firstname, lastname, username, email, phonenumber`; + const values = [Admin.firstname, Admin.lastname, Admin.username, + Admin.email, Admin.password, Admin.phonenumber, + ]; + await pool.query(queryPlaceholder, values); + // eslint-disable-next-line quotes + const makeAdmin = `UPDATE users SET admin = true WHERE email = $1`; + + const adminValue = [Admin.email]; + await pool.query(makeAdmin, adminValue); + + console.log('Creating meetups table...'); await pool.query(`CREATE TABLE IF NOT EXISTS meetups( id SERIAL PRIMARY KEY, topic VARCHAR(255) NOT NULL, location TEXT NOT NULL, - date VARCHAR(50) NOT NULL, + happeningon VARCHAR(50) NOT NULL, image VARCHAR(50), - tags VARCHAR(500), createdon TIMESTAMPTZ DEFAULT NOW())`); + console.log('Creating questions table...'); await pool.query(`CREATE TABLE IF NOT EXISTS questions( id SERIAL PRIMARY KEY, meetupid INT NOT NULL, @@ -49,6 +67,7 @@ console.log('Creating tables...'); FOREIGN KEY (userid) REFERENCES users (id) ON DELETE CASCADE, FOREIGN KEY (meetupid) REFERENCES meetups (id) ON DELETE CASCADE)`); + console.log('Creating rsvps table...'); await pool.query(`CREATE TABLE IF NOT EXISTS rsvps( id SERIAL, meetupid INT NOT NULL, @@ -58,6 +77,7 @@ console.log('Creating tables...'); FOREIGN KEY (meetupid) REFERENCES meetups (id) ON DELETE CASCADE, FOREIGN KEY (userid) REFERENCES users (id) ON DELETE CASCADE)`); + console.log('Creating comments table...'); await pool.query(`CREATE TABLE IF NOT EXISTS comments( id SERIAL PRIMARY KEY, meetupid INT NOT NULL, @@ -69,6 +89,7 @@ console.log('Creating tables...'); FOREIGN KEY (questionid) REFERENCES questions (id) ON DELETE CASCADE, FOREIGN KEY (meetupid) REFERENCES meetups (id) ON DELETE CASCADE)`); + console.log('Creating votes table...'); await pool.query(`CREATE TABLE IF NOT EXISTS votes( id SERIAL PRIMARY KEY, userid INT NOT NULL, diff --git a/server/middleware/Auth.js b/server/middleware/Auth.js index 6f19185..7f93ae5 100644 --- a/server/middleware/Auth.js +++ b/server/middleware/Auth.js @@ -1,24 +1,24 @@ /* eslint-disable consistent-return */ /* eslint-disable eol-last */ import jwt from 'jsonwebtoken'; +import dotenv from 'dotenv'; +import { + errorResponse, +} from '../utilities/responseformat'; + +dotenv.config(); const Auth = { async verifyToken(req, res, next) { const header = req.headers.authorization; if (typeof header === 'undefined') { - return res.status(401).json({ - status: 401, - error: 'Unauthorized header', - }); + return errorResponse(res, 401, 'You are not authorized to make this action'); } const token = header.split(' ')[1]; if (!token) { - return res.status(403).send({ - status: 403, - error: 'Token is not provided', - }); + return errorResponse(res, 401, 'You are not authorized to make this action please login'); } try { const decoded = await jwt.verify(token, process.env.SECRET); @@ -35,10 +35,7 @@ const Auth = { } = req.user; if (!admin) { - return res.status(403).json({ - status: 403, - error: 'Unauthorized request', - }); + return errorResponse(res, 401, 'You are not authorized to make this action'); } next(); }, diff --git a/server/middleware/validatemeetups.js b/server/middleware/validatemeetups.js new file mode 100644 index 0000000..84a8d89 --- /dev/null +++ b/server/middleware/validatemeetups.js @@ -0,0 +1,39 @@ +/* eslint-disable eol-last */ +import Validator from 'validatorjs'; +import { + errorResponse, +} from '../utilities/responseformat'; +import customErrorMessages from '../utilities/errorresponses'; + +export default class MeetupValidation { + static validCreateMeetup(req, res, next) { + const meetup = req.body; + + const meetupProperties = { + topic: 'required|string|min:1|max:255', + location: 'required|string|min:1', + happeningon: ['required', 'date', 'regex:/[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}/'], + images: 'url', + }; + + const validator = new Validator(meetup, meetupProperties, customErrorMessages); + validator.passes(() => next()); + validator.fails(() => { + const errors = validator.errors.all(); + return errorResponse(res, 400, errors); + }); + } + + static checkDate(req, res, next) { + const { + happeningon, + } = req.body; + const currentDate = new Date(Date.now()); + const meetupDate = new Date(happeningon); + + if (currentDate > meetupDate) { + return errorResponse(res, 400, `Your Date should be greater than ${currentDate}.`); + } + return next(); + } +} \ No newline at end of file diff --git a/server/middleware/validatequestion.js b/server/middleware/validatequestion.js new file mode 100644 index 0000000..c158ecd --- /dev/null +++ b/server/middleware/validatequestion.js @@ -0,0 +1,25 @@ +/* eslint-disable eol-last */ +import Validator from 'validatorjs'; +import { + errorResponse, +} from '../utilities/responseformat'; +import customErrorMessages from '../utilities/errorresponses'; + +export default class QuestionValidation { + static validateQuestion(req, res, next) { + const question = req.body; + + const questionProperties = { + meetupid: 'required|numeric', + title: 'required|string|max:200', + body: 'required|string|max:500', + }; + + const validator = new Validator(question, questionProperties, customErrorMessages); + validator.passes(() => next()); + validator.fails(() => { + const errors = validator.errors.all(); + return errorResponse(res, 400, errors); + }); + } +} \ No newline at end of file diff --git a/server/middleware/validateuser.js b/server/middleware/validateuser.js new file mode 100644 index 0000000..a875f83 --- /dev/null +++ b/server/middleware/validateuser.js @@ -0,0 +1,45 @@ +/* eslint-disable eol-last */ +import Validator from 'validatorjs'; +import { + errorResponse, +} from '../utilities/responseformat'; +import customErrorMessages from '../utilities/errorresponses'; + +export default class UserValidation { + static validateSignUp(req, res, next) { + const user = req.body; + + const userProperties = { + firstname: 'required|alpha|min:2|max:50', + lastname: 'required|alpha|min:2|max:50', + username: 'required|alpha_num|min:5|max:50', + email: 'required|email|max:100', + password: 'required|alpha_num|min:6|max:18', + confirmPassword: 'required_with:password', + phonenumber: 'required|digits:11', + }; + + const validator = new Validator(user, userProperties, customErrorMessages); + validator.passes(() => next()); + validator.fails(() => { + const errors = validator.errors.all(); + return errorResponse(res, 400, errors); + }); + } + + static validateLogin(req, res, next) { + const user = req.body; + + const userProperties = { + email: 'required|email|max:100', + password: 'required|alpha_num|min:6|max:18', + }; + + const validator = new Validator(user, userProperties, customErrorMessages); + validator.passes(() => next()); + validator.fails(() => { + const errors = validator.errors.all(); + return errorResponse(res, 400, errors); + }); + } +} \ No newline at end of file diff --git a/server/middleware/validator/validator.js b/server/middleware/validator/validator.js deleted file mode 100644 index 7716a23..0000000 --- a/server/middleware/validator/validator.js +++ /dev/null @@ -1,40 +0,0 @@ -/* eslint-disable eol-last */ -import Joi from 'joi'; - -export default { - - createMeetup: { - body: { - topic: Joi.string().required(), - location: Joi.string().required(), - date: Joi.date().required(), - tags: Joi.string(), - }, - }, - - getMeetup: { - params: { - meetupId: Joi.number().required(), - }, - }, - - - postQuestion: { - body: { - meetupid: Joi.number().required(), - title: Joi.string().required(), - body: Joi.string().required(), - }, - }, - - rsvps: { - body: { - meetupId: Joi.number().required(), - rsvps: Joi.string().valid( - 'yes', - 'no', - 'maybe', - ).required(), - }, - }, -}; \ No newline at end of file diff --git a/server/models/meetup.js b/server/models/meetup.js index e280433..a207c85 100644 --- a/server/models/meetup.js +++ b/server/models/meetup.js @@ -6,16 +6,15 @@ class Meetup { constructor(meetup) { this.topic = meetup.topic; this.location = meetup.location; - this.date = meetup.date; + this.happeningon = meetup.happeningon; this.image = meetup.image; - this.tags = meetup.tags; } async createMeetup() { - const queryPlaceholder = `INSERT INTO meetups (topic, location, date, - image, tags) VALUES ($1, $2, $3, $4, $5) RETURNING *`; - const entryValues = [this.topic, this.location, this.date, - this.image, this.tags, + const queryPlaceholder = `INSERT INTO meetups (topic, location, happeningon, + image) VALUES ($1, $2, $3, $4) RETURNING *`; + const entryValues = [this.topic, this.location, this.happeningon, + this.image, ]; const { rows, @@ -41,13 +40,20 @@ class Meetup { } static async retrieveUpcomingMeetups(currentDate) { - const queryPlaceholder = 'SELECT * FROM meetups WHERE date > $1 ORDER BY date'; + const queryPlaceholder = 'SELECT * FROM meetups WHERE happeningon > $1 ORDER BY happeningon'; const queryValues = [currentDate]; const { rows, } = await pool.query(queryPlaceholder, queryValues); return rows; } + + static async deleteMeetup(id) { + const queryPlaceholder = 'DELETE FROM meetups WHERE id = $1'; + const queryValues = [id]; + const result = await pool.query(queryPlaceholder, queryValues); + return result; + } } export default Meetup; \ No newline at end of file diff --git a/server/models/user.js b/server/models/user.js index 7039ef3..9cccc94 100644 --- a/server/models/user.js +++ b/server/models/user.js @@ -43,7 +43,7 @@ class UserModel { } static async logIn(email) { - const queryPlaceholder = 'SELECT id, password FROM users WHERE email = $1'; + const queryPlaceholder = 'SELECT id, firstname, lastname, username, email, phonenumber FROM users WHERE email = $1'; const values = [email]; const { rows, diff --git a/server/routes/admin.js b/server/routes/admin.js deleted file mode 100644 index 974a13b..0000000 --- a/server/routes/admin.js +++ /dev/null @@ -1,12 +0,0 @@ -/* eslint-disable eol-last */ -/* eslint-disable import/first */ -import express from 'express'; - -const router = express.Router(); - -import Admin from '../controller/admin'; - -router.post('/auth/signup', Admin.createnewAdmin); -router.post('/auth/login', Admin.loginAdmin); - -export default router; \ No newline at end of file diff --git a/server/routes/comments.js b/server/routes/comments.js index 428fff4..e282557 100644 --- a/server/routes/comments.js +++ b/server/routes/comments.js @@ -1,12 +1,12 @@ /* eslint-disable eol-last */ /* eslint-disable import/first */ -// import express from 'express'; +import express from 'express'; -// const router = express.Router(); +const router = express.Router(); -// import Admin from '../controller/admin'; +import Comments from '../controller/comments'; +import tryCatch from '../utilities/trycatch'; -// router.post('/auth/signup', Admin.createnewAdmin); -// router.post('/auth/login', Admin.loginAdmin); +// router.post('/auth/signup', tryCatch(Comments.createComment)); -// export default router; \ No newline at end of file +export default router; \ No newline at end of file diff --git a/server/routes/index.js b/server/routes/index.js new file mode 100644 index 0000000..71b9f9f --- /dev/null +++ b/server/routes/index.js @@ -0,0 +1,16 @@ +/* eslint-disable eol-last */ +import { + Router, +} from 'express'; +import userRoutes from './user'; +import meetupRoutes from './meetups'; +import questionsRoute from './questions'; +// import commentRoutes from './routes/comments'; + +const router = new Router(); + +router.use('/auth', userRoutes); +router.use('/meetups', meetupRoutes); +router.use('/questions', questionsRoute); + +export default router; \ No newline at end of file diff --git a/server/routes/meetups.js b/server/routes/meetups.js index 364f74b..fb0ba55 100644 --- a/server/routes/meetups.js +++ b/server/routes/meetups.js @@ -3,19 +3,21 @@ /* eslint-disable eol-last */ /* eslint-disable import/first */ import express from 'express'; -import validate from 'express-validation'; const router = express.Router(); + import meetupController from '../controller/meetups'; import rsvpController from '../controller/rsvp'; -import validations from '../middleware/validator/validator'; import Auth from '../middleware/Auth'; +import meetupValidation from '../middleware/validatemeetups'; +import tryCatch from '../utilities/trycatch'; -router.post('/', Auth.verifyToken, Auth.adminAuth, validate(validations.createMeetup), meetupController.createMeetup); -router.get('/upcoming/', Auth.verifyToken, meetupController.getUpcomingMeetups); -router.get('/:meetupId', Auth.verifyToken, validate(validations.getMeetup), meetupController.getUpcomingMeetups); -router.get('/', Auth.verifyToken, meetupController.getAllMeetups); -router.patch('/:meetupId/rsvps', Auth.verifyToken, validate(validations.rsvps), rsvpController.respondToRsvp); +router.post('/', Auth.verifyToken, Auth.adminAuth, meetupValidation.validCreateMeetup, meetupValidation.checkDate, tryCatch(meetupController.createMeetup)); +router.get('/upcoming/', Auth.verifyToken, tryCatch(meetupController.getUpcomingMeetups)); +router.get('/:id', Auth.verifyToken, tryCatch(meetupController.getSingleMeetup)); +router.get('/', Auth.verifyToken, tryCatch(meetupController.getAllMeetups)); +router.patch('/:meetupId/rsvps', Auth.verifyToken, tryCatch(rsvpController.respondToRsvp)); +router.delete('/:id', Auth.verifyToken, Auth.adminAuth, tryCatch(meetupController.deleteMeetup)); export default router; \ No newline at end of file diff --git a/server/routes/questions.js b/server/routes/questions.js index 720ec61..0d35fc1 100644 --- a/server/routes/questions.js +++ b/server/routes/questions.js @@ -1,17 +1,17 @@ /* eslint-disable eol-last */ /* eslint-disable import/first */ import express from 'express'; -import validate from 'express-validation'; const router = express.Router(); import questionsController from '../controller/questions'; // import voteController from '../controller/vote'; -import validations from '../middleware/validator/validator'; import Auth from '../middleware/Auth'; +import validate from '../middleware/validatequestion'; +import tryCatch from '../utilities/trycatch'; -router.post('/', Auth.verifyToken, validate(validations.postQuestion), questionsController.createQuestion); +router.post('/', Auth.verifyToken, validate.validateQuestion, tryCatch(questionsController.createQuestion)); // router.patch('/:questionId/upvote', Auth.verifyToken, voteController.upvote); // router.patch('/:questionId/downvote', Auth.verifyToken, voteController.downvote); diff --git a/server/routes/user.js b/server/routes/user.js index 4198b97..e74465d 100644 --- a/server/routes/user.js +++ b/server/routes/user.js @@ -5,10 +5,11 @@ import express from 'express'; const router = express.Router(); import User from '../controller/user'; +import tryCatch from '../utilities/trycatch'; +import userValidation from '../middleware/validateuser'; -router.post('/auth/signup', User.createNewUser); - -router.post('/auth/login', User.loginUser); +router.post('/signup', userValidation.validateSignUp, tryCatch(User.createNewUser)); +router.post('/login', userValidation.validateLogin, tryCatch(User.loginUser)); export default router; \ No newline at end of file diff --git a/server/test/meetups.spec.js b/server/test/meetups.spec.js index bdc348c..2401cfd 100644 --- a/server/test/meetups.spec.js +++ b/server/test/meetups.spec.js @@ -11,57 +11,60 @@ import { import server from '../app'; import { - createAdmin, loginAdmin, createMeetup, } from './testData'; let adminToken; -describe('Create an admin', async () => { - await it('Should create an admin', (done) => { +describe('Login an admin', async () => { + it('Admin should be able to login', (done) => { request(server) - .post('/api/v1/admin/auth/signup') - .send(createAdmin) + .post('/api/v1/auth/login') + .send(loginAdmin) .set('Accept', 'application/json') .expect('Content-Type', /json/) - .expect(201) .end((err, res) => { if (err) return done(err); + adminToken = res.body.token; return done(); }); }); }); -describe('Login an admin', async () => { - await it('Should login an admin', (done) => { + +describe('Create Meetups', async () => { + it('Admin should be able to create a meetup', (done) => { request(server) - .post('/api/v1/admin/auth/login') - .send(loginAdmin) + .post('/api/v1/meetups') + .send(createMeetup) .set('Accept', 'application/json') + .set({ + Authorization: `Bearer ${adminToken}`, + }) .expect('Content-Type', /json/) + .expect(201) .end((err, res) => { if (err) return done(err); - adminToken = res.body.token; + expect(res.body.status).to.equal(201); return done(); }); }); }); - describe('Create Meetups', async () => { - await it('Should Create a meetup', (done) => { + it('Should throw an error if meetup does not exist', (done) => { request(server) - .post('/api/v1/meetups') - .send(createMeetup) + .get('/api/v1/meetups/15') + .set('Accept', 'application/json') .set({ Authorization: `Bearer ${adminToken}`, }) .expect('Content-Type', /json/) - .expect(201) + .expect(404) .end((err, res) => { if (err) return done(err); - expect(res.body.status).to.equal(201); + expect(res.body.status).to.equal(404); return done(); }); }); diff --git a/server/test/testData.js b/server/test/testData.js index 652c612..7a0d6fe 100644 --- a/server/test/testData.js +++ b/server/test/testData.js @@ -1,47 +1,42 @@ /* eslint-disable eol-last */ -const createAdmin = { - firstname: 'Thor', - lastname: 'Ragnar', - username: 'earlRagnar', - email: 'ragnarok@gmail.com', - password: 'Odin777', - confirmPassword: 'Odin777', - phonenumber: '08135266484', -}; +import dotenv from 'dotenv'; + +dotenv.config(); + const loginAdmin = { - email: 'ragnarok@gmail.com', - password: 'Odin777', + email: 'legolaslegit@gmail.com', + password: process.env.ADMIN_PASSWORD, }; const createUser = { firstname: 'legolas', lastname: 'Aragorn', - username: 'legoagon', - email: 'legoagon@gmail.com', - password: 'Odin', - confirmPassword: 'Odin', + username: 'legogonn', + email: 'legolasfake@gmail.com', + password: 'Odinsleep', + confirmPassword: 'Odinsleep', phonenumber: '08135266484', }; const userLogin = { - email: 'legoagon@gmail.com', - password: 'Odin', + email: 'legolasfake@gmail.com', + password: 'Odinsleep', }; const invalidUser = { - email: 'gonalons@gmail.com', - password: 'Bor', + email: 'legolasfake@gmail.com', + password: 'Odinslee', }; const createMeetup = { topic: 'Montypoint tech meetup', location: 'Yaba, Lagos state', - date: '1465599344356', + happeningon: '2025-01-23T22:00', image: 'heroku.com', - tags: 'goal yeaah nope', }; + const askQuestion = { meetupid: '1', title: 'title of question', @@ -49,7 +44,6 @@ const askQuestion = { }; export { - createAdmin, loginAdmin, createUser, userLogin, diff --git a/server/test/users.spec.js b/server/test/users.spec.js index 7d6e1c0..ee48712 100644 --- a/server/test/users.spec.js +++ b/server/test/users.spec.js @@ -14,6 +14,7 @@ import { createUser, userLogin, invalidUser, + askQuestion, } from './testData'; should(); @@ -21,7 +22,7 @@ should(); let userToken; describe('Create a user', async () => { - it('Should create a user', (done) => { + it('A prospective user should be able to create an account', (done) => { request(server) .post('/api/v1/auth/signup') .send(createUser) @@ -34,7 +35,7 @@ describe('Create a user', async () => { const userFirstname = res.body.data.firstname; const arrayProp = res.body.data; expect(res.body.status).to.equal(201); - expect(userid).to.equal(1); + expect(userid).to.equal(2); expect(userFirstname).to.equal('legolas'); (arrayProp).should.be.an('object'); return done(); @@ -44,7 +45,7 @@ describe('Create a user', async () => { describe('Login an existing User', async () => { - it('Should Login an existing user', (done) => { + it('A user should be able to login', (done) => { request(server) .post('/api/v1/auth/login') .send(userLogin) @@ -56,7 +57,6 @@ describe('Login an existing User', async () => { expect(res.body.status).to.equal(200); expect(res.body).to.be.an('object'); expect(res.body).to.have.property('token'); - // console.log(res.body.token); userToken = res.body.token; return done(); }); @@ -68,13 +68,33 @@ describe('Login an existing User', async () => { .send(invalidUser) .set('Accept', 'application/json') .expect('Content-Type', /json/) - .expect(404) + .expect(401) .end((err, res) => { if (err) return done(err); - expect(res.body.status).to.equal(404); + expect(res.body.status).to.equal(401); expect(res.body).to.have.property('error'); expect(res.body.error).to.equal('The credentials you provided is incorrect'); return done(); }); }); +}); + + +describe('Create ask questions', async () => { + it('A user should be able to ask a question', (done) => { + request(server) + .post('/api/v1/questions') + .send(askQuestion) + .set('Accept', 'application/json') + .set({ + Authorization: `Bearer ${userToken}`, + }) + .expect('Content-Type', /json/) + .expect(201) + .end((err, res) => { + if (err) return done(err); + expect(res.body.status).to.equal(201); + return done(); + }); + }); }); \ No newline at end of file diff --git a/server/utilities/errorresponses.js b/server/utilities/errorresponses.js new file mode 100644 index 0000000..8ba1da4 --- /dev/null +++ b/server/utilities/errorresponses.js @@ -0,0 +1,12 @@ +/* eslint-disable eol-last */ +const customErrorResponses = { + required: 'Please insert your :attribute.', + alpha: 'Your :attribute should contain only alphabets.', + min: 'Your :attribute should not be less than :min characters long.', + max: 'Your :attribute should not be more than :max characters long.', + email: 'Please insert a valid :attribute address.', + date: 'Please insert a valid date and time', + alpha_num: 'Your :attribute should contain only alphabets and numbers', +}; + +export default customErrorResponses; \ No newline at end of file diff --git a/server/utilities/responseformat.js b/server/utilities/responseformat.js new file mode 100644 index 0000000..376d877 --- /dev/null +++ b/server/utilities/responseformat.js @@ -0,0 +1,34 @@ +/* eslint-disable eol-last */ +const successResponse = (res, statusCode, message, data) => { + const response = { + status: statusCode, + message, + data, + }; + return res.status(statusCode).json(response); +}; + +const successfullLogin = (res, statusCode, message, token, data) => { + const response = { + status: statusCode, + message, + token, + data, + }; + return res.status(statusCode).json(response); +}; + + +const errorResponse = (res, statusCode, message) => { + const response = { + status: statusCode, + error: message, + }; + return res.status(statusCode).json(response); +}; + +export { + successResponse, + errorResponse, + successfullLogin, +}; \ No newline at end of file diff --git a/server/utilities/trimfunction.js b/server/utilities/trimfunction.js new file mode 100644 index 0000000..6b4c62a --- /dev/null +++ b/server/utilities/trimfunction.js @@ -0,0 +1,4 @@ +/* eslint-disable eol-last */ +const convertName = name => (name.charAt(0).toUpperCase() + name.slice(1)).trim(); + +export default convertName; \ No newline at end of file diff --git a/server/utilities/trycatch.js b/server/utilities/trycatch.js new file mode 100644 index 0000000..42b5b32 --- /dev/null +++ b/server/utilities/trycatch.js @@ -0,0 +1,15 @@ +/* eslint-disable eol-last */ +import { + errorResponse, +} from './responseformat'; + +const tryCatch = controller => async (req, res) => { + try { + await controller(req, res); + } catch (error) { + return errorResponse(res, 500, error.message); + } + return true; +}; + +export default tryCatch; \ No newline at end of file