From 48ee8cf08ff8211c80a13182714b6cb19462b723 Mon Sep 17 00:00:00 2001 From: Alexis <35.alexis.pele@gmail.com> Date: Tue, 26 May 2020 21:49:45 +0200 Subject: [PATCH 1/9] - Added dependencies --- package-lock.json | 1292 +++++++++++++++++++++++++++++++++++++++++++++ package.json | 2 + 2 files changed, 1294 insertions(+) diff --git a/package-lock.json b/package-lock.json index 6d450b8..1a368bc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -37,16 +37,106 @@ "readable-stream": "^2.0.6" } }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==" + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=" + }, + "array-each": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", + "integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8=" + }, "array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, + "array-slice": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz", + "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==" + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=" + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" + }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, "basic-auth": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", @@ -69,6 +159,11 @@ "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz", "integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==" }, + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + }, "body-parser": { "version": "1.19.0", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", @@ -86,6 +181,17 @@ "type-is": "~1.6.17" } }, + "bookshelf": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/bookshelf/-/bookshelf-1.1.1.tgz", + "integrity": "sha512-swHlUoCSBv7lS5P6Sbc3zTo64L8Yal98H7v/d1pJLrQLZjiJYjzsdEwnBmnbIr2zDEAZhJW3OZSJFj8ps6o9pQ==", + "requires": { + "bluebird": "^3.7.2", + "create-error": "~0.3.1", + "inflection": "^1.12.0", + "lodash": "^4.17.15" + } + }, "bowser": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.9.0.tgz", @@ -100,6 +206,33 @@ "concat-map": "0.0.1" } }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, "buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", @@ -110,6 +243,22 @@ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, "camelize": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz", @@ -120,11 +269,56 @@ "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, "code-point-at": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "colorette": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.1.0.tgz", + "integrity": "sha512-6S062WDQUXi6hOfkO/sBPVwE5ASXY4G2+b4atvhJfSsuUUhIaUKlkjLe9692Ipyt5/a+IPF5aVTu3V5gvXq5cg==" + }, + "commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==" + }, + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -163,6 +357,11 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=" + }, "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", @@ -177,6 +376,11 @@ "vary": "^1" } }, + "create-error": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/create-error/-/create-error-0.3.1.tgz", + "integrity": "sha1-aYECRaYp5lRDK/BDdzYAA6U1GiM=" + }, "dasherize": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/dasherize/-/dasherize-2.0.0.tgz", @@ -190,11 +394,53 @@ "ms": "2.0.0" } }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" + }, "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==" }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, "delegates": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", @@ -210,6 +456,11 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, + "detect-file": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", + "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=" + }, "detect-libc": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", @@ -248,11 +499,56 @@ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" }, + "esm": { + "version": "3.2.25", + "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", + "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==" + }, "etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", + "requires": { + "homedir-polyfill": "^1.0.1" + } + }, "expect-ct": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/expect-ct/-/expect-ct-0.2.0.tgz", @@ -295,11 +591,115 @@ "vary": "~1.1.2" } }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, "feature-policy": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/feature-policy/-/feature-policy-0.3.0.tgz", "integrity": "sha512-ZtijOTFN7TzCujt1fnNhfWPFPSHeZkesff9AXZj+UEjYBynWNUIYpC87Ve4wHzyexQsImicLu7WsC2LHq7/xrQ==" }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, "finalhandler": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", @@ -314,11 +714,60 @@ "unpipe": "~1.0.0" } }, + "findup-sync": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz", + "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==", + "requires": { + "detect-file": "^1.0.0", + "is-glob": "^4.0.0", + "micromatch": "^3.0.4", + "resolve-dir": "^1.0.1" + } + }, + "fined": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fined/-/fined-1.2.0.tgz", + "integrity": "sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng==", + "requires": { + "expand-tilde": "^2.0.2", + "is-plain-object": "^2.0.3", + "object.defaults": "^1.1.0", + "object.pick": "^1.2.0", + "parse-filepath": "^1.0.1" + } + }, + "flagged-respawn": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz", + "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==" + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=" + }, + "for-own": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", + "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", + "requires": { + "for-in": "^1.0.1" + } + }, "forwarded": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "requires": { + "map-cache": "^0.2.2" + } + }, "frameguard": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/frameguard/-/frameguard-3.1.0.tgz", @@ -357,6 +806,16 @@ "wide-align": "^1.1.0" } }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=" + }, + "getopts": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/getopts/-/getopts-2.2.5.tgz", + "integrity": "sha512-9jb7AW5p3in+IiJWhQiZmmwkpLaR/ccTWdWQCtZM66HJcHHLegowh4q4tSD7gouUyeNvFWRavfK9GXosQHDpFA==" + }, "glob": { "version": "7.1.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", @@ -370,11 +829,62 @@ "path-is-absolute": "^1.0.0" } }, + "global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "requires": { + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" + } + }, + "global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", + "requires": { + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" + } + }, "has-unicode": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, "helmet": { "version": "3.22.0", "resolved": "https://registry.npmjs.org/helmet/-/helmet-3.22.0.tgz", @@ -425,6 +935,14 @@ "resolved": "https://registry.npmjs.org/hide-powered-by/-/hide-powered-by-1.1.0.tgz", "integrity": "sha512-Io1zA2yOA1YJslkr+AJlWSf2yWFkKjvkcL9Ni1XSUqnGLr/qRQe2UI3Cn/J9MsJht7yEVCe0SscY1HgVMujbgg==" }, + "homedir-polyfill": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "requires": { + "parse-passwd": "^1.0.0" + } + }, "hpkp": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/hpkp/-/hpkp-2.0.0.tgz", @@ -478,6 +996,11 @@ "minimatch": "^3.0.4" } }, + "inflection": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.12.0.tgz", + "integrity": "sha1-ogCTVlbW9fa8TcdQLhrstwMihBY=" + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -497,11 +1020,93 @@ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" }, + "interpret": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.0.0.tgz", + "integrity": "sha512-e0/LknJ8wpMMhTiWcjivB+ESwIuvHnBSlBbmP/pSb8CQJldoj1p2qv7xGZ/+BtbTziYRFSz8OsvdbiX45LtYQA==" + }, "ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" }, + "is-absolute": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", + "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", + "requires": { + "is-relative": "^1.0.0", + "is-windows": "^1.0.1" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" + } + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + }, "is-fullwidth-code-point": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", @@ -510,11 +1115,76 @@ "number-is-nan": "^1.0.0" } }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "requires": { + "isobject": "^3.0.1" + } + }, + "is-relative": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", + "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", + "requires": { + "is-unc-path": "^1.0.0" + } + }, + "is-unc-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", + "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", + "requires": { + "unc-path-regex": "^0.1.2" + } + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==" + }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + }, "jsonschema": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.2.6.tgz", @@ -563,6 +1233,78 @@ "safe-buffer": "^5.0.1" } }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" + }, + "knex": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/knex/-/knex-0.21.1.tgz", + "integrity": "sha512-uWszXC2DPaLn/YznGT9wFTWUG9+kqbL4DMz+hCH789GLcLuYzq8werHPDKBJxtKvxrW/S1XIXgrTWdMypiVvsw==", + "requires": { + "colorette": "1.1.0", + "commander": "^5.1.0", + "debug": "4.1.1", + "esm": "^3.2.25", + "getopts": "2.2.5", + "inherits": "~2.0.4", + "interpret": "^2.0.0", + "liftoff": "3.1.0", + "lodash": "^4.17.15", + "mkdirp": "^1.0.4", + "pg-connection-string": "2.2.0", + "tarn": "^3.0.0", + "tildify": "2.0.0", + "uuid": "^7.0.3", + "v8flags": "^3.1.3" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "liftoff": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-3.1.0.tgz", + "integrity": "sha512-DlIPlJUkCV0Ips2zf2pJP0unEoT1kwYhiiPUGF3s/jtxTCjziNLoiVVh+jqWOWeFi6mmwQ5fNxvAUyPad4Dfog==", + "requires": { + "extend": "^3.0.0", + "findup-sync": "^3.0.0", + "fined": "^1.0.1", + "flagged-respawn": "^1.0.0", + "is-plain-object": "^2.0.4", + "object.map": "^1.0.0", + "rechoir": "^0.6.2", + "resolve": "^1.1.7" + } + }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + }, "lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", @@ -598,6 +1340,27 @@ "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" }, + "make-iterator": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz", + "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==", + "requires": { + "kind-of": "^6.0.2" + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=" + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "requires": { + "object-visit": "^1.0.0" + } + }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -613,6 +1376,26 @@ "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, "mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", @@ -661,6 +1444,25 @@ "minipass": "^2.9.0" } }, + "mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, "mkdirp": { "version": "0.5.5", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", @@ -704,6 +1506,24 @@ "sqlstring": "2.3.1" } }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + } + }, "needle": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/needle/-/needle-2.5.0.tgz", @@ -814,6 +1634,70 @@ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "requires": { + "isobject": "^3.0.0" + } + }, + "object.defaults": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz", + "integrity": "sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8=", + "requires": { + "array-each": "^1.0.1", + "array-slice": "^1.0.0", + "for-own": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "object.map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz", + "integrity": "sha1-z4Plncj8wK1fQlDh94s7gb2AHTc=", + "requires": { + "for-own": "^1.0.0", + "make-iterator": "^1.0.0" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "requires": { + "isobject": "^3.0.1" + } + }, "on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", @@ -854,21 +1738,69 @@ "os-tmpdir": "^1.0.0" } }, + "parse-filepath": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", + "integrity": "sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE=", + "requires": { + "is-absolute": "^1.0.0", + "map-cache": "^0.2.0", + "path-root": "^0.1.1" + } + }, + "parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=" + }, "parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=" + }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" + }, + "path-root": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", + "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc=", + "requires": { + "path-root-regex": "^0.1.0" + } + }, + "path-root-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", + "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=" + }, "path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, + "pg-connection-string": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.2.0.tgz", + "integrity": "sha512-xB/+wxcpFipUZOQcSzcgkjcNOosGhEoPSjz06jC89lv1dj7mc9bZv6wLVy8M2fVjP0a/xN0N988YDq1L0FhK3A==" + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=" + }, "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -929,11 +1861,65 @@ "util-deprecate": "~1.0.1" } }, + "rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "requires": { + "resolve": "^1.1.6" + } + }, "referrer-policy": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/referrer-policy/-/referrer-policy-1.2.0.tgz", "integrity": "sha512-LgQJIuS6nAy1Jd88DCQRemyE3mS+ispwlqMk3b0yjZ257fI1v9c+/p6SD5gP5FGyXUIgrNOAfmyioHwZtYv2VA==" }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==" + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" + }, + "resolve": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "requires": { + "path-parse": "^1.0.6" + } + }, + "resolve-dir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", + "requires": { + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" + } + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=" + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==" + }, "rimraf": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", @@ -947,6 +1933,14 @@ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "requires": { + "ret": "~0.1.10" + } + }, "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -1005,6 +1999,27 @@ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" }, + "set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, "setprototypeof": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", @@ -1015,11 +2030,157 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "requires": { + "kind-of": "^3.2.0" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "requires": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=" + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "requires": { + "extend-shallow": "^3.0.0" + } + }, "sqlstring": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.1.tgz", "integrity": "sha1-R1OT/56RR5rqYtyvDKPRSYOn+0A=" }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, "statuses": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", @@ -1070,6 +2231,54 @@ "yallist": "^3.0.3" } }, + "tarn": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tarn/-/tarn-3.0.0.tgz", + "integrity": "sha512-PKUnlDFODZueoA8owLehl8vLcgtA8u4dRuVbZc92tspDYZixjJL6TqYOmryf/PfP/EBX+2rgNcrj96NO+RPkdQ==" + }, + "tildify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tildify/-/tildify-2.0.0.tgz", + "integrity": "sha512-Cc+OraorugtXNfs50hU9KS369rFXCfgGLpfCfvlc+Ud5u6VWmUQsOAa9HbTvheQdYnrdJqqv1e5oIqXppMYnSw==" + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, "toidentifier": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", @@ -1084,11 +2293,73 @@ "mime-types": "~2.1.24" } }, + "unc-path-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", + "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=" + }, + "union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + } + }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=" + } + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=" + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==" + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -1099,11 +2370,32 @@ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" }, + "uuid": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz", + "integrity": "sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==" + }, + "v8flags": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.1.3.tgz", + "integrity": "sha512-amh9CCg3ZxkzQ48Mhcb8iX7xpAfYJgePHxWMQCBWECpOSqJUXgY26ncA61UTV0BkPqfhcy6mzwCIoP4ygxpW8w==", + "requires": { + "homedir-polyfill": "^1.0.1" + } + }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "requires": { + "isexe": "^2.0.0" + } + }, "wide-align": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", diff --git a/package.json b/package.json index 688d1af..7e3086c 100644 --- a/package.json +++ b/package.json @@ -28,11 +28,13 @@ "homepage": "https://github.com/AlexisNP/spellsaurus#readme", "dependencies": { "bcrypt": "^4.0.1", + "bookshelf": "^1.1.1", "cors": "^2.8.5", "express": "^4.17.1", "helmet": "^3.22.0", "jsonschema": "^1.2.6", "jsonwebtoken": "^8.5.1", + "knex": "^0.21.1", "morgan": "^1.10.0", "mysql": "^2.18.1" } From 1280d6ee1ff5babd0257e4cc3c0e7ea9cbd03065 Mon Sep 17 00:00:00 2001 From: Alexis <35.alexis.pele@gmail.com> Date: Wed, 27 May 2020 21:26:35 +0200 Subject: [PATCH 2/9] - Added getAll route for spells with bookshelf --- database/auracle_db_create.sql | 40 +++++------ database/auracle_db_data.sql | 6 +- database/connection.js | 16 ++++- models/{School.js => SchoolValidation.js} | 0 models/Spell.js | 39 ----------- models/SpellValidation.js | 39 +++++++++++ models/{User.js => UserValidation.js} | 0 routes/schools.js | 2 +- routes/spells.js | 84 ++++++++++++++++------- routes/users.js | 2 +- 10 files changed, 139 insertions(+), 89 deletions(-) rename models/{School.js => SchoolValidation.js} (100%) create mode 100644 models/SpellValidation.js rename models/{User.js => UserValidation.js} (100%) diff --git a/database/auracle_db_create.sql b/database/auracle_db_create.sql index f36cf18..c07b3c1 100644 --- a/database/auracle_db_create.sql +++ b/database/auracle_db_create.sql @@ -29,9 +29,9 @@ CREATE TABLE IF NOT EXISTS `school` ( `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, `name` VARCHAR(255) NOT NULL DEFAULT "Nom de l'école", `description` VARCHAR(255) DEFAULT "Description de l'école", - `id_meta_school` INT UNSIGNED NOT NULL, + `meta_school_id` INT UNSIGNED NOT NULL, PRIMARY KEY (`id`), - FOREIGN KEY(`id_meta_school`) REFERENCES meta_school(`id`) + FOREIGN KEY(`meta_school_id`) REFERENCES meta_school(`id`) ); /* COMMON INGREDIENTS */ @@ -63,30 +63,30 @@ CREATE TABLE IF NOT EXISTS `user` ( /* SPELLS' SCHOOLS */ /* One spell can have multiple (up to 3) schools */ -CREATE TABLE IF NOT EXISTS `spells_schools` ( - `id_spell` INT UNSIGNED NOT NULL, - `id_school` INT UNSIGNED NOT NULL, - PRIMARY KEY (`id_spell`, `id_school`), - FOREIGN KEY(`id_spell`) REFERENCES spell(`id`), - FOREIGN KEY(`id_school`) REFERENCES school(`id`) +CREATE TABLE IF NOT EXISTS `spell_school` ( + `spell_id` INT UNSIGNED NOT NULL, + `school_id` INT UNSIGNED NOT NULL, + PRIMARY KEY (`spell_id`, `school_id`), + FOREIGN KEY(`spell_id`) REFERENCES spell(`id`), + FOREIGN KEY(`school_id`) REFERENCES school(`id`) ); /* SPELLS' VARIABLES */ /* One spell can have multiple (up to 2) variables of cost */ -CREATE TABLE IF NOT EXISTS `spells_variables` ( - `id_spell` INT UNSIGNED NOT NULL, - `id_variable` INT UNSIGNED NOT NULL, - PRIMARY KEY (`id_spell`, `id_variable`), - FOREIGN KEY(`id_spell`) REFERENCES spell(`id`), - FOREIGN KEY(`id_variable`) REFERENCES variable(`id`) +CREATE TABLE IF NOT EXISTS `spell_variable` ( + `spell_id` INT UNSIGNED NOT NULL, + `variable_id` INT UNSIGNED NOT NULL, + PRIMARY KEY (`spell_id`, `variable_id`), + FOREIGN KEY(`spell_id`) REFERENCES spell(`id`), + FOREIGN KEY(`variable_id`) REFERENCES variable(`id`) ); /* SPELLS' VARIABLES */ /* One spell can have multiple ingredients */ -CREATE TABLE IF NOT EXISTS `spells_ingredients` ( - `id_spell` INT UNSIGNED NOT NULL, - `id_ingredient` INT UNSIGNED NOT NULL, - PRIMARY KEY (`id_spell`, `id_ingredient`), - FOREIGN KEY(`id_spell`) REFERENCES spell(`id`), - FOREIGN KEY(`id_ingredient`) REFERENCES ingredient(`id`) +CREATE TABLE IF NOT EXISTS `spell_ingredient` ( + `spell_id` INT UNSIGNED NOT NULL, + `ingredient_id` INT UNSIGNED NOT NULL, + PRIMARY KEY (`spell_id`, `ingredient_id`), + FOREIGN KEY(`spell_id`) REFERENCES spell(`id`), + FOREIGN KEY(`ingredient_id`) REFERENCES ingredient(`id`) ); diff --git a/database/auracle_db_data.sql b/database/auracle_db_data.sql index 350946c..e0a2fb3 100644 --- a/database/auracle_db_data.sql +++ b/database/auracle_db_data.sql @@ -12,7 +12,7 @@ INSERT INTO `meta_school` (name, description) VALUES ('Magies autres', 'Magies trop spécifiques et ne rentrant dans aucune autre grande école.'); -- SCHOOLS -INSERT INTO `school` (name, description, id_meta_school) VALUES +INSERT INTO `school` (name, description, meta_school_id) VALUES ('Lumomancie', 'Discipline arcanique de la lumière.', 1), ('Vitamancie', 'Discipline arcanique de la guérison et de l\'énergie vitale.', 1), ('Obstrumancie', 'Discipline arcanique de la protection et des sceaux.', 1), @@ -223,11 +223,11 @@ SCHEMA */ DELIMITER $$ -CREATE PROCEDURE insertIntoSchoolRange(IN delimiter_start INT, IN delimiter_end INT, IN school_id INT) +CREATE PROCEDURE insertIntoSchoolRange(IN delimiter_start INT, IN delimiter_end INT, IN id_school INT) BEGIN SET @i = delimiter_start; WHILE @i <= delimiter_end DO - INSERT INTO spells_schools (id_spell, id_school) VALUES (@i, school_id); + INSERT INTO spell_school (spell_id, school_id) VALUES (@i, id_school); SET @i = @i + 1; END WHILE; END$$ diff --git a/database/connection.js b/database/connection.js index 3084136..d890da9 100644 --- a/database/connection.js +++ b/database/connection.js @@ -6,6 +6,20 @@ const mysql = require('mysql') let credentials_data = fs.readFileSync('./database/credentials.json') let credentials = JSON.parse(credentials_data) +// Setting up the database connection +const knex = require('knex')({ + client: credentials.client, + connection: { + host : credentials.host, + user : credentials.user, + password : credentials.password, + database : credentials.database, + charset : credentials.charset + }, + debug: true +}) +const bookshelf = require('bookshelf')(knex) + const db = mysql.createConnection({ host: credentials.host, user: credentials.user, @@ -19,4 +33,4 @@ db.on('error', err => { } }) -module.exports = { db } \ No newline at end of file +module.exports = { db, bookshelf } \ No newline at end of file diff --git a/models/School.js b/models/SchoolValidation.js similarity index 100% rename from models/School.js rename to models/SchoolValidation.js diff --git a/models/Spell.js b/models/Spell.js index 38284fb..e69de29 100644 --- a/models/Spell.js +++ b/models/Spell.js @@ -1,39 +0,0 @@ -const Spell = { - "id": "/SpellModel", - "type": Object, - "properties": { - "name": { "type": "string" }, - "description": { "type": "string" }, - "level": { "type": "number" }, - "charge": { "type": "number" }, - "cost": { "type": "string" }, - "is_ritual": { "type": "boolean" }, - "schools": { - "type": { "type": "array" }, - "items": { - "properties": { - "id": { "type": "number" }, - } - } - }, - "variables": { - "type": { "type": "array" }, - "items": { - "properties": { - "id": { "type": "number" }, - } - } - }, - "ingredients": { - "type": { "type": "array" }, - "items": { - "properties": { - "id": { "type": "number" }, - } - } - } - }, - "required": ["name", "description"] -} - -module.exports = Spell \ No newline at end of file diff --git a/models/SpellValidation.js b/models/SpellValidation.js new file mode 100644 index 0000000..da0167e --- /dev/null +++ b/models/SpellValidation.js @@ -0,0 +1,39 @@ +const SpellModel = { + "id": "/SpellModel", + "type": Object, + "properties": { + "name": { "type": "string" }, + "description": { "type": "string" }, + "level": { "type": "number" }, + "charge": { "type": "number" }, + "cost": { "type": "string" }, + "is_ritual": { "type": "boolean" }, + "schools": { + "type": { "type": "array" }, + "items": { + "properties": { + "id": { "type": "number" }, + } + } + }, + "variables": { + "type": { "type": "array" }, + "items": { + "properties": { + "id": { "type": "number" }, + } + } + }, + "ingredients": { + "type": { "type": "array" }, + "items": { + "properties": { + "id": { "type": "number" }, + } + } + } + }, + "required": ["name", "description"] +} + +module.exports = SpellModel \ No newline at end of file diff --git a/models/User.js b/models/UserValidation.js similarity index 100% rename from models/User.js rename to models/UserValidation.js diff --git a/routes/schools.js b/routes/schools.js index cb5b825..daceed1 100644 --- a/routes/schools.js +++ b/routes/schools.js @@ -15,7 +15,7 @@ const regexXSS = RegExp(/<[^>]*script/) // Model validation const Validator = require('jsonschema').Validator const v = new Validator() -const School = require("../models/School") +const School = require("../models/SchoolValidation") v.addSchema(School, "/SchoolModel") // Error handling diff --git a/routes/spells.js b/routes/spells.js index d208043..8c31e98 100644 --- a/routes/spells.js +++ b/routes/spells.js @@ -12,11 +12,58 @@ const db = connection.db const regexInt = RegExp(/^[1-9]\d*$/) const regexXSS = RegExp(/<[^>]*script/) +// Bookshelf Models +const bookshelf = require('../database/connection').bookshelf + +const Spell = bookshelf.Model.extend({ + tableName: 'spell', + schools() { + return this.belongsToMany( School, 'spell_school', 'spell_id', 'school_id' ) + }, + variables() { + return this.belongsToMany( Variable, 'spell_variable', 'spell_id', 'variable_id' ) + }, + ingredients() { + return this.belongsToMany( Ingredient, 'spell_ingredient', 'spell_id', 'ingredient_id' ) + } +}) + +const MetaSchool = bookshelf.Model.extend({ + tableName: 'meta_school', + schools() { + return this.hasMany( School ) + } +}) + +const School = bookshelf.Model.extend({ + tableName: 'school', + spells() { + return this.belongsToMany( Spell, 'spell_school', 'school_id', 'spell_id') + }, + meta_school() { + return this.belongsTo( MetaSchool, 'meta_school_id') + } +}) + +const Ingredient = bookshelf.Model.extend({ + tableName: 'ingredient', + spells() { + return this.belongsToMany( Spell, 'spell_ingredient', 'ingredient_id', 'spell_id') + } +}) + +const Variable = bookshelf.Model.extend({ + tableName: 'variable', + spells() { + return this.belongsToMany( Spell, 'spell_variable', 'variable_id', 'spell_id') + } +}) + // Model validation const Validator = require('jsonschema').Validator const v = new Validator() -const Spell = require("../models/Spell") -v.addSchema(Spell, "/SpellModel") +const SpellModel = require("../models/SpellValidation") +v.addSchema(SpellModel, "/SpellModel") // Error handling const { HttpError } = require('../models/Errors') @@ -26,25 +73,14 @@ const { HttpError } = require('../models/Errors') const getSpells = () => { return new Promise((resolve, reject) => { - let query = "SELECT DISTINCT * FROM spell" - - db.query(query, async (err, result) => { - if (err) { - reject(new HttpError(500, 'Database error')) - } else if (result.length == 0) { - reject(new HttpError(404, 'No spells were found')) - } - - // Loops over the results to fetch the associated tables - for (let i = 0; i < result.length; i++) { - try { - result[i] = await buildSpell(result[i]) - } catch (err) { - reject(err) - } - } - resolve(result) + Spell.forge().fetchAll({ withRelated: ['schools.meta_school', 'variables', 'ingredients'] }) + .then(v => { + resolve(v.toJSON({ omitPivot: true })) }) + .catch(err => { + reject(new HttpError(500, err.message)) + }) + }) .catch(err => { throw err @@ -113,8 +149,8 @@ const addSpell = (s) => { // Checks if body exists and if the model fits, and throws errors if it doesn't if (isEmptyObject(s)) { reject(new HttpError(403, "Error: Spell cannot be nothing !")) - } else if (!v.validate(s, Spell).valid) { - reject(new HttpError(403, "Error: Schema is not valid - " + v.validate(s, Spell).errors)) + } else if (!v.validate(s, SpellModel).valid) { + reject(new HttpError(403, "Error: Schema is not valid - " + v.validate(s, SpellModel).errors)) } else if (isXSSAttempt(s.name) || isXSSAttempt(s.description) || isXSSAttempt(s.cost)) { reject(new HttpError(403, 'Injection attempt detected, aborting the request.')) } else { @@ -271,8 +307,8 @@ const updateSpell = (s, id) => { // Checks if body exists and if the model fits, and throws errors if it doesn't if (isEmptyObject(s)) { reject(new HttpError(403, "Error: Spell cannot be nothing !")) - } else if (!v.validate(s, Spell).valid) { - reject(new HttpError(403, "Error: Schema is not valid - " + v.validate(s, Spell).errors)) + } else if (!v.validate(s, SpellModel).valid) { + reject(new HttpError(403, "Error: Schema is not valid - " + v.validate(s, SpellModel).errors)) } else if (isXSSAttempt(s.name) || isXSSAttempt(s.description) || isXSSAttempt(s.cost)) { reject(new HttpError(403, 'Injection attempt detected, aborting the request.')) } else { diff --git a/routes/users.js b/routes/users.js index e62360a..793526b 100644 --- a/routes/users.js +++ b/routes/users.js @@ -16,7 +16,7 @@ const regexXSS = RegExp(/<[^>]*script/) // Model validation const Validator = require('jsonschema').Validator const v = new Validator() -const User = require("../models/User") +const User = require("../models/UserValidation") v.addSchema(User, "/UserModel") // Error handling From cabbf68f7a419f468ec7793ddc7c513af32a2767 Mon Sep 17 00:00:00 2001 From: Alexis <35.alexis.pele@gmail.com> Date: Tue, 2 Jun 2020 17:19:54 +0200 Subject: [PATCH 3/9] - Worked on spell things --- routes/spells.js | 270 ++++++++++++++++++++++++----------------------- 1 file changed, 140 insertions(+), 130 deletions(-) diff --git a/routes/spells.js b/routes/spells.js index 8c31e98..d989d19 100644 --- a/routes/spells.js +++ b/routes/spells.js @@ -73,12 +73,18 @@ const { HttpError } = require('../models/Errors') const getSpells = () => { return new Promise((resolve, reject) => { - Spell.forge().fetchAll({ withRelated: ['schools.meta_school', 'variables', 'ingredients'] }) + Spell.forge() + .fetchAll({ withRelated: ['schools.meta_school', 'variables', 'ingredients'] }) .then(v => { resolve(v.toJSON({ omitPivot: true })) }) .catch(err => { - reject(new HttpError(500, err.message)) + console.log(err) + if (err.message == "EmptyResponse") { + reject(new HttpError(404, "No spells matching this id were found")) + } else { + reject(new HttpError(500, err.message)) + } }) }) @@ -107,19 +113,16 @@ router.get('/', async (req, res) => { const getSpell = (id) => { return new Promise((resolve, reject) => { - let query = "SELECT * FROM spell WHERE id = " + db.escape(id) - - db.query(query, async (err, result) => { - if (err) { - reject(new HttpError(500, 'Database error')) - } - try { - result = buildSpell(result[0]) - resolve(result) - } catch (err) { - reject(err) - } + Spell.forge() + .where({ 'id' : id }) + .fetch({ withRelated: ['schools.meta_school', 'variables', 'ingredients'] }) + .then(v => { + resolve(v.toJSON({ omitPivot: true })) }) + .catch(err => { + reject(new HttpError(500, err.message)) + }) + }) .catch(err => { throw err @@ -154,124 +157,131 @@ const addSpell = (s) => { } else if (isXSSAttempt(s.name) || isXSSAttempt(s.description) || isXSSAttempt(s.cost)) { reject(new HttpError(403, 'Injection attempt detected, aborting the request.')) } else { - let query = - 'INSERT INTO spell (name, description' - if (s.level !== null) { query += ', level' } - if (s.charge !== null) { query += ', charge' } - if (s.cost !== null) { query += ', cost' } - if (s.is_ritual !== null) { query += ', is_ritual' } - - query += `) VALUES (${db.escape(s.name)}, ${db.escape(s.description)}` - - if (s.level !== null) { query += `, ${s.level}` } - if (s.charge !== null) { query += `, ${s.charge}` } - if (s.cost !== null) { query += `, ${db.escape(s.cost)}` } - if (s.is_ritual !== null) { query += `, ${s.is_ritual}` } - - query += ')' - - db.query(query, async (err, result) => { - if (err) { - reject(new HttpError(500, 'Database error')) - } - console.log(`Inserted "${s.name}" with ID ${result.insertId}, affecting ${result.affectedRows} row(s)`) - - const new_spell_id = result.insertId - - let addSchoolsData = () => { - return new Promise((resolve, reject) => { - if (s.schools !== null) { - if (s.schools.length > 0) { - for (let i = 0; i < s.schools.length; i++) { - if (!regexInt.test(s.schools[i].id)) { - reject(new HttpError(403, 'Query error - School ID should be an integer !')) - } - - let insert_schools_query = `INSERT INTO spells_schools (id_spell, id_school) VALUES (${new_spell_id}, ${s.schools[i].id})` - - db.query(insert_schools_query, async (err, result) => { - if (err) { - reject(new HttpError(404, 'Database error - No schools matching this ID')) - } else { - console.log(`Associated school ID ${s.schools[i].id} to spell ID ${new_spell_id}, affecting ${result.affectedRows} row(s)`) - resolve() - } - }) - } - } else { - resolve() - } - } else { - resolve() - } - }) - } - - let addVariablesData = () => { - return new Promise((resolve, reject) => { - if (s.variables !== null) { - if (s.variables.length > 0) { - for (let i = 0; i < s.variables.length; i++) { - if (!regexInt.test(s.variables[i].id)) { - reject(new HttpError(403, 'Query error - Variable ID should be an integer !')) - } - - let insert_variables_query = `INSERT INTO spells_variables (id_spell, id_variable) VALUES (${new_spell_id}, ${s.variables[i].id})` - db.query(insert_variables_query, async (err, result) => { - if (err) { - reject(new HttpError(404, 'Database error - No variables matching this ID')) - } else { - console.log(`Associated variable ID ${s.variables[i].id} to spell ID ${new_spell_id}, affecting ${result.affectedRows} row(s)`) - resolve() - } - }) - } - } else { - resolve() - } - } else { - resolve() - } - }) - } - - let addIngredientsData = () => { - return new Promise((resolve, reject) => { - if (s.ingredients !== null) { - if (s.ingredients.length > 0) { - for (let i = 0; i < s.ingredients.length; i++) { - if (!regexInt.test(s.ingredients[i].id)) { - reject(new HttpError(403, 'Query error - Ingredient ID should be an integer !')) - } - - let insert_ingredients_query = `INSERT INTO spells_ingredients (id_spell, id_ingredient) VALUES (${new_spell_id}, ${s.ingredients[i].id})` - db.query(insert_ingredients_query, async (err, result) => { - if (err) { - reject(new HttpError(404, 'Database error - No ingredients matching this ID')) - } else { - console.log(`Associated ingredient ID ${s.ingredients[i].id} to spell ID ${new_spell_id}, affecting ${result.affectedRows} row(s)`) - resolve() - } - }) - } - } else { - resolve() - } - } else { - resolve() - } - }) - } - - Promise.all([addSchoolsData(), addVariablesData(), addIngredientsData()]) - .then(() => { - resolve(getSpell(new_spell_id)) - }) - .catch(err => { - reject(err) - }) + Spell.forge({ + 'name': s.name, + 'description': s.description, + 'level': s.level, }) + + // let query = + // 'INSERT INTO spell (name, description' + + // if (s.level !== null) { query += ', level' } + // if (s.charge !== null) { query += ', charge' } + // if (s.cost !== null) { query += ', cost' } + // if (s.is_ritual !== null) { query += ', is_ritual' } + + // query += `) VALUES (${db.escape(s.name)}, ${db.escape(s.description)}` + + // if (s.level !== null) { query += `, ${s.level}` } + // if (s.charge !== null) { query += `, ${s.charge}` } + // if (s.cost !== null) { query += `, ${db.escape(s.cost)}` } + // if (s.is_ritual !== null) { query += `, ${s.is_ritual}` } + + // query += ')' + + // db.query(query, async (err, result) => { + // if (err) { + // reject(new HttpError(500, 'Database error')) + // } + // console.log(`Inserted "${s.name}" with ID ${result.insertId}, affecting ${result.affectedRows} row(s)`) + + // const new_spell_id = result.insertId + + // let addSchoolsData = () => { + // return new Promise((resolve, reject) => { + // if (s.schools !== null) { + // if (s.schools.length > 0) { + // for (let i = 0; i < s.schools.length; i++) { + // if (!regexInt.test(s.schools[i].id)) { + // reject(new HttpError(403, 'Query error - School ID should be an integer !')) + // } + + // let insert_schools_query = `INSERT INTO spells_schools (id_spell, id_school) VALUES (${new_spell_id}, ${s.schools[i].id})` + + // db.query(insert_schools_query, async (err, result) => { + // if (err) { + // reject(new HttpError(404, 'Database error - No schools matching this ID')) + // } else { + // console.log(`Associated school ID ${s.schools[i].id} to spell ID ${new_spell_id}, affecting ${result.affectedRows} row(s)`) + // resolve() + // } + // }) + // } + // } else { + // resolve() + // } + // } else { + // resolve() + // } + // }) + // } + + // let addVariablesData = () => { + // return new Promise((resolve, reject) => { + // if (s.variables !== null) { + // if (s.variables.length > 0) { + // for (let i = 0; i < s.variables.length; i++) { + // if (!regexInt.test(s.variables[i].id)) { + // reject(new HttpError(403, 'Query error - Variable ID should be an integer !')) + // } + + // let insert_variables_query = `INSERT INTO spells_variables (id_spell, id_variable) VALUES (${new_spell_id}, ${s.variables[i].id})` + // db.query(insert_variables_query, async (err, result) => { + // if (err) { + // reject(new HttpError(404, 'Database error - No variables matching this ID')) + // } else { + // console.log(`Associated variable ID ${s.variables[i].id} to spell ID ${new_spell_id}, affecting ${result.affectedRows} row(s)`) + // resolve() + // } + // }) + // } + // } else { + // resolve() + // } + // } else { + // resolve() + // } + // }) + // } + + // let addIngredientsData = () => { + // return new Promise((resolve, reject) => { + // if (s.ingredients !== null) { + // if (s.ingredients.length > 0) { + // for (let i = 0; i < s.ingredients.length; i++) { + // if (!regexInt.test(s.ingredients[i].id)) { + // reject(new HttpError(403, 'Query error - Ingredient ID should be an integer !')) + // } + + // let insert_ingredients_query = `INSERT INTO spells_ingredients (id_spell, id_ingredient) VALUES (${new_spell_id}, ${s.ingredients[i].id})` + // db.query(insert_ingredients_query, async (err, result) => { + // if (err) { + // reject(new HttpError(404, 'Database error - No ingredients matching this ID')) + // } else { + // console.log(`Associated ingredient ID ${s.ingredients[i].id} to spell ID ${new_spell_id}, affecting ${result.affectedRows} row(s)`) + // resolve() + // } + // }) + // } + // } else { + // resolve() + // } + // } else { + // resolve() + // } + // }) + // } + + // Promise.all([addSchoolsData(), addVariablesData(), addIngredientsData()]) + // .then(() => { + // resolve(getSpell(new_spell_id)) + // }) + // .catch(err => { + // reject(err) + // }) + // }) } }).catch(err => { throw err From 369d53d509ada2ed31d9e0233448c8ffb8854d96 Mon Sep 17 00:00:00 2001 From: Alexis <35.alexis.pele@gmail.com> Date: Sat, 6 Jun 2020 00:17:01 +0200 Subject: [PATCH 4/9] - Did the unthinkable and actually achieved Peak ORM --- models/Spell.js | 0 repositories/ingredient-repository.js | 27 ++ repositories/meta-school-repository.js | 27 ++ repositories/school-repository.js | 33 +++ repositories/spell-repository.js | 160 ++++++++++++ repositories/user-repository.js | 85 +++++++ repositories/variable-repository.js | 27 ++ routes/spells.js | 340 +------------------------ routes/users.js | 76 +----- 9 files changed, 381 insertions(+), 394 deletions(-) delete mode 100644 models/Spell.js create mode 100644 repositories/ingredient-repository.js create mode 100644 repositories/meta-school-repository.js create mode 100644 repositories/school-repository.js create mode 100644 repositories/spell-repository.js create mode 100644 repositories/user-repository.js create mode 100644 repositories/variable-repository.js diff --git a/models/Spell.js b/models/Spell.js deleted file mode 100644 index e69de29..0000000 diff --git a/repositories/ingredient-repository.js b/repositories/ingredient-repository.js new file mode 100644 index 0000000..285d539 --- /dev/null +++ b/repositories/ingredient-repository.js @@ -0,0 +1,27 @@ +'use strict' +// Bookshelf +const bookshelf = require('../database/connection').bookshelf + +const Spells = require('./spell-repository') + +class IngredientRepository { + + constructor() { + this.model = bookshelf.Model.extend({ + tableName: 'ingredient', + spells() { + return this.belongsToMany( Spells._model, 'spell_ingredient', 'ingredient_id', 'spell_id') + } + }) + } + + set model(model) { + this._model = model + } + + get model() { + return this._model + } +} + +module.exports = IngredientRepository \ No newline at end of file diff --git a/repositories/meta-school-repository.js b/repositories/meta-school-repository.js new file mode 100644 index 0000000..c8f8960 --- /dev/null +++ b/repositories/meta-school-repository.js @@ -0,0 +1,27 @@ +'use strict' +// Bookshelf +const bookshelf = require('../database/connection').bookshelf + +const Schools = require('./school-repository') + +class MetaSchoolRepository { + + constructor() { + this.model = bookshelf.Model.extend({ + tableName: 'meta_school', + schools() { + return this.hasMany( Schools._model ) + } + }) + } + + set model(model) { + this._model = model + } + + get model() { + return this._model + } +} + +module.exports = MetaSchoolRepository \ No newline at end of file diff --git a/repositories/school-repository.js b/repositories/school-repository.js new file mode 100644 index 0000000..f625679 --- /dev/null +++ b/repositories/school-repository.js @@ -0,0 +1,33 @@ +'use strict' +// Bookshelf +const bookshelf = require('../database/connection').bookshelf + +const Spells = require('./spell-repository') + +const MetaSchoolRepository = require('./meta-school-repository') +const MetaSchools = new MetaSchoolRepository() + +class SchoolRepository { + + constructor() { + this.model = bookshelf.Model.extend({ + tableName: 'school', + spells() { + return this.belongsToMany( Spells._model, 'spell_school', 'school_id', 'spell_id') + }, + meta_schools() { + return this.belongsTo( MetaSchools._model, 'meta_school_id') + } + }) + } + + set model(model) { + this._model = model + } + + get model() { + return this._model + } +} + +module.exports = SchoolRepository \ No newline at end of file diff --git a/repositories/spell-repository.js b/repositories/spell-repository.js new file mode 100644 index 0000000..fdf2c6c --- /dev/null +++ b/repositories/spell-repository.js @@ -0,0 +1,160 @@ +'use strict' + +// Bookshelf +const bookshelf = require('../database/connection').bookshelf + +const SchoolRepository = require('./school-repository') +const Schools = new SchoolRepository() + +const IngredientRepository = require('./ingredient-repository') +const Ingredients = new IngredientRepository() + +const VariableRepository = require('./variable-repository') +const Variables = new VariableRepository() + +// Model validation +const Validator = require('jsonschema').Validator +const v = new Validator() +const SpellModel = require("../models/SpellValidation") +v.addSchema(SpellModel, "/SpellModel") + +// Validations +const regexXSS = RegExp(/<[^>]*script/) + +// Error handling +const { HttpError } = require('../models/Errors') + +class SpellRepository { + + constructor() { + this.model = bookshelf.Model.extend({ + tableName: 'spell', + schools() { + return this.belongsToMany( Schools._model, 'spell_school', 'spell_id', 'school_id' ) + }, + variables() { + return this.belongsToMany( Variables._model, 'spell_variable', 'spell_id', 'variable_id' ) + }, + ingredients() { + return this.belongsToMany( Ingredients._model, 'spell_ingredient', 'spell_id', 'ingredient_id' ) + } + }) + } + + set model(model) { + this._model = model + } + + get model() { + return this._model + } + + getAll() { + return new Promise((resolve, reject) => { + this._model.forge() + .fetchAll({ withRelated: ['schools.meta_schools', 'variables', 'ingredients'] }) + .then(v => { + resolve(v.toJSON({ omitPivot: true })) + }) + .catch(err => { + console.log(err) + reject(new HttpError(500, "Couldn't get spells")) + }) + }) + } + + getOne(id) { + return new Promise((resolve, reject) => { + this._model.forge() + .where({ 'id' : id }) + .fetch({ withRelated: ['schools.meta_schools', 'variables', 'ingredients'] }) + .then(v => { + resolve(v.toJSON({ omitPivot: true })) + }) + .catch(err => { + console.log(err) + reject(new HttpError(500, "Couldn't get spell")) + }) + }) + } + + addOne(s) { + return new Promise((resolve, reject) => { + // Checks if body exists and if the model fits, and throws errors if it doesn't + if (this.isEmptyObject(s)) { + reject(new HttpError(403, "Error: Spell cannot be nothing !")) + } else if (!v.validate(s, SpellModel).valid) { + reject(new HttpError(403, "Error: Schema is not valid - " + v.validate(s, SpellModel).errors)) + } else if (this.isXSSAttempt(s.name) || this.isXSSAttempt(s.description) || this.isXSSAttempt(s.cost)) { + reject(new HttpError(403, 'Injection attempt detected, aborting the request.')) + } else { + bookshelf.transaction(t => { + return this._model.forge({ + 'name': s.name, + 'description': s.description, + 'level': s.level, + 'charge' : s.charge, + 'cost' : s.cost, + 'is_ritual' : s.is_ritual + }).save(null, { + transacting: t + }) + .tap(spell => { + return spell + .schools() + .attach(s.schools, { + transacting: t + }); + }) + .tap(spell => { + return spell + .variables() + .attach(s.variables, { + transacting: t + }); + }) + .tap(spell => { + return spell + .ingredients() + .attach(s.ingredients, { + transacting: t + }); + }) + .catch(err => { + throw err + }) + }) + .then(v => { + return v.load(['schools.meta_schools', 'variables', 'ingredients']) + }) + .then(v => { + resolve(this.getOne(v.id)) + }) + .catch(err => { + console.log(err) + reject(new HttpError(500, "Insert error")) + }) + } + }) + } + + // Check if object is null + isEmptyObject = (obj) => { + if (Object.keys(obj).length === 0 && obj.constructor === Object) { + return true + } else { + return false + } + } + + // Check if script injection attempt + isXSSAttempt = (string) => { + if (regexXSS.test(string)) { + return true + } else { + return false + } + } +} + +module.exports = SpellRepository \ No newline at end of file diff --git a/repositories/user-repository.js b/repositories/user-repository.js new file mode 100644 index 0000000..b6d1abc --- /dev/null +++ b/repositories/user-repository.js @@ -0,0 +1,85 @@ +'use strict' + +// Bookshelf +const bookshelf = require('../database/connection').bookshelf + +// Hashing and passwords +const bcrypt = require('bcrypt') + +// Model validation +const Validator = require('jsonschema').Validator +const v = new Validator() +const UserModel = require("../models/UserValidation") +v.addSchema(UserModel, "/UserModel") + +// Validations +const regexInt = RegExp(/^[1-9]\d*$/) +const regexXSS = RegExp(/<[^>]*script/) + +// Error handling +const { HttpError } = require('../models/Errors') + +class UserRepository { + constructor() { + this.model = bookshelf.Model.extend({ + tableName: 'user', + }) + } + + set model(model) { + this._model = model + } + + get model() { + return this._model + } + + getAll() { + return new Promise((resolve, reject) => { + this._model.forge() + .fetchAll() + .then(v => { + resolve(v.toJSON({ omitPivot: true })) + }) + .catch(err => { + console.log(err) + reject(new HttpError(500, "Couldn't get users")) + }) + }) + } + + getOne(id) { + return new Promise((resolve, reject) => { + this._model.forge() + .where({ 'id' : id }) + .fetch() + .then(v => { + resolve(v.toJSON({ omitPivot: true })) + }) + .catch(err => { + console.log(err) + reject(new HttpError(500, "Couldn't get user")) + }) + }) + } + + // Check if object is null + isEmptyObject = (obj) => { + if (Object.keys(obj).length === 0 && obj.constructor === Object) { + return true + } else { + return false + } + } + + // Check if script injection attempt + isXSSAttempt = (string) => { + if (regexXSS.test(string)) { + return true + } else { + return false + } + } +} + +module.exports = UserRepository \ No newline at end of file diff --git a/repositories/variable-repository.js b/repositories/variable-repository.js new file mode 100644 index 0000000..2be3ca1 --- /dev/null +++ b/repositories/variable-repository.js @@ -0,0 +1,27 @@ +'use strict' +// Bookshelf +const bookshelf = require('../database/connection').bookshelf + +const Spells = require('./spell-repository') + +class VariableRepository { + + constructor() { + this.model = bookshelf.Model.extend({ + tableName: 'variable', + spells() { + return this.belongsToMany( Spells._model, 'spell_variable', 'variable_id', 'spell_id') + } + }) + } + + set model(model) { + this._model = model + } + + get model() { + return this._model + } +} + +module.exports = VariableRepository \ No newline at end of file diff --git a/routes/spells.js b/routes/spells.js index d989d19..043b8ab 100644 --- a/routes/spells.js +++ b/routes/spells.js @@ -8,87 +8,18 @@ let router = express.Router() const connection = require('../database/connection') const db = connection.db -// Validations +// Repository +const SpellReposity = require('../repositories/spell-repository'); +const Spells = new SpellReposity(); + const regexInt = RegExp(/^[1-9]\d*$/) -const regexXSS = RegExp(/<[^>]*script/) - -// Bookshelf Models -const bookshelf = require('../database/connection').bookshelf - -const Spell = bookshelf.Model.extend({ - tableName: 'spell', - schools() { - return this.belongsToMany( School, 'spell_school', 'spell_id', 'school_id' ) - }, - variables() { - return this.belongsToMany( Variable, 'spell_variable', 'spell_id', 'variable_id' ) - }, - ingredients() { - return this.belongsToMany( Ingredient, 'spell_ingredient', 'spell_id', 'ingredient_id' ) - } -}) - -const MetaSchool = bookshelf.Model.extend({ - tableName: 'meta_school', - schools() { - return this.hasMany( School ) - } -}) - -const School = bookshelf.Model.extend({ - tableName: 'school', - spells() { - return this.belongsToMany( Spell, 'spell_school', 'school_id', 'spell_id') - }, - meta_school() { - return this.belongsTo( MetaSchool, 'meta_school_id') - } -}) - -const Ingredient = bookshelf.Model.extend({ - tableName: 'ingredient', - spells() { - return this.belongsToMany( Spell, 'spell_ingredient', 'ingredient_id', 'spell_id') - } -}) - -const Variable = bookshelf.Model.extend({ - tableName: 'variable', - spells() { - return this.belongsToMany( Spell, 'spell_variable', 'variable_id', 'spell_id') - } -}) - -// Model validation -const Validator = require('jsonschema').Validator -const v = new Validator() -const SpellModel = require("../models/SpellValidation") -v.addSchema(SpellModel, "/SpellModel") - -// Error handling -const { HttpError } = require('../models/Errors') // ROUTES // GET ALL ------------------ const getSpells = () => { - return new Promise((resolve, reject) => { - - Spell.forge() - .fetchAll({ withRelated: ['schools.meta_school', 'variables', 'ingredients'] }) - .then(v => { - resolve(v.toJSON({ omitPivot: true })) - }) - .catch(err => { - console.log(err) - if (err.message == "EmptyResponse") { - reject(new HttpError(404, "No spells matching this id were found")) - } else { - reject(new HttpError(500, err.message)) - } - }) - - }) + return Spells.getAll() .catch(err => { + console.log(err) throw err }) } @@ -111,20 +42,9 @@ router.get('/', async (req, res) => { // GET ONE ------------------ const getSpell = (id) => { - return new Promise((resolve, reject) => { - - Spell.forge() - .where({ 'id' : id }) - .fetch({ withRelated: ['schools.meta_school', 'variables', 'ingredients'] }) - .then(v => { - resolve(v.toJSON({ omitPivot: true })) - }) - .catch(err => { - reject(new HttpError(500, err.message)) - }) - - }) + return Spells.getOne(id) .catch(err => { + console.log(err) throw err }) } @@ -147,144 +67,9 @@ router.get('/:id/', async (req, res) => { // CREATE ONE ------------------ const addSpell = (s) => { - return new Promise(async (resolve, reject) => { - - // Checks if body exists and if the model fits, and throws errors if it doesn't - if (isEmptyObject(s)) { - reject(new HttpError(403, "Error: Spell cannot be nothing !")) - } else if (!v.validate(s, SpellModel).valid) { - reject(new HttpError(403, "Error: Schema is not valid - " + v.validate(s, SpellModel).errors)) - } else if (isXSSAttempt(s.name) || isXSSAttempt(s.description) || isXSSAttempt(s.cost)) { - reject(new HttpError(403, 'Injection attempt detected, aborting the request.')) - } else { - - Spell.forge({ - 'name': s.name, - 'description': s.description, - 'level': s.level, - }) - - // let query = - // 'INSERT INTO spell (name, description' - - // if (s.level !== null) { query += ', level' } - // if (s.charge !== null) { query += ', charge' } - // if (s.cost !== null) { query += ', cost' } - // if (s.is_ritual !== null) { query += ', is_ritual' } - - // query += `) VALUES (${db.escape(s.name)}, ${db.escape(s.description)}` - - // if (s.level !== null) { query += `, ${s.level}` } - // if (s.charge !== null) { query += `, ${s.charge}` } - // if (s.cost !== null) { query += `, ${db.escape(s.cost)}` } - // if (s.is_ritual !== null) { query += `, ${s.is_ritual}` } - - // query += ')' - - // db.query(query, async (err, result) => { - // if (err) { - // reject(new HttpError(500, 'Database error')) - // } - // console.log(`Inserted "${s.name}" with ID ${result.insertId}, affecting ${result.affectedRows} row(s)`) - - // const new_spell_id = result.insertId - - // let addSchoolsData = () => { - // return new Promise((resolve, reject) => { - // if (s.schools !== null) { - // if (s.schools.length > 0) { - // for (let i = 0; i < s.schools.length; i++) { - // if (!regexInt.test(s.schools[i].id)) { - // reject(new HttpError(403, 'Query error - School ID should be an integer !')) - // } - - // let insert_schools_query = `INSERT INTO spells_schools (id_spell, id_school) VALUES (${new_spell_id}, ${s.schools[i].id})` - - // db.query(insert_schools_query, async (err, result) => { - // if (err) { - // reject(new HttpError(404, 'Database error - No schools matching this ID')) - // } else { - // console.log(`Associated school ID ${s.schools[i].id} to spell ID ${new_spell_id}, affecting ${result.affectedRows} row(s)`) - // resolve() - // } - // }) - // } - // } else { - // resolve() - // } - // } else { - // resolve() - // } - // }) - // } - - // let addVariablesData = () => { - // return new Promise((resolve, reject) => { - // if (s.variables !== null) { - // if (s.variables.length > 0) { - // for (let i = 0; i < s.variables.length; i++) { - // if (!regexInt.test(s.variables[i].id)) { - // reject(new HttpError(403, 'Query error - Variable ID should be an integer !')) - // } - - // let insert_variables_query = `INSERT INTO spells_variables (id_spell, id_variable) VALUES (${new_spell_id}, ${s.variables[i].id})` - // db.query(insert_variables_query, async (err, result) => { - // if (err) { - // reject(new HttpError(404, 'Database error - No variables matching this ID')) - // } else { - // console.log(`Associated variable ID ${s.variables[i].id} to spell ID ${new_spell_id}, affecting ${result.affectedRows} row(s)`) - // resolve() - // } - // }) - // } - // } else { - // resolve() - // } - // } else { - // resolve() - // } - // }) - // } - - // let addIngredientsData = () => { - // return new Promise((resolve, reject) => { - // if (s.ingredients !== null) { - // if (s.ingredients.length > 0) { - // for (let i = 0; i < s.ingredients.length; i++) { - // if (!regexInt.test(s.ingredients[i].id)) { - // reject(new HttpError(403, 'Query error - Ingredient ID should be an integer !')) - // } - - // let insert_ingredients_query = `INSERT INTO spells_ingredients (id_spell, id_ingredient) VALUES (${new_spell_id}, ${s.ingredients[i].id})` - // db.query(insert_ingredients_query, async (err, result) => { - // if (err) { - // reject(new HttpError(404, 'Database error - No ingredients matching this ID')) - // } else { - // console.log(`Associated ingredient ID ${s.ingredients[i].id} to spell ID ${new_spell_id}, affecting ${result.affectedRows} row(s)`) - // resolve() - // } - // }) - // } - // } else { - // resolve() - // } - // } else { - // resolve() - // } - // }) - // } - - // Promise.all([addSchoolsData(), addVariablesData(), addIngredientsData()]) - // .then(() => { - // resolve(getSpell(new_spell_id)) - // }) - // .catch(err => { - // reject(err) - // }) - // }) - } - }).catch(err => { - throw err + return Spells.addOne(s) + .catch(err => { + console.log(err) }) } router.post('/', async (req, res) => { @@ -627,107 +412,4 @@ router.param('id', (req, res, next, id) => { } }) -// SHARED FUNCTIONS ------------------ - -// Builds the associated infos for a given spell object -const buildSpell = async (spell) => { - - // Fetches the spell's schools - let fetchSpellSchoolData = (s) => { - return new Promise((resolve, reject) => { - - if (s == null) { reject(new HttpError(404, "Error: No spell matching this ID"))} - - let query = - "SELECT school.id, school.name " + - "FROM spells_schools AS sc " + - "INNER JOIN school AS school ON sc.id_school = school.id " + - "WHERE sc.id_spell = " + s.id - - db.query(query, (err, result) => { - if (err) { - reject(new HttpError(500, 'Database error')) - } else { - s.schools = result - resolve(s) - } - }) - }) - } - - // Fetches the spell's variables - let fetchSpellVariablesData = (s) => { - return new Promise((resolve, reject) => { - - if (s == null) { reject(new HttpError(404, "Error: No spell matching this ID"))} - - let query = - "SELECT variable.id, variable.description " + - "FROM spells_variables AS sv " + - "INNER JOIN variable AS variable ON sv.id_variable = variable.id " + - "WHERE sv.id_spell = " + s.id - - db.query(query, (err, result) => { - if (err) { - reject(new HttpError(500, 'Database error')) - } else { - s.variables = result - resolve(s) - } - }) - }) - } - - // Fetches the spell's ingredients - let fetchSpellIngredientsData = (s) => { - return new Promise((resolve, reject) => { - - if (s == null) { reject(new HttpError(404, "Error: No spell matching this ID"))} - - let query = - "SELECT ingredient.id, ingredient.name " + - "FROM spells_ingredients AS si " + - "INNER JOIN ingredient AS ingredient ON si.id_ingredient = ingredient.id " + - "WHERE si.id_spell = " + s.id - - db.query(query, (err, result) => { - if (err) { - reject(new HttpError(500, 'Database error')) - } else { - s.ingredients = result - resolve(s) - } - }) - }) - } - - // Builds the spell and returns it - let s = await fetchSpellSchoolData(spell) - .then(s => { return fetchSpellVariablesData(s) }) - .then(s => { return fetchSpellIngredientsData(s) }) - .catch(err => { - throw err - }) - - return s -} - -// Check if object is null -const isEmptyObject = (obj) => { - if (Object.keys(obj).length === 0 && obj.constructor === Object) { - return true - } else { - return false - } -} - -// Check if script injection attempt -const isXSSAttempt = (string) => { - if (regexXSS.test(string)) { - return true - } else { - return false - } -} - module.exports = router diff --git a/routes/users.js b/routes/users.js index 793526b..3c932c1 100644 --- a/routes/users.js +++ b/routes/users.js @@ -1,3 +1,5 @@ +'use strict' + // Router const express = require('express') let router = express.Router() @@ -6,40 +8,18 @@ let router = express.Router() const connection = require('../database/connection') const db = connection.db -// Hashing and passwords -const bcrypt = require('bcrypt') +// Repository +const UserRepository = require('../repositories/user-repository'); +const Users = new UserRepository(); -// Validations const regexInt = RegExp(/^[1-9]\d*$/) -const regexXSS = RegExp(/<[^>]*script/) - -// Model validation -const Validator = require('jsonschema').Validator -const v = new Validator() -const User = require("../models/UserValidation") -v.addSchema(User, "/UserModel") - -// Error handling -const { HttpError } = require('../models/Errors') // ROUTES // GET ALL ------------------ const getUsers = () => { - return new Promise((resolve, reject) => { - - let query = "SELECT DISTINCT * FROM user" - - db.query(query, async (err, result) => { - if (err) { - reject(new HttpError(500, 'Database error')) - } else if (result.length == 0) { - reject(new HttpError(404, 'No users were found')) - } else { - resolve(result) - } - }) - }) + return Users.getAll() .catch(err => { + console.log(err) throw err }) } @@ -62,21 +42,9 @@ router.get('/', async (req, res) => { // GET ONE ------------------ const getUser = (id) => { - return new Promise((resolve, reject) => { - - let query = "SELECT * FROM user WHERE id = " + db.escape(id) - - db.query(query, async (err, result) => { - if (err) { - reject(new HttpError(500, 'Database error')) - } else if (result.length == 0) { - reject(new HttpError(404, 'No User matching this ID')) - } else { - resolve(result) - } - }) - }) + return Users.getOne(id) .catch(err => { + console.log(err) throw err }) } @@ -211,33 +179,11 @@ router.param('id', (req, res, next, id) => { if (regexInt.test(id)) { next() } else { - throw new HttpError(403, 'Provided ID must be an integer and not zero') + new Error } } catch (err) { - res.status(err.code).send(JSON.stringify( - { - "error": err.message, - }) - ) + throw new HttpError(403, 'Provided ID must be an integer and not zero') } }) -// Check if object is null -const isEmptyObject = (obj) => { - if (Object.keys(obj).length === 0 && obj.constructor === Object) { - return true - } else { - return false - } -} - -// Check if script injection attempt -const isXSSAttempt = (string) => { - if (regexXSS.test(string)) { - return true - } else { - return false - } -} - module.exports = router \ No newline at end of file From 8932736e6a8308bd8cc896d8f63055d95d3b0e59 Mon Sep 17 00:00:00 2001 From: Alexis <35.alexis.pele@gmail.com> Date: Sat, 6 Jun 2020 16:38:08 +0200 Subject: [PATCH 5/9] jesus fucking christ finished the update spell thing why are apis like this --- repositories/spell-repository.js | 83 +++++++++++++- routes/spells.js | 186 ++----------------------------- routes/users.js | 3 + 3 files changed, 95 insertions(+), 177 deletions(-) diff --git a/repositories/spell-repository.js b/repositories/spell-repository.js index fdf2c6c..0a5b2df 100644 --- a/repositories/spell-repository.js +++ b/repositories/spell-repository.js @@ -67,7 +67,7 @@ class SpellRepository { return new Promise((resolve, reject) => { this._model.forge() .where({ 'id' : id }) - .fetch({ withRelated: ['schools.meta_schools', 'variables', 'ingredients'] }) + .fetch({ withRelated: ['schools.meta_schools', 'variables', 'ingredients']}) .then(v => { resolve(v.toJSON({ omitPivot: true })) }) @@ -138,6 +138,87 @@ class SpellRepository { }) } + updateOne(id, s) { + return new Promise((resolve, reject) => { + this._model.forge({id: id}) + .fetch({require: true, withRelated: ['schools.meta_schools', 'variables', 'ingredients']}) + .then(v => { + bookshelf.transaction(t => { + return v.save({ + 'name': s.name, + 'description': s.description, + 'level': s.level, + 'charge' : s.charge, + 'cost' : s.cost, + 'is_ritual' : s.is_ritual + }, { + method: 'update', + transacting: t + }) + // Detaches AND attaches pivot tables, dw about it + .tap(spell => { + if (s.schools) { + let schools = spell.related('school'); + return spell.schools().detach(schools, { transacting: t}); + } + return + }) + .tap(spell => { + if (s.variables) { + let variables = spell.related('variable'); + return spell.variables().detach(variables, { transacting: t}); + } + return + }) + .tap(spell => { + if (s.ingredients) { + let ingredients = spell.related('ingredient'); + return spell.ingredients().detach(ingredients, { transacting: t}); + } + }) + .tap(spell => { + return spell + .schools() + .attach(s.schools, { + transacting: t + }); + }) + .tap(spell => { + return spell + .variables() + .attach(s.variables, { + transacting: t + }); + }) + .tap(spell => { + return spell + .ingredients() + .attach(s.ingredients, { + transacting: t + }); + }) + .catch(err => { + console.log(err) + throw err + }) + }) + .then(v => { + return v.load(['schools.meta_schools', 'variables', 'ingredients']) + }) + .then(v => { + resolve(this.getOne(v.id)) + }) + .catch(err => { + console.log(err) + reject(new HttpError(500, "Update error")) + }) + }) + .catch(err => { + reject(new HttpError(404, "Couldn't get spell")) + }) + }) + } + // Check if object is null isEmptyObject = (obj) => { if (Object.keys(obj).length === 0 && obj.constructor === Object) { diff --git a/routes/spells.js b/routes/spells.js index 043b8ab..e2aefde 100644 --- a/routes/spells.js +++ b/routes/spells.js @@ -14,6 +14,9 @@ const Spells = new SpellReposity(); const regexInt = RegExp(/^[1-9]\d*$/) +// Error handling +const { HttpError } = require('../models/Errors') + // ROUTES // GET ALL ------------------ const getSpells = () => { @@ -70,6 +73,7 @@ const addSpell = (s) => { return Spells.addOne(s) .catch(err => { console.log(err) + throw err }) } router.post('/', async (req, res) => { @@ -90,186 +94,15 @@ router.post('/', async (req, res) => { // UPDATE ONE ------------------ -const updateSpell = (s, id) => { - return new Promise( async (resolve, reject) => { - - // Check if spell exists - let old_spell = await getSpell(id) - .catch(err => { - reject(err) - }) - - // Checks if body exists and if the model fits, and throws errors if it doesn't - if (isEmptyObject(s)) { - reject(new HttpError(403, "Error: Spell cannot be nothing !")) - } else if (!v.validate(s, SpellModel).valid) { - reject(new HttpError(403, "Error: Schema is not valid - " + v.validate(s, SpellModel).errors)) - } else if (isXSSAttempt(s.name) || isXSSAttempt(s.description) || isXSSAttempt(s.cost)) { - reject(new HttpError(403, 'Injection attempt detected, aborting the request.')) - } else { - - let updateSchoolsData = () => { - return new Promise((resolve, reject) => { - if (s.schools !== null) { - if (s.schools.length > 0) { - let delete_schools_query = - `DELETE FROM spells_schools WHERE id_spell = ${old_spell.id}` - - db.query(delete_schools_query, async (err, result) => { - if (err) { - reject(new HttpError(500, 'Database error - Spell school deletion failed.')) - } - }) - - for (let i = 0; i < s.schools.length; i++) { - if (!regexInt.test(s.schools[i].id)) { - reject(new HttpError(403, 'Query error - School ID should be an integer !')) - } else {} - - let update_schools_query = `INSERT INTO spells_schools (id_spell, id_school) VALUES (${old_spell.id}, ${s.schools[i].id})` - db.query(update_schools_query, async (err, result) => { - if (err) { - reject(new HttpError(404, 'Database error - No schools matching this ID')) - } else { - console.log(`Updated association school ID ${s.schools[i].id} to spell ID ${old_spell.id}`) - resolve() - } - }) - } - } else { - resolve() - } - } else { - resolve() - } - }) - } - - let updateVariablesData = () => { - return new Promise((resolve, reject) => { - if (s.variables !== null) { - if (s.variables.length > 0) { - let delete_variables_query = - `DELETE FROM spells_variables WHERE id_spell = ${old_spell.id}` - - db.query(delete_variables_query, async (err, result) => { - if (err) { - reject(new HttpError(500, 'Database error - Spell variable deletion failed.')) - } - }) - - for (let i = 0; i < s.variables.length; i++) { - if (!regexInt.test(s.variables[i].id)) { - reject(new HttpError(403, 'Query error - Variable ID should be an integer !')) - } - - let update_variables_query = `INSERT INTO spells_variables (id_spell, id_variable) VALUES (${old_spell.id}, ${s.variables[i].id})` - db.query(update_variables_query, async (err, result) => { - if (err) { - reject(new HttpError(404, 'Database error - No variables matching this ID')) - } else { - console.log(`Updated variable ID "${s.variables[i].id}" to spell ID ${old_spell.id}`) - resolve() - } - }) - } - } else { - resolve() - } - } else { - resolve() - } - }) - } - - let updateIngredientsData = () => { - return new Promise((resolve, reject) => { - if (s.ingredients !== null) { - if (s.ingredients.length > 0) { - let delete_ingredients_query = - `DELETE FROM spells_ingredients WHERE id_spell = ${old_spell.id}` - - db.query(delete_ingredients_query, async (err, result) => { - if (err) { - reject(new HttpError(500, 'Database error - Spell ingredients deletion failed.')) - } - console.log(result) - }) - - // Loops over ingredients query - for (let i = 0; i < s.ingredients.length; i++) { - if (!regexInt.test(s.ingredients[i].id)) { - reject(new HttpError(403, 'Query error - Ingredient ID should be an integer !')) - } - - let update_ingredients_query = `INSERT INTO spells_ingredients (id_spell, id_ingredient) VALUES (${old_spell.id}, ${s.ingredients[i].id})` - db.query(update_ingredients_query, async (err, result) => { - if (err) { - reject(new HttpError(404, 'Database error - No ingredients matching this ID')) - } else { - console.log(`Updated ingredient ID "${s.ingredients[i].id}" to spell ID ${old_spell.id}`) - resolve() - } - }) - } - } else { - resolve() - } - } else { - resolve() - } - }) - } - - let updateSpellData = () => { - return new Promise((resolve, reject) => { - let update_spell_query = - 'UPDATE spell SET ' - - if (s.name !== null) { update_spell_query += `name = "${s.name}" ` } - if (s.description !== null) { update_spell_query += `, description = "${s.description}" ` } - if (s.level !== null) { update_spell_query += `, level = ${s.level} ` } - if (s.charge !== null) { update_spell_query += `, charge = ${s.charge} ` } - if (s.cost !== null) { update_spell_query += `, cost = "${s.cost}" ` } - if (s.is_ritual !== null) { update_spell_query += `, is_ritual = ${s.is_ritual} ` } - - update_spell_query += ` WHERE id = ${db.escape(id)}` - - db.query(update_spell_query, async (err, result) => { - if (err) { - reject(new HttpError(500, 'Database error - Spell update failed')) - } else { - console.log(`Updated "${s.name}" on ID ${old_spell.id}, affecting ${result.affectedRows} row(s)`) - resolve() - } - }) - }) - } - - const sub_promises = [ - updateSchoolsData(), - updateVariablesData(), - updateIngredientsData() - ] - - Promise.all(sub_promises) - .then(() => { - updateSpellData() - }) - .then(() => { - resolve(getSpell(old_spell.id)) - }) - .catch(err => { - reject(err) - }) - } - }) +const updateSpell = (id, s) => { + return Spells.updateOne(id, s) .catch(err => { + console.log(err) throw err }) } router.put('/:id/', async (req, res) => { - updateSpell(req.body, req.params.id) + updateSpell(req.params.id, req.body) .then(v => { res.setHeader('Content-Type', 'application/json;charset=utf-8') res.send(JSON.stringify(v)) @@ -400,9 +233,10 @@ router.param('id', (req, res, next, id) => { if (regexInt.test(id)) { next() } else { - throw new HttpError(403, 'Provided ID must be an integer and not zero') + throw new Error; } } catch (err) { + err = new HttpError(403, 'Provided ID must be an integer and not zero') res.status(err.code).send(JSON.stringify( { "error": err.message, diff --git a/routes/users.js b/routes/users.js index 3c932c1..26ed9d2 100644 --- a/routes/users.js +++ b/routes/users.js @@ -14,6 +14,9 @@ const Users = new UserRepository(); const regexInt = RegExp(/^[1-9]\d*$/) +// Error handling +const { HttpError } = require('../models/Errors') + // ROUTES // GET ALL ------------------ const getUsers = () => { From 3dbb95ab4dec1b3ff493dee818048be470a11a53 Mon Sep 17 00:00:00 2001 From: Alexis <35.alexis.pele@gmail.com> Date: Sat, 6 Jun 2020 17:07:03 +0200 Subject: [PATCH 6/9] finished the delete ORM method for spell --- repositories/spell-repository.js | 24 +++++++++ routes/spells.js | 87 +------------------------------- 2 files changed, 26 insertions(+), 85 deletions(-) diff --git a/repositories/spell-repository.js b/repositories/spell-repository.js index 0a5b2df..eb720d3 100644 --- a/repositories/spell-repository.js +++ b/repositories/spell-repository.js @@ -214,6 +214,30 @@ class SpellRepository { }) }) .catch(err => { + console.log(err) + reject(new HttpError(404, "Couldn't get spell")) + }) + }) + } + + deleteOne(id) { + return new Promise((resolve, reject) => { + this._model.forge() + .where({ 'id' : id }) + .fetch({require: true, withRelated: ['schools.meta_schools', 'variables', 'ingredients']}) + .then(v => { + v.schools().detach(); + v.variables().detach(); + v.ingredients().detach(); + v.destroy(); + }) + .then(() => { + resolve({ + 'message': 'Spell with ID ' + id + ' successfully deleted !' + }) + }) + .catch(err => { + console.log(err) reject(new HttpError(404, "Couldn't get spell")) }) }) diff --git a/routes/spells.js b/routes/spells.js index e2aefde..d7ed52c 100644 --- a/routes/spells.js +++ b/routes/spells.js @@ -120,92 +120,9 @@ router.put('/:id/', async (req, res) => { // DELETE ONE ------------------ const deleteSpell = (id) => { - return new Promise(async (resolve, reject) => { - - // Check if spell exists - let old_spell = await getSpell(id) - .catch(() => { - reject((new HttpError(404, 'No spell matching this ID'))) - }) - - let deleteSchoolsData = () => { - return new Promise((resolve, reject) => { - let delete_schools_query = `DELETE FROM spells_schools WHERE id_spell = ${db.escape(id)}` - db.query(delete_schools_query, async (err, result) => { - if (err) { - reject(new HttpError(500, 'Spell schools deletion failed')) - } else { - console.log(`Deleted schools associated to spell ID ${db.escape(id)}`) - resolve() - } - }) - }) - } - - let deleteVariablesData = () => { - return new Promise((resolve, reject) => { - let delete_variables_query = `DELETE FROM spells_variables WHERE id_spell = ${db.escape(id)}` - db.query(delete_variables_query, async (err, result) => { - if (err) { - console.log(err) - reject(new HttpError(500, 'Spell variables deletion failed')) - } else { - console.log(`Deleted variables associated to spell ID ${db.escape(id)}`) - resolve() - } - }) - }) - } - - let deleteIngredientsData = () => { - return new Promise((resolve, reject) => { - let delete_ingredients_query = `DELETE FROM spells_ingredients WHERE id_spell = ${db.escape(id)}` - db.query(delete_ingredients_query, async (err, result) => { - if (err) { - console.log(err) - reject(new HttpError(500, 'Spell ingredients deletion failed')) - } else { - console.log(`Deleted ingredients associated to spell ID ${db.escape(id)}`) - resolve() - } - }) - }) - } - - let deleteSpellData = () => { - return new Promise((resolve, reject) => { - let delete_spell_query = `DELETE FROM spell WHERE id = ${db.escape(id)}` - db.query(delete_spell_query, async (err, result) => { - if (err) { - console.log(err) - reject(new HttpError(500, 'Spell deletion failed')) - } else { - console.log(`Deleted spell ID ${db.escape(id)}, affecting ${result.affectedRows} rows`) - resolve() - } - }) - }) - } - - const promises = [ - deleteSchoolsData(), - deleteVariablesData(), - deleteIngredientsData() - ] - - Promise.all(promises) - .then(() => { - deleteSpellData() - let response = { - message: `Spell ID ${id} was successfully deleted.` - } - resolve(response) - }) - .catch(err => { - reject(err) - }) - }) + return Spells.deleteOne(id) .catch(err => { + console.log(err) throw err }) } From ce9018f65ef39513582fd560946f9ab00a428e5b Mon Sep 17 00:00:00 2001 From: Alexis <35.alexis.pele@gmail.com> Date: Sat, 6 Jun 2020 23:35:11 +0200 Subject: [PATCH 7/9] - Added some school methods but will need to refactor the model calls --- database/connection.js | 15 +-- models/SchoolValidation.js | 4 +- repositories/school-repository.js | 161 +++++++++++++++++++++++ repositories/spell-repository.js | 143 ++++++++++---------- routes/schools.js | 212 ++++++++++-------------------- routes/spells.js | 2 +- 6 files changed, 309 insertions(+), 228 deletions(-) diff --git a/database/connection.js b/database/connection.js index d890da9..1ffb0ac 100644 --- a/database/connection.js +++ b/database/connection.js @@ -20,17 +20,4 @@ const knex = require('knex')({ }) const bookshelf = require('bookshelf')(knex) -const db = mysql.createConnection({ - host: credentials.host, - user: credentials.user, - password: credentials.password, - database: credentials.database, -}) - -db.on('error', err => { - if (err.errno == 'ECONNRESET') { - console.log("The connection was reset during your request. Please try again later."); - } -}) - -module.exports = { db, bookshelf } \ No newline at end of file +module.exports = { bookshelf } \ No newline at end of file diff --git a/models/SchoolValidation.js b/models/SchoolValidation.js index fe1067c..8b48344 100644 --- a/models/SchoolValidation.js +++ b/models/SchoolValidation.js @@ -4,9 +4,9 @@ const School = { "properties": { "name": { "type": "string" }, "description": { "type": "string" }, - "id_meta_school": { "type": "number" }, + "meta_school_id": { "type": "number" }, }, - "required": ["name", "description", "id_meta_school"] + "required": ["name", "description", "meta_school_id"] } module.exports = School \ No newline at end of file diff --git a/repositories/school-repository.js b/repositories/school-repository.js index f625679..477d45b 100644 --- a/repositories/school-repository.js +++ b/repositories/school-repository.js @@ -7,6 +7,18 @@ const Spells = require('./spell-repository') const MetaSchoolRepository = require('./meta-school-repository') const MetaSchools = new MetaSchoolRepository() +// Model validation +const Validator = require('jsonschema').Validator +const v = new Validator() +const SchoolModel = require("../models/SchoolValidation") +v.addSchema(SchoolModel, "/SchoolModel") + +// Validations +const regexXSS = RegExp(/<[^>]*script/) + +// Error handling +const { HttpError } = require('../models/Errors') + class SchoolRepository { constructor() { @@ -28,6 +40,155 @@ class SchoolRepository { get model() { return this._model } + + getAll() { + return new Promise((resolve, reject) => { + this._model.forge() + .fetchAll({ withRelated: ['meta_schools'] }) + .then(v => { + resolve(v.toJSON({ omitPivot: true })) + }) + .catch(err => { + console.log(err) + reject(new HttpError(500, "Couldn't get schools")) + }) + }) + } + + getOne(id) { + return new Promise((resolve, reject) => { + this._model.forge() + .where({ 'id' : id }) + .fetch({ withRelated: ['meta_schools']}) + .then(v => { + resolve(v.toJSON({ omitPivot: true })) + }) + .catch(err => { + console.log(err) + reject(new HttpError(500, "Couldn't get school")) + }) + }) + } + + addOne(s) { + return new Promise((resolve, reject) => { + // Checks if body exists and if the model fits, and throws errors if it doesn't + if (this.isEmptyObject(s)) { + reject(new HttpError(403, "Error: School cannot be nothing !")) + } else if (!v.validate(s, SchoolModel).valid) { + reject(new HttpError(403, "Error: Schema is not valid - " + v.validate(s, SchoolModel).errors)) + } else if (this.isXSSAttempt(s.name) || this.isXSSAttempt(s.description)) { + reject(new HttpError(403, 'Injection attempt detected, aborting the request.')) + } else { + bookshelf.transaction(t => { + return this._model.forge({ + 'name': s.name, + 'description': s.description, + 'meta_school_id': s.meta_school_id, + }).save(null, { + transacting: t + }) + .catch(err => { + throw err + }) + }) + .then(v => { + return v.load(['meta_schools']) + }) + .then(v => { + resolve(this.getOne(v.id)) + }) + .catch(err => { + console.log(err) + reject(new HttpError(500, "Insert School error")) + }) + } + }) + } + + updateOne(id, s) { + return new Promise((resolve, reject) => { + // Checks if body exists and if the model fits, and throws errors if it doesn't + if (this.isEmptyObject(s)) { + reject(new HttpError(403, "Error: School cannot be nothing !")) + } else if (!v.validate(s, SchoolModel).valid) { + reject(new HttpError(403, "Error: Schema is not valid - " + v.validate(s, SchoolModel).errors)) + } else if (this.isXSSAttempt(s.name) || this.isXSSAttempt(s.description)) { + reject(new HttpError(403, 'Injection attempt detected, aborting the request.')) + } else { + this._model.forge({id: id}) + .fetch({require: true, withRelated: ['meta_schools']}) + .then(v => { + bookshelf.transaction(t => { + return v.save({ + 'name': s.name, + 'description': s.description, + 'meta_school_id': s.meta_school_id + }, { + method: 'update', + transacting: t + }) + .catch(err => { + console.log(err) + throw err + }) + }) + .then(v => { + return v.load(['meta_schools']) + }) + .then(v => { + resolve(this.getOne(v.id)) + }) + .catch(err => { + console.log(err) + reject(new HttpError(500, "Update School error")) + }) + }) + .catch(err => { + console.log(err) + reject(new HttpError(404, "Couldn't get school")) + }) + } + }) + } + + deleteOne(id) { + return new Promise((resolve, reject) => { + this._model.forge() + .where({ 'id' : id }) + .fetch({require: true, withRelated: ['spells', 'meta_schools']}) + .then(v => { + + }) + .then(() => { + resolve({ + 'message': 'School with ID ' + id + ' successfully deleted !' + }) + }) + .catch(err => { + console.log(err) + reject(new HttpError(404, "Couldn't get school")) + }) + }) + } + + // Check if object is null + isEmptyObject = (obj) => { + if (Object.keys(obj).length === 0 && obj.constructor === Object) { + return true + } else { + return false + } + } + + // Check if script injection attempt + isXSSAttempt = (string) => { + if (regexXSS.test(string)) { + return true + } else { + return false + } + } } module.exports = SchoolRepository \ No newline at end of file diff --git a/repositories/spell-repository.js b/repositories/spell-repository.js index eb720d3..b49ac45 100644 --- a/repositories/spell-repository.js +++ b/repositories/spell-repository.js @@ -132,7 +132,7 @@ class SpellRepository { }) .catch(err => { console.log(err) - reject(new HttpError(500, "Insert error")) + reject(new HttpError(500, "Insert Spell error")) }) } }) @@ -140,83 +140,92 @@ class SpellRepository { updateOne(id, s) { return new Promise((resolve, reject) => { - this._model.forge({id: id}) - .fetch({require: true, withRelated: ['schools.meta_schools', 'variables', 'ingredients']}) - .then(v => { - bookshelf.transaction(t => { - return v.save({ - 'name': s.name, - 'description': s.description, - 'level': s.level, - 'charge' : s.charge, - 'cost' : s.cost, - 'is_ritual' : s.is_ritual - }, { - method: 'update', - transacting: t - }) - // Detaches AND attaches pivot tables, dw about it - .tap(spell => { - if (s.schools) { - let schools = spell.related('school'); - return spell.schools().detach(schools, { transacting: t}); - } - return - }) - .tap(spell => { - if (s.variables) { - let variables = spell.related('variable'); - return spell.variables().detach(variables, { transacting: t}); - } - return - }) - .tap(spell => { - if (s.ingredients) { - let ingredients = spell.related('ingredient'); - return spell.ingredients().detach(ingredients, { transacting: t}); - } - }) - .tap(spell => { - return spell - .schools() - .attach(s.schools, { + // Checks if body exists and if the model fits, and throws errors if it doesn't + if (this.isEmptyObject(s)) { + reject(new HttpError(403, "Error: Spell cannot be nothing !")) + } else if (!v.validate(s, SpellModel).valid) { + reject(new HttpError(403, "Error: Schema is not valid - " + v.validate(s, SpellModel).errors)) + } else if (this.isXSSAttempt(s.name) || this.isXSSAttempt(s.description) || this.isXSSAttempt(s.cost)) { + reject(new HttpError(403, 'Injection attempt detected, aborting the request.')) + } else { + this._model.forge({id: id}) + .fetch({require: true, withRelated: ['schools.meta_schools', 'variables', 'ingredients']}) + .then(v => { + bookshelf.transaction(t => { + return v.save({ + 'name': s.name, + 'description': s.description, + 'level': s.level, + 'charge' : s.charge, + 'cost' : s.cost, + 'is_ritual' : s.is_ritual + }, { + method: 'update', transacting: t - }); + }) + // Detaches AND attaches pivot tables, dw about it + .tap(spell => { + if (s.schools) { + let schools = spell.related('school'); + return spell.schools().detach(schools, { transacting: t}); + } + return + }) + .tap(spell => { + if (s.variables) { + let variables = spell.related('variable'); + return spell.variables().detach(variables, { transacting: t}); + } + return + }) + .tap(spell => { + if (s.ingredients) { + let ingredients = spell.related('ingredient'); + return spell.ingredients().detach(ingredients, { transacting: t}); + } + }) + .tap(spell => { + return spell + .schools() + .attach(s.schools, { + transacting: t + }); + }) + .tap(spell => { + return spell + .variables() + .attach(s.variables, { + transacting: t + }); + }) + .tap(spell => { + return spell + .ingredients() + .attach(s.ingredients, { + transacting: t + }); + }) + .catch(err => { + console.log(err) + throw err + }) }) - .tap(spell => { - return spell - .variables() - .attach(s.variables, { - transacting: t - }); + .then(v => { + return v.load(['schools.meta_schools', 'variables', 'ingredients']) }) - .tap(spell => { - return spell - .ingredients() - .attach(s.ingredients, { - transacting: t - }); + .then(v => { + resolve(this.getOne(v.id)) }) .catch(err => { console.log(err) - throw err + reject(new HttpError(500, "Update Spell error")) }) }) - .then(v => { - return v.load(['schools.meta_schools', 'variables', 'ingredients']) - }) - .then(v => { - resolve(this.getOne(v.id)) - }) .catch(err => { console.log(err) - reject(new HttpError(500, "Update error")) + reject(new HttpError(404, "Couldn't get spell")) }) - }) - .catch(err => { - console.log(err) - reject(new HttpError(404, "Couldn't get spell")) - }) + } }) } diff --git a/routes/schools.js b/routes/schools.js index daceed1..502e303 100644 --- a/routes/schools.js +++ b/routes/schools.js @@ -8,46 +8,21 @@ let router = express.Router() const connection = require('../database/connection') const db = connection.db -// Validations -const regexInt = RegExp(/^[1-9]\d*$/) -const regexXSS = RegExp(/<[^>]*script/) +// Repository +const SchoolRepository = require('../repositories/school-repository'); +const Schools = new SchoolRepository(); -// Model validation -const Validator = require('jsonschema').Validator -const v = new Validator() -const School = require("../models/SchoolValidation") -v.addSchema(School, "/SchoolModel") +const regexInt = RegExp(/^[1-9]\d*$/) // Error handling const { HttpError } = require('../models/Errors') - // ROUTES // GET ALL ------------------ const getSchools = () => { - return new Promise((resolve, reject) => { - - let query = "SELECT DISTINCT * FROM school" - - db.query(query, async (err, result) => { - if (err) { - reject(new HttpError(500, 'Database error')) - } else if (result.length == 0) { - reject(new HttpError(404, 'No schools were found')) - } - - // Loops over the results to fetch the associated tables - for (let i = 0; i < result.length; i++) { - try { - result[i] = await buildSchool(result[i]) - } catch (err) { - reject(err) - } - } - resolve(result) - }) - }) + return Schools.getAll() .catch(err => { + console.log(err) throw err }) } @@ -70,23 +45,9 @@ router.get('/', async (req, res) => { // GET ONE ------------------ const getSchool = (id) => { - return new Promise((resolve, reject) => { - - let query = "SELECT * FROM school WHERE id = " + db.escape(id) - - db.query(query, async (err, result) => { - if (err) { - reject(new HttpError(500, 'Database error')) - } - try { - result = buildSchool(result[0]) - resolve(result) - } catch (err) { - reject(err) - } - }) - }) + return Schools.getOne(id) .catch(err => { + console.log(err) throw err }) } @@ -109,41 +70,9 @@ router.get('/:id/', async (req, res) => { // CREATE ONE ------------------ const addSchool = (s) => { - return new Promise(async (resolve, reject) => { - - // Checks if body exists and if the model fits, and throws errors if it doesn't - if (isEmptyObject(s)) { - reject(new HttpError(403, "Error: School cannot be nothing !")) - } else if (!v.validate(s, School).valid) { - reject(new HttpError(403, "Error: Schema is not valid - " + v.validate(s, School).errors)) - } else if (isXSSAttempt(s.name) || isXSSAttempt(s.description)) { - reject(new HttpError(403, 'Injection attempt detected, aborting the request.')) - } else { - let query = `INSERT INTO school (name, description, id_meta_school) VALUES (${db.escape(s.name)}, ${db.escape(s.description)}, ${db.escape(s.id_meta_school)})` - - db.query(query, (err, result) => { - if (err) { - if (err.errno == 1452) { - reject(new HttpError(404, "Error: No meta school matching this ID")) - } else { - reject(new HttpError(500, 'Database error')) - } - } else { - console.log(`Inserted "${s.name}" with ID ${result.insertId}, affecting ${result.affectedRows} row(s)`) - - const new_school_id = result.insertId - - getSchool(new_school_id) - .then(v => { - resolve(v) - }) - .catch(err => { - reject(err) - }) - } - }) - } - }).catch(err => { + return Schools.addOne(s) + .catch(err => { + console.log(err) throw err }) } @@ -163,79 +92,74 @@ router.post('/', async (req, res) => { }) }) -// Param validation for single school -// (check if id is int) (could be refactored) -router.param('id', (req, res, next, id) => { - const regex = RegExp(/^[1-9]\d*$/); - try { - if (regex.test(id)) { - next() - } else { - throw new HttpError(403, 'Provided ID must be an integer') - } - } catch (err) { +// UPDATE ONE ------------------ +const updateSchool = (id, s) => { + return Schools.updateOne(id, s) + .catch(err => { + console.log(err) + throw err + }) +} +router.put('/:id/', async (req, res) => { + updateSchool(req.params.id, req.body) + .then(v => { + res.setHeader('Content-Type', 'application/json;charset=utf-8') + res.send(JSON.stringify(v)) + }) + .catch(err => { res.status(err.code).send(JSON.stringify( { "error": err.message, "code": err.code }) ) - } + }) }) -// SHARED FUNCTIONS ------------------ -// Builds the associated infos for a given school object -const buildSchool = async (school) => { - - // Fetches the school's parent school - let fetchMetaSchoolData = (s) => { - return new Promise((resolve, reject) => { - - if (s == null) { reject(new HttpError(404, "Error: No school matching this ID"))} - - let query = - "SELECT ms.id, ms.name " + - "FROM meta_school AS ms " + - "WHERE ms.id = " + s.id_meta_school - - - db.query(query, (err, result) => { - if (err) { - reject(new HttpError(500, 'Database error')) - } else { - delete s.id_meta_school - s.meta_school = result - resolve(s) - } - }) - }) - } - - let s = fetchMetaSchoolData(school) +// DELETE ONE ------------------ +const deleteSchool = (id) => { + return Schools.deleteOne(id) .catch(err => { + console.log(err) throw err }) - - return s } +router.delete('/:id/', async (req, res) => { + deleteSchool(req.params.id) + .then(v => { + res.setHeader('Content-Type', 'application/json;charset=utf-8') + res.send(JSON.stringify(v)) + }) + .catch(err => { + res.status(err.code).send(JSON.stringify( + { + "error": err.message, + "code": err.code + }) + ) + }) +}) -// Check if object is null -const isEmptyObject = (obj) => { - if (Object.keys(obj).length === 0 && obj.constructor === Object) { - return true - } else { - return false - } -} - -// Check if script injection attempt -const isXSSAttempt = (string) => { - if (regexXSS.test(string)) { - return true - } else { - return false - } -} - -module.exports = router \ No newline at end of file +// Param validation for ID + // (check if id is int) (could be refactored) + router.param('id', (req, res, next, id) => { + try { + if (regexInt.test(id)) { + next() + } else { + throw new Error; + } + } catch (err) { + err = new HttpError(403, 'Provided ID must be an integer and not zero') + res.status(err.code).send(JSON.stringify( + { + "error": err.message, + "code": err.code + }) + ) + } + }) + + module.exports = router + \ No newline at end of file diff --git a/routes/spells.js b/routes/spells.js index d7ed52c..f032da8 100644 --- a/routes/spells.js +++ b/routes/spells.js @@ -143,7 +143,7 @@ router.delete('/:id/', async (req, res) => { }) -// Param validation for single spell +// Param validation for ID // (check if id is int) (could be refactored) router.param('id', (req, res, next, id) => { try { From 48987b834952361ac518c72e8ffba448144e572c Mon Sep 17 00:00:00 2001 From: Alexis <35.alexis.pele@gmail.com> Date: Mon, 8 Jun 2020 19:55:36 +0200 Subject: [PATCH 8/9] - Added A TON of stuff, separated everything neatly and cleaned most models --- database/{connection.js => bookshelf.js} | 0 index.js | 7 +- models/ingredient-model.js | 13 +++ models/meta-school-model.js | 13 +++ models/school-model.js | 17 ++++ models/spell-model.js | 21 +++++ models/user-model.js | 8 ++ models/variable-model.js | 13 +++ repositories/ingredient-repository.js | 52 +++++++++--- repositories/meta-school-repository.js | 67 ++++++++++++--- repositories/school-repository.js | 52 ++++-------- repositories/spell-repository.js | 65 ++++----------- repositories/user-repository.js | 23 ++---- repositories/variable-repository.js | 47 ++++++++--- routes/index.js | 10 ++- routes/ingredients.js | 90 ++++++++++++++++++++ routes/meta_schools.js | 91 +++++++++++++++++++++ routes/schools.js | 43 +++++----- routes/spells.js | 4 +- routes/users.js | 4 +- routes/variables.js | 91 +++++++++++++++++++++ {models => validations}/Errors.js | 0 validations/IngredientValidation.js | 10 +++ validations/MetaSchoolValidation.js | 12 +++ {models => validations}/SchoolValidation.js | 2 +- {models => validations}/SpellValidation.js | 6 +- {models => validations}/UserValidation.js | 2 +- validations/VariableValidation.js | 10 +++ 28 files changed, 602 insertions(+), 171 deletions(-) rename database/{connection.js => bookshelf.js} (100%) create mode 100644 models/ingredient-model.js create mode 100644 models/meta-school-model.js create mode 100644 models/school-model.js create mode 100644 models/spell-model.js create mode 100644 models/user-model.js create mode 100644 models/variable-model.js create mode 100644 routes/ingredients.js create mode 100644 routes/meta_schools.js create mode 100644 routes/variables.js rename {models => validations}/Errors.js (100%) create mode 100644 validations/IngredientValidation.js create mode 100644 validations/MetaSchoolValidation.js rename {models => validations}/SchoolValidation.js (90%) rename {models => validations}/SpellValidation.js (92%) rename {models => validations}/UserValidation.js (90%) create mode 100644 validations/VariableValidation.js diff --git a/database/connection.js b/database/bookshelf.js similarity index 100% rename from database/connection.js rename to database/bookshelf.js diff --git a/index.js b/index.js index a6b9cc1..4ae5ffd 100644 --- a/index.js +++ b/index.js @@ -8,7 +8,7 @@ const morgan = require('morgan') const cors = require('cors') // module to format the json response // Creates instances of database connections -const connection = require('./database/connection') +const connection = require('./database/bookshelf') const db = connection.db // CONSTANTS @@ -30,4 +30,7 @@ const server = app.listen( port, () => {console.log(`App listening on port ${por // Routing app.use('/api/spells', routes.spells) app.use('/api/schools', routes.schools) -app.use('/api/users', routes.users) \ No newline at end of file +app.use('/api/meta_schools', routes.meta_schools) +app.use('/api/variables', routes.variables) +app.use('/api/ingredients', routes.ingredients) +app.use('/api/users', routes.users) diff --git a/models/ingredient-model.js b/models/ingredient-model.js new file mode 100644 index 0000000..cdb8ded --- /dev/null +++ b/models/ingredient-model.js @@ -0,0 +1,13 @@ +'use strict' +const bookshelf = require('../database/bookshelf').bookshelf + +require('./spell-model') + +let Ingredient = bookshelf.Model.extend({ + tableName: 'ingredient', + spells() { + return this.belongsToMany( 'Spell', 'spell_ingredient') + } +}) + +module.exports = bookshelf.model('Ingredient', Ingredient) \ No newline at end of file diff --git a/models/meta-school-model.js b/models/meta-school-model.js new file mode 100644 index 0000000..e52ea7e --- /dev/null +++ b/models/meta-school-model.js @@ -0,0 +1,13 @@ +'use strict' +const bookshelf = require('../database/bookshelf').bookshelf + +require('./school-model') + +let MetaSchool = bookshelf.Model.extend({ + tableName: 'meta_school', + schools() { + return this.hasMany( 'School' ) + } +}) + +module.exports = bookshelf.model('MetaSchool', MetaSchool) \ No newline at end of file diff --git a/models/school-model.js b/models/school-model.js new file mode 100644 index 0000000..2d3ef63 --- /dev/null +++ b/models/school-model.js @@ -0,0 +1,17 @@ +'use strict' +const bookshelf = require('../database/bookshelf').bookshelf + +require('./spell-model') +require('./meta-school-model') + +let School = bookshelf.Model.extend({ + tableName: 'school', + spells() { + return this.belongsToMany( 'Spell', 'spell_school') + }, + meta_schools() { + return this.belongsTo( 'MetaSchool', 'meta_school_id') + } +}) + +module.exports = bookshelf.model('School', School) \ No newline at end of file diff --git a/models/spell-model.js b/models/spell-model.js new file mode 100644 index 0000000..96100a5 --- /dev/null +++ b/models/spell-model.js @@ -0,0 +1,21 @@ +'use strict' +const bookshelf = require('../database/bookshelf').bookshelf + +require('./school-model') +require('./variable-model') +require('./ingredient-model') + +let Spell = bookshelf.Model.extend({ + tableName: 'spell', + schools() { + return this.belongsToMany( 'School', 'spell_school' ) + }, + variables() { + return this.belongsToMany( 'Variable', 'spell_variable' ) + }, + ingredients() { + return this.belongsToMany( 'Ingredient', 'spell_ingredient' ) + } +}) + +module.exports = bookshelf.model('Spell', Spell) \ No newline at end of file diff --git a/models/user-model.js b/models/user-model.js new file mode 100644 index 0000000..60b5c64 --- /dev/null +++ b/models/user-model.js @@ -0,0 +1,8 @@ +'use strict' +const bookshelf = require('../database/bookshelf').bookshelf + +let User = bookshelf.Model.extend({ + tableName: 'user', +}) + +module.exports = bookshelf.model('User', User) \ No newline at end of file diff --git a/models/variable-model.js b/models/variable-model.js new file mode 100644 index 0000000..ba020c9 --- /dev/null +++ b/models/variable-model.js @@ -0,0 +1,13 @@ +'use strict' +const bookshelf = require('../database/bookshelf').bookshelf + +require('./spell-model') + +let Variable = bookshelf.Model.extend({ + tableName: 'variable', + spells() { + return this.belongsToMany( 'Spell', 'spell_variable') + } +}) + +module.exports = bookshelf.model('Variable', Variable) \ No newline at end of file diff --git a/repositories/ingredient-repository.js b/repositories/ingredient-repository.js index 285d539..a24a30f 100644 --- a/repositories/ingredient-repository.js +++ b/repositories/ingredient-repository.js @@ -1,27 +1,55 @@ 'use strict' // Bookshelf -const bookshelf = require('../database/connection').bookshelf +const bookshelf = require('../database/bookshelf').bookshelf +const model = require('../models/ingredient-model') -const Spells = require('./spell-repository') +// Model validation +const Validator = require('jsonschema').Validator +const v = new Validator() +const IngredientValidation = require("../validations/IngredientValidation") +v.addSchema(IngredientValidation, "/IngredientValidation") + +// Validations +const regexXSS = RegExp(/<[^>]*script/) + +// Error handling +const { HttpError } = require('../validations/Errors') class IngredientRepository { constructor() { - this.model = bookshelf.Model.extend({ - tableName: 'ingredient', - spells() { - return this.belongsToMany( Spells._model, 'spell_ingredient', 'ingredient_id', 'spell_id') - } + } + + getAll() { + return new Promise((resolve, reject) => { + model.forge() + .fetchAll({ withRelated: ['spells'] }) + .then(v => { + resolve(v.toJSON({ omitPivot: true })) + }) + .catch(err => { + console.log(err) + reject(new HttpError(500, "Couldn't get ingredients")) + }) }) } - set model(model) { - this._model = model + + getOne(id) { + return new Promise((resolve, reject) => { + model.forge() + .where({ 'id' : id }) + .fetch({ withRelated: ['spells']}) + .then(v => { + resolve(v.toJSON({ omitPivot: true })) + }) + .catch(err => { + console.log(err) + reject(new HttpError(500, "Couldn't get ingredient")) + }) + }) } - get model() { - return this._model - } } module.exports = IngredientRepository \ No newline at end of file diff --git a/repositories/meta-school-repository.js b/repositories/meta-school-repository.js index c8f8960..f2eb7bc 100644 --- a/repositories/meta-school-repository.js +++ b/repositories/meta-school-repository.js @@ -1,27 +1,72 @@ 'use strict' // Bookshelf -const bookshelf = require('../database/connection').bookshelf +const bookshelf = require('../database/bookshelf').bookshelf +const model = require('../models/meta-school-model') -const Schools = require('./school-repository') +// Model validation +const Validator = require('jsonschema').Validator +const v = new Validator() +const MetaSchoolValidation = require("../validations/MetaSchoolValidation") +v.addSchema(MetaSchoolValidation, "/MetaSchoolValidation") + +// Validations +const regexXSS = RegExp(/<[^>]*script/) + +// Error handling +const { HttpError } = require('../validations/Errors') class MetaSchoolRepository { constructor() { - this.model = bookshelf.Model.extend({ - tableName: 'meta_school', - schools() { - return this.hasMany( Schools._model ) - } + } + + getAll() { + return new Promise((resolve, reject) => { + model.forge() + .fetchAll({ withRelated: ['schools'] }) + .then(v => { + resolve(v.toJSON({ omitPivot: true })) + }) + .catch(err => { + console.log(err) + reject(new HttpError(500, "Couldn't get meta schools")) + }) }) } - set model(model) { - this._model = model + getOne(id) { + return new Promise((resolve, reject) => { + model.forge() + .where({ 'id' : id }) + .fetch({ withRelated: ['schools']}) + .then(v => { + resolve(v.toJSON({ omitPivot: true })) + }) + .catch(err => { + console.log(err) + reject(new HttpError(500, "Couldn't get meta school")) + }) + }) } - get model() { - return this._model + // Check if object is null + isEmptyObject = (obj) => { + if (Object.keys(obj).length === 0 && obj.constructor === Object) { + return true + } else { + return false + } } + + // Check if script injection attempt + isXSSAttempt = (string) => { + if (regexXSS.test(string)) { + return true + } else { + return false + } + } + } module.exports = MetaSchoolRepository \ No newline at end of file diff --git a/repositories/school-repository.js b/repositories/school-repository.js index 477d45b..2ffa311 100644 --- a/repositories/school-repository.js +++ b/repositories/school-repository.js @@ -1,49 +1,28 @@ 'use strict' // Bookshelf -const bookshelf = require('../database/connection').bookshelf - -const Spells = require('./spell-repository') - -const MetaSchoolRepository = require('./meta-school-repository') -const MetaSchools = new MetaSchoolRepository() +const bookshelf = require('../database/bookshelf').bookshelf +const model = require('../models/school-model') // Model validation const Validator = require('jsonschema').Validator const v = new Validator() -const SchoolModel = require("../models/SchoolValidation") -v.addSchema(SchoolModel, "/SchoolModel") +const SchoolValidation = require("../validations/SchoolValidation") +v.addSchema(SchoolValidation, "/SchoolValidation") // Validations const regexXSS = RegExp(/<[^>]*script/) // Error handling -const { HttpError } = require('../models/Errors') +const { HttpError } = require('../validations/Errors') class SchoolRepository { constructor() { - this.model = bookshelf.Model.extend({ - tableName: 'school', - spells() { - return this.belongsToMany( Spells._model, 'spell_school', 'school_id', 'spell_id') - }, - meta_schools() { - return this.belongsTo( MetaSchools._model, 'meta_school_id') - } - }) - } - - set model(model) { - this._model = model - } - - get model() { - return this._model } getAll() { return new Promise((resolve, reject) => { - this._model.forge() + model.forge() .fetchAll({ withRelated: ['meta_schools'] }) .then(v => { resolve(v.toJSON({ omitPivot: true })) @@ -57,7 +36,7 @@ class SchoolRepository { getOne(id) { return new Promise((resolve, reject) => { - this._model.forge() + model.forge() .where({ 'id' : id }) .fetch({ withRelated: ['meta_schools']}) .then(v => { @@ -75,13 +54,13 @@ class SchoolRepository { // Checks if body exists and if the model fits, and throws errors if it doesn't if (this.isEmptyObject(s)) { reject(new HttpError(403, "Error: School cannot be nothing !")) - } else if (!v.validate(s, SchoolModel).valid) { - reject(new HttpError(403, "Error: Schema is not valid - " + v.validate(s, SchoolModel).errors)) + } else if (!v.validate(s, SchoolValidation).valid) { + reject(new HttpError(403, "Error: Schema is not valid - " + v.validate(s, SchoolValidation).errors)) } else if (this.isXSSAttempt(s.name) || this.isXSSAttempt(s.description)) { reject(new HttpError(403, 'Injection attempt detected, aborting the request.')) } else { bookshelf.transaction(t => { - return this._model.forge({ + return model.forge({ 'name': s.name, 'description': s.description, 'meta_school_id': s.meta_school_id, @@ -111,12 +90,12 @@ class SchoolRepository { // Checks if body exists and if the model fits, and throws errors if it doesn't if (this.isEmptyObject(s)) { reject(new HttpError(403, "Error: School cannot be nothing !")) - } else if (!v.validate(s, SchoolModel).valid) { - reject(new HttpError(403, "Error: Schema is not valid - " + v.validate(s, SchoolModel).errors)) + } else if (!v.validate(s, SchoolValidation).valid) { + reject(new HttpError(403, "Error: Schema is not valid - " + v.validate(s, SchoolValidation).errors)) } else if (this.isXSSAttempt(s.name) || this.isXSSAttempt(s.description)) { reject(new HttpError(403, 'Injection attempt detected, aborting the request.')) } else { - this._model.forge({id: id}) + model.forge({id: id}) .fetch({require: true, withRelated: ['meta_schools']}) .then(v => { bookshelf.transaction(t => { @@ -154,11 +133,12 @@ class SchoolRepository { deleteOne(id) { return new Promise((resolve, reject) => { - this._model.forge() + model.forge() .where({ 'id' : id }) .fetch({require: true, withRelated: ['spells', 'meta_schools']}) .then(v => { - + v.spells().detach() + v.destroy() }) .then(() => { resolve({ diff --git a/repositories/spell-repository.js b/repositories/spell-repository.js index b49ac45..cc14d8f 100644 --- a/repositories/spell-repository.js +++ b/repositories/spell-repository.js @@ -1,57 +1,28 @@ 'use strict' - // Bookshelf -const bookshelf = require('../database/connection').bookshelf - -const SchoolRepository = require('./school-repository') -const Schools = new SchoolRepository() - -const IngredientRepository = require('./ingredient-repository') -const Ingredients = new IngredientRepository() - -const VariableRepository = require('./variable-repository') -const Variables = new VariableRepository() +const bookshelf = require('../database/bookshelf').bookshelf +const model = require('../models/spell-model') // Model validation const Validator = require('jsonschema').Validator const v = new Validator() -const SpellModel = require("../models/SpellValidation") -v.addSchema(SpellModel, "/SpellModel") +const SpellValidation = require("../validations/SpellValidation") +v.addSchema(SpellValidation, "/SpellValidation") // Validations const regexXSS = RegExp(/<[^>]*script/) // Error handling -const { HttpError } = require('../models/Errors') +const { HttpError } = require('../validations/Errors') class SpellRepository { constructor() { - this.model = bookshelf.Model.extend({ - tableName: 'spell', - schools() { - return this.belongsToMany( Schools._model, 'spell_school', 'spell_id', 'school_id' ) - }, - variables() { - return this.belongsToMany( Variables._model, 'spell_variable', 'spell_id', 'variable_id' ) - }, - ingredients() { - return this.belongsToMany( Ingredients._model, 'spell_ingredient', 'spell_id', 'ingredient_id' ) - } - }) - } - - set model(model) { - this._model = model - } - - get model() { - return this._model } getAll() { return new Promise((resolve, reject) => { - this._model.forge() + model.forge() .fetchAll({ withRelated: ['schools.meta_schools', 'variables', 'ingredients'] }) .then(v => { resolve(v.toJSON({ omitPivot: true })) @@ -65,7 +36,7 @@ class SpellRepository { getOne(id) { return new Promise((resolve, reject) => { - this._model.forge() + model.forge() .where({ 'id' : id }) .fetch({ withRelated: ['schools.meta_schools', 'variables', 'ingredients']}) .then(v => { @@ -83,13 +54,13 @@ class SpellRepository { // Checks if body exists and if the model fits, and throws errors if it doesn't if (this.isEmptyObject(s)) { reject(new HttpError(403, "Error: Spell cannot be nothing !")) - } else if (!v.validate(s, SpellModel).valid) { - reject(new HttpError(403, "Error: Schema is not valid - " + v.validate(s, SpellModel).errors)) + } else if (!v.validate(s, SpellValidation).valid) { + reject(new HttpError(403, "Error: Schema is not valid - " + v.validate(s, SpellValidation).errors)) } else if (this.isXSSAttempt(s.name) || this.isXSSAttempt(s.description) || this.isXSSAttempt(s.cost)) { reject(new HttpError(403, 'Injection attempt detected, aborting the request.')) } else { bookshelf.transaction(t => { - return this._model.forge({ + return model.forge({ 'name': s.name, 'description': s.description, 'level': s.level, @@ -143,12 +114,12 @@ class SpellRepository { // Checks if body exists and if the model fits, and throws errors if it doesn't if (this.isEmptyObject(s)) { reject(new HttpError(403, "Error: Spell cannot be nothing !")) - } else if (!v.validate(s, SpellModel).valid) { - reject(new HttpError(403, "Error: Schema is not valid - " + v.validate(s, SpellModel).errors)) + } else if (!v.validate(s, SpellValidation).valid) { + reject(new HttpError(403, "Error: Schema is not valid - " + v.validate(s, SpellValidation).errors)) } else if (this.isXSSAttempt(s.name) || this.isXSSAttempt(s.description) || this.isXSSAttempt(s.cost)) { reject(new HttpError(403, 'Injection attempt detected, aborting the request.')) } else { - this._model.forge({id: id}) + model.forge({id: id}) .fetch({require: true, withRelated: ['schools.meta_schools', 'variables', 'ingredients']}) .then(v => { bookshelf.transaction(t => { @@ -231,14 +202,14 @@ class SpellRepository { deleteOne(id) { return new Promise((resolve, reject) => { - this._model.forge() + model.forge() .where({ 'id' : id }) .fetch({require: true, withRelated: ['schools.meta_schools', 'variables', 'ingredients']}) .then(v => { - v.schools().detach(); - v.variables().detach(); - v.ingredients().detach(); - v.destroy(); + v.schools().detach() + v.variables().detach() + v.ingredients().detach() + v.destroy() }) .then(() => { resolve({ diff --git a/repositories/user-repository.js b/repositories/user-repository.js index b6d1abc..292e5fa 100644 --- a/repositories/user-repository.js +++ b/repositories/user-repository.js @@ -1,7 +1,7 @@ 'use strict' - // Bookshelf -const bookshelf = require('../database/connection').bookshelf +const bookshelf = require('../database/bookshelf').bookshelf +const model = require('../models/user-model') // Hashing and passwords const bcrypt = require('bcrypt') @@ -9,29 +9,18 @@ const bcrypt = require('bcrypt') // Model validation const Validator = require('jsonschema').Validator const v = new Validator() -const UserModel = require("../models/UserValidation") -v.addSchema(UserModel, "/UserModel") +const UserValidation = require("../validations/UserValidation") +v.addSchema(UserValidation, "/UserValidation") // Validations -const regexInt = RegExp(/^[1-9]\d*$/) const regexXSS = RegExp(/<[^>]*script/) // Error handling -const { HttpError } = require('../models/Errors') +const { HttpError } = require('../validations/Errors') class UserRepository { - constructor() { - this.model = bookshelf.Model.extend({ - tableName: 'user', - }) - } - - set model(model) { - this._model = model - } - get model() { - return this._model + constructor() { } getAll() { diff --git a/repositories/variable-repository.js b/repositories/variable-repository.js index 2be3ca1..549f1ca 100644 --- a/repositories/variable-repository.js +++ b/repositories/variable-repository.js @@ -1,26 +1,47 @@ 'use strict' // Bookshelf -const bookshelf = require('../database/connection').bookshelf +const bookshelf = require('../database/bookshelf').bookshelf +const model = require('../models/variable-model') -const Spells = require('./spell-repository') +// Model validation +const Validator = require('jsonschema').Validator +const v = new Validator() +const VariableValidation = require("../validations/VariableValidation") +v.addSchema(VariableValidation, "/VariableValidation") class VariableRepository { constructor() { - this.model = bookshelf.Model.extend({ - tableName: 'variable', - spells() { - return this.belongsToMany( Spells._model, 'spell_variable', 'variable_id', 'spell_id') - } + } + + getAll() { + return new Promise((resolve, reject) => { + model.forge() + .fetchAll({ withRelated: ['spells'] }) + .then(v => { + resolve(v.toJSON({ omitPivot: true })) + }) + .catch(err => { + console.log(err) + reject(new HttpError(500, "Couldn't get variables")) + }) }) } - set model(model) { - this._model = model - } - - get model() { - return this._model + + getOne(id) { + return new Promise((resolve, reject) => { + model.forge() + .where({ 'id' : id }) + .fetch({ withRelated: ['spells']}) + .then(v => { + resolve(v.toJSON({ omitPivot: true })) + }) + .catch(err => { + console.log(err) + reject(new HttpError(500, "Couldn't get variable")) + }) + }) } } diff --git a/routes/index.js b/routes/index.js index 021e2b2..f2a3aa5 100644 --- a/routes/index.js +++ b/routes/index.js @@ -1,9 +1,15 @@ -const users = require('./users') const spells = require('./spells') const schools = require('./schools') +const meta_schools = require('./meta_schools') +const variables = require('./variables') +const ingredients = require('./ingredients') +const users = require('./users') module.exports = { spells, schools, - users + meta_schools, + ingredients, + variables, + users, } \ No newline at end of file diff --git a/routes/ingredients.js b/routes/ingredients.js new file mode 100644 index 0000000..ea5c36d --- /dev/null +++ b/routes/ingredients.js @@ -0,0 +1,90 @@ +'use strict' + +// Router +const express = require('express') +let router = express.Router() + +// Connection +const connection = require('../database/bookshelf') +const db = connection.db + +// Repository +const IngredientRepository = require('../repositories/ingredient-repository'); +const Ingredients = new IngredientRepository(); + +const regexInt = RegExp(/^[1-9]\d*$/) + +// Error handling +const { HttpError } = require('../validations/Errors') + +// ROUTES +// GET ALL ------------------ +const getIngredients = () => { + return Ingredients.getAll() + .catch(err => { + console.log(err) + throw err + }) +} +router.get('/', async (req, res) => { + getIngredients() + .then(v => { + res.setHeader('Content-Type', 'application/json;charset=utf-8') + res.end(JSON.stringify(v)) + }) + .catch(err => { + res.status(err.code).send(JSON.stringify( + { + "error": err.message, + "code": err.code + }) + ) + }) +}) + + +// GET ONE ------------------ +const getIngredient = (id) => { + return Ingredients.getOne(id) + .catch(err => { + console.log(err) + throw err + }) +} +router.get('/:id/', async (req, res) => { + getIngredient(req.params.id) + .then(v => { + res.setHeader('Content-Type', 'application/json;charset=utf-8') + res.end(JSON.stringify(v)) + }) + .catch(err => { + res.status(err.code).send(JSON.stringify( + { + "error": err.message, + "code": err.code + }) + ) + }) +}) + +// Param validation for ID +// (check if id is int) (could be refactored) +router.param('id', (req, res, next, id) => { + try { + if (regexInt.test(id)) { + next() + } else { + throw new Error; + } + } catch (err) { + err = new HttpError(403, 'Provided ID must be an integer and not zero') + res.status(err.code).send(JSON.stringify( + { + "error": err.message, + "code": err.code + }) + ) + } +}) + +module.exports = router \ No newline at end of file diff --git a/routes/meta_schools.js b/routes/meta_schools.js new file mode 100644 index 0000000..4639c36 --- /dev/null +++ b/routes/meta_schools.js @@ -0,0 +1,91 @@ +'use strict' + +// Router +const express = require('express') +let router = express.Router() + +// Connection +const connection = require('../database/bookshelf') +const db = connection.db + +// Repository +const MetaSchoolRepository = require('../repositories/meta-school-repository'); +const MetaSchools = new MetaSchoolRepository(); + +const regexInt = RegExp(/^[1-9]\d*$/) + +// Error handling +const { HttpError } = require('../validations/Errors') + +// ROUTES +// GET ALL ------------------ +const getMetaSchools = () => { + return MetaSchools.getAll() + .catch(err => { + console.log(err) + throw err + }) +} +router.get('/', async (req, res) => { + getMetaSchools() + .then(v => { + res.setHeader('Content-Type', 'application/json;charset=utf-8') + res.end(JSON.stringify(v)) + }) + .catch(err => { + res.status(err.code).send(JSON.stringify( + { + "error": err.message, + "code": err.code + }) + ) + }) +}) + + +// GET ONE ------------------ +const getMetaSchool = (id) => { + return MetaSchools.getOne(id) + .catch(err => { + console.log(err) + throw err + }) +} +router.get('/:id/', async (req, res) => { + getMetaSchool(req.params.id) + .then(v => { + res.setHeader('Content-Type', 'application/json;charset=utf-8') + res.end(JSON.stringify(v)) + }) + .catch(err => { + res.status(err.code).send(JSON.stringify( + { + "error": err.message, + "code": err.code + }) + ) + }) +}) + + +// Param validation for ID +// (check if id is int) (could be refactored) +router.param('id', (req, res, next, id) => { + try { + if (regexInt.test(id)) { + next() + } else { + throw new Error; + } + } catch (err) { + err = new HttpError(403, 'Provided ID must be an integer and not zero') + res.status(err.code).send(JSON.stringify( + { + "error": err.message, + "code": err.code + }) + ) + } +}) + +module.exports = router \ No newline at end of file diff --git a/routes/schools.js b/routes/schools.js index 502e303..4b228e0 100644 --- a/routes/schools.js +++ b/routes/schools.js @@ -5,7 +5,7 @@ const express = require('express') let router = express.Router() // Connection -const connection = require('../database/connection') +const connection = require('../database/bookshelf') const db = connection.db // Repository @@ -15,7 +15,7 @@ const Schools = new SchoolRepository(); const regexInt = RegExp(/^[1-9]\d*$/) // Error handling -const { HttpError } = require('../models/Errors') +const { HttpError } = require('../validations/Errors') // ROUTES // GET ALL ------------------ @@ -142,24 +142,23 @@ router.delete('/:id/', async (req, res) => { }) // Param validation for ID - // (check if id is int) (could be refactored) - router.param('id', (req, res, next, id) => { - try { - if (regexInt.test(id)) { - next() - } else { - throw new Error; - } - } catch (err) { - err = new HttpError(403, 'Provided ID must be an integer and not zero') - res.status(err.code).send(JSON.stringify( - { - "error": err.message, - "code": err.code - }) - ) +// (check if id is int) (could be refactored) +router.param('id', (req, res, next, id) => { + try { + if (regexInt.test(id)) { + next() + } else { + throw new Error; } - }) - - module.exports = router - \ No newline at end of file + } catch (err) { + err = new HttpError(403, 'Provided ID must be an integer and not zero') + res.status(err.code).send(JSON.stringify( + { + "error": err.message, + "code": err.code + }) + ) + } +}) + +module.exports = router \ No newline at end of file diff --git a/routes/spells.js b/routes/spells.js index f032da8..2d3ea4f 100644 --- a/routes/spells.js +++ b/routes/spells.js @@ -5,7 +5,7 @@ const express = require('express') let router = express.Router() // Connection -const connection = require('../database/connection') +const connection = require('../database/bookshelf') const db = connection.db // Repository @@ -15,7 +15,7 @@ const Spells = new SpellReposity(); const regexInt = RegExp(/^[1-9]\d*$/) // Error handling -const { HttpError } = require('../models/Errors') +const { HttpError } = require('../validations/Errors') // ROUTES // GET ALL ------------------ diff --git a/routes/users.js b/routes/users.js index 26ed9d2..951aab9 100644 --- a/routes/users.js +++ b/routes/users.js @@ -5,7 +5,7 @@ const express = require('express') let router = express.Router() // Connection -const connection = require('../database/connection') +const connection = require('../database/bookshelf') const db = connection.db // Repository @@ -15,7 +15,7 @@ const Users = new UserRepository(); const regexInt = RegExp(/^[1-9]\d*$/) // Error handling -const { HttpError } = require('../models/Errors') +const { HttpError } = require('../validations/Errors') // ROUTES // GET ALL ------------------ diff --git a/routes/variables.js b/routes/variables.js new file mode 100644 index 0000000..1814b18 --- /dev/null +++ b/routes/variables.js @@ -0,0 +1,91 @@ +'use strict' + +// Router +const express = require('express') +let router = express.Router() + +// Connection +const connection = require('../database/bookshelf') +const db = connection.db + +// Repository +const VariableRepository = require('../repositories/variable-repository'); +const Variables = new VariableRepository(); + +const regexInt = RegExp(/^[1-9]\d*$/) + +// Error handling +const { HttpError } = require('../validations/Errors') + +// ROUTES +// GET ALL ------------------ +const getvariables = () => { + return Variables.getAll() + .catch(err => { + console.log(err) + throw err + }) +} +router.get('/', async (req, res) => { + getvariables() + .then(v => { + res.setHeader('Content-Type', 'application/json;charset=utf-8') + res.end(JSON.stringify(v)) + }) + .catch(err => { + res.status(err.code).send(JSON.stringify( + { + "error": err.message, + "code": err.code + }) + ) + }) +}) + + +// GET ONE ------------------ +const getVariable = (id) => { + return Variables.getOne(id) + .catch(err => { + console.log(err) + throw err + }) +} +router.get('/:id/', async (req, res) => { + getVariable(req.params.id) + .then(v => { + res.setHeader('Content-Type', 'application/json;charset=utf-8') + res.end(JSON.stringify(v)) + }) + .catch(err => { + res.status(err.code).send(JSON.stringify( + { + "error": err.message, + "code": err.code + }) + ) + }) +}) + + +// Param validation for ID +// (check if id is int) (could be refactored) +router.param('id', (req, res, next, id) => { + try { + if (regexInt.test(id)) { + next() + } else { + throw new Error; + } + } catch (err) { + err = new HttpError(403, 'Provided ID must be an integer and not zero') + res.status(err.code).send(JSON.stringify( + { + "error": err.message, + "code": err.code + }) + ) + } +}) + +module.exports = router \ No newline at end of file diff --git a/models/Errors.js b/validations/Errors.js similarity index 100% rename from models/Errors.js rename to validations/Errors.js diff --git a/validations/IngredientValidation.js b/validations/IngredientValidation.js new file mode 100644 index 0000000..7bd1cef --- /dev/null +++ b/validations/IngredientValidation.js @@ -0,0 +1,10 @@ +const Ingredient = { + "id": "/IngredientValidation", + "type": Object, + "properties": { + "name": { "type": "string" }, + }, + "required": ["name"] +} + +module.exports = Ingredient \ No newline at end of file diff --git a/validations/MetaSchoolValidation.js b/validations/MetaSchoolValidation.js new file mode 100644 index 0000000..0bbcf23 --- /dev/null +++ b/validations/MetaSchoolValidation.js @@ -0,0 +1,12 @@ +const MetaSchool = { + "id": "/MetaSchoolValidation", + "type": Object, + "properties": { + "name": { "type": "string" }, + "description": { "type": "string" }, + "schools": { "type": "array" } + }, + "required": ["name", "description"] +} + +module.exports = MetaSchool \ No newline at end of file diff --git a/models/SchoolValidation.js b/validations/SchoolValidation.js similarity index 90% rename from models/SchoolValidation.js rename to validations/SchoolValidation.js index 8b48344..f2dd21d 100644 --- a/models/SchoolValidation.js +++ b/validations/SchoolValidation.js @@ -1,5 +1,5 @@ const School = { - "id": "/SchoolModel", + "id": "/SchoolValidation", "type": Object, "properties": { "name": { "type": "string" }, diff --git a/models/SpellValidation.js b/validations/SpellValidation.js similarity index 92% rename from models/SpellValidation.js rename to validations/SpellValidation.js index da0167e..4ae434b 100644 --- a/models/SpellValidation.js +++ b/validations/SpellValidation.js @@ -1,5 +1,5 @@ -const SpellModel = { - "id": "/SpellModel", +const Spell = { + "id": "/SpellValidation", "type": Object, "properties": { "name": { "type": "string" }, @@ -36,4 +36,4 @@ const SpellModel = { "required": ["name", "description"] } -module.exports = SpellModel \ No newline at end of file +module.exports = Spell \ No newline at end of file diff --git a/models/UserValidation.js b/validations/UserValidation.js similarity index 90% rename from models/UserValidation.js rename to validations/UserValidation.js index 451adad..1dc9290 100644 --- a/models/UserValidation.js +++ b/validations/UserValidation.js @@ -1,5 +1,5 @@ const User = { - "id": "/UserModel", + "id": "/UserValidation", "type": Object, "properties": { "name": { "type": "string" }, diff --git a/validations/VariableValidation.js b/validations/VariableValidation.js new file mode 100644 index 0000000..29cf829 --- /dev/null +++ b/validations/VariableValidation.js @@ -0,0 +1,10 @@ +const Variable = { + "id": "/VariableValidation", + "type": Object, + "properties": { + "description": { "type": "string" }, + }, + "required": ["description"] +} + +module.exports = Variable \ No newline at end of file From dda9545b68cd9691d755ae3ddad77b59aa7c01d1 Mon Sep 17 00:00:00 2001 From: Alexis <35.alexis.pele@gmail.com> Date: Mon, 8 Jun 2020 20:25:05 +0200 Subject: [PATCH 9/9] - Finished CRUD methods of Variables, Ingredients, MetaSchools, pretty much everything except users ye --- database/auracle_db_create.sql | 1 + database/auracle_db_data.sql | 6 +- repositories/ingredient-repository.js | 118 +++++++++++++++++++++++++ repositories/variable-repository.js | 122 ++++++++++++++++++++++++++ routes/ingredients.js | 75 ++++++++++++++++ routes/variables.js | 74 ++++++++++++++++ validations/IngredientValidation.js | 3 +- 7 files changed, 395 insertions(+), 4 deletions(-) diff --git a/database/auracle_db_create.sql b/database/auracle_db_create.sql index c07b3c1..9e5d75e 100644 --- a/database/auracle_db_create.sql +++ b/database/auracle_db_create.sql @@ -38,6 +38,7 @@ CREATE TABLE IF NOT EXISTS `school` ( CREATE TABLE IF NOT EXISTS `ingredient` ( `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, `name` VARCHAR(255) NOT NULL DEFAULT "Langue de salamandre", + `description` VARCHAR(255) NOT NULL DEFAULT "Une langue de salamandre de feu qui bouge encore un peu.", PRIMARY KEY (`id`) ); diff --git a/database/auracle_db_data.sql b/database/auracle_db_data.sql index e0a2fb3..b58cb4d 100644 --- a/database/auracle_db_data.sql +++ b/database/auracle_db_data.sql @@ -207,9 +207,9 @@ INSERT INTO `user` (name, mail, password) VALUES ('Kevin', '35.alexis.mail@gmail.com', 'sept777'); -- INGREDIENTS -INSERT INTO `ingredient` (name) VALUES -('Volonté'), -('Geste'); +INSERT INTO `ingredient` (name, description) VALUES +('Volonté', 'La force de volonté du lanceur, concentrée sur un objectif'), +('Geste', 'Un geste précis facilitant la canalisation magique'); -- VARIABLES INSERT INTO `variable` (description) VALUES diff --git a/repositories/ingredient-repository.js b/repositories/ingredient-repository.js index a24a30f..d6cc682 100644 --- a/repositories/ingredient-repository.js +++ b/repositories/ingredient-repository.js @@ -50,6 +50,124 @@ class IngredientRepository { }) } + addOne(igr) { + return new Promise((resolve, reject) => { + // Checks if body exists and if the model fits, and throws errors if it doesn't + if (this.isEmptyObject(igr)) { + reject(new HttpError(403, "Error: Ingredient cannot be nothing !")) + } else if (!v.validate(igr, IngredientValidation).valid) { + reject(new HttpError(403, "Error: Schema is not valid - " + v.validate(igr, IngredientValidation).errors)) + } else if (this.isXSSAttempt(igr.description)) { + reject(new HttpError(403, 'Injection attempt detected, aborting the request.')) + } else { + bookshelf.transaction(t => { + return model.forge({ + 'name': igr.name, + 'description': igr.description, + }).save(null, { + transacting: t + }) + .catch(err => { + throw err + }) + }) + .then(v => { + return v.load(['spells']) + }) + .then(v => { + resolve(this.getOne(v.id)) + }) + .catch(err => { + console.log(err) + reject(new HttpError(500, "Insert Ingredient error")) + }) + } + }) + } + + updateOne(id, igr) { + return new Promise((resolve, reject) => { + // Checks if body exists and if the model fits, and throws errors if it doesn't + if (this.isEmptyObject(igr)) { + reject(new HttpError(403, "Error: Ingredient cannot be nothing !")) + } else if (!v.validate(igr, IngredientValidation).valid) { + reject(new HttpError(403, "Error: Schema is not valid - " + v.validate(igr, IngredientValidation).errors)) + } else if (this.isXSSAttempt(igr.description)) { + reject(new HttpError(403, 'Injection attempt detected, aborting the request.')) + } else { + model.forge({id: id}) + .fetch({require: true, withRelated: ['spells']}) + .then(v => { + bookshelf.transaction(t => { + return v.save({ + 'name': igr.name, + 'description': igr.description, + }, { + method: 'update', + transacting: t + }) + .catch(err => { + console.log(err) + throw err + }) + }) + .then(v => { + return v.load(['spells']) + }) + .then(v => { + resolve(this.getOne(v.id)) + }) + .catch(err => { + console.log(err) + reject(new HttpError(500, "Update Ingredient error")) + }) + }) + .catch(err => { + console.log(err) + reject(new HttpError(404, "Couldn't get ingredient")) + }) + } + }) + } + + deleteOne(id) { + return new Promise((resolve, reject) => { + model.forge() + .where({ 'id' : id }) + .fetch({require: true, withRelated: ['spells']}) + .then(v => { + v.spells().detach() + v.destroy() + }) + .then(() => { + resolve({ + 'message': 'Ingredient with ID ' + id + ' successfully deleted !' + }) + }) + .catch(err => { + console.log(err) + reject(new HttpError(404, "Couldn't get ingredient")) + }) + }) + } + + // Check if object is null + isEmptyObject = (obj) => { + if (Object.keys(obj).length === 0 && obj.constructor === Object) { + return true + } else { + return false + } + } + + // Check if script injection attempt + isXSSAttempt = (string) => { + if (regexXSS.test(string)) { + return true + } else { + return false + } + } } module.exports = IngredientRepository \ No newline at end of file diff --git a/repositories/variable-repository.js b/repositories/variable-repository.js index 549f1ca..517a672 100644 --- a/repositories/variable-repository.js +++ b/repositories/variable-repository.js @@ -9,6 +9,11 @@ const v = new Validator() const VariableValidation = require("../validations/VariableValidation") v.addSchema(VariableValidation, "/VariableValidation") +// Validations +const regexXSS = RegExp(/<[^>]*script/) + +// Error handling +const { HttpError } = require('../validations/Errors') class VariableRepository { constructor() { @@ -43,6 +48,123 @@ class VariableRepository { }) }) } + + addOne(vr) { + return new Promise((resolve, reject) => { + // Checks if body exists and if the model fits, and throws errors if it doesn't + if (this.isEmptyObject(vr)) { + reject(new HttpError(403, "Error: Variable cannot be nothing !")) + } else if (!v.validate(vr, VariableValidation).valid) { + reject(new HttpError(403, "Error: Schema is not valid - " + v.validate(vr, VariableValidation).errors)) + } else if (this.isXSSAttempt(vr.description)) { + reject(new HttpError(403, 'Injection attempt detected, aborting the request.')) + } else { + bookshelf.transaction(t => { + return model.forge({ + 'description': vr.description, + }).save(null, { + transacting: t + }) + .catch(err => { + throw err + }) + }) + .then(v => { + return v.load(['spells']) + }) + .then(v => { + resolve(this.getOne(v.id)) + }) + .catch(err => { + console.log(err) + reject(new HttpError(500, "Insert Variable error")) + }) + } + }) + } + + updateOne(id, vr) { + return new Promise((resolve, reject) => { + // Checks if body exists and if the model fits, and throws errors if it doesn't + if (this.isEmptyObject(vr)) { + reject(new HttpError(403, "Error: Variable cannot be nothing !")) + } else if (!v.validate(vr, VariableValidation).valid) { + reject(new HttpError(403, "Error: Schema is not valid - " + v.validate(vr, VariableValidation).errors)) + } else if (this.isXSSAttempt(vr.description)) { + reject(new HttpError(403, 'Injection attempt detected, aborting the request.')) + } else { + model.forge({id: id}) + .fetch({require: true, withRelated: ['spells']}) + .then(v => { + bookshelf.transaction(t => { + return v.save({ + 'description': vr.description, + }, { + method: 'update', + transacting: t + }) + .catch(err => { + console.log(err) + throw err + }) + }) + .then(v => { + return v.load(['spells']) + }) + .then(v => { + resolve(this.getOne(v.id)) + }) + .catch(err => { + console.log(err) + reject(new HttpError(500, "Update Variable error")) + }) + }) + .catch(err => { + console.log(err) + reject(new HttpError(404, "Couldn't get variable")) + }) + } + }) + } + + deleteOne(id) { + return new Promise((resolve, reject) => { + model.forge() + .where({ 'id' : id }) + .fetch({require: true, withRelated: ['spells']}) + .then(v => { + v.spells().detach() + v.destroy() + }) + .then(() => { + resolve({ + 'message': 'Variable with ID ' + id + ' successfully deleted !' + }) + }) + .catch(err => { + console.log(err) + reject(new HttpError(404, "Couldn't get variable")) + }) + }) + } + + // Check if object is null + isEmptyObject = (obj) => { + if (Object.keys(obj).length === 0 && obj.constructor === Object) { + return true + } else { + return false + } + } + + // Check if script injection attempt + isXSSAttempt = (string) => { + if (regexXSS.test(string)) { + return true + } else { + return false + } + } } module.exports = VariableRepository \ No newline at end of file diff --git a/routes/ingredients.js b/routes/ingredients.js index ea5c36d..057b2f3 100644 --- a/routes/ingredients.js +++ b/routes/ingredients.js @@ -67,6 +67,81 @@ router.get('/:id/', async (req, res) => { }) }) + +// CREATE ONE ------------------ +const addIngredient = (igr) => { + return Ingredients.addOne(igr) + .catch(err => { + console.log(err) + throw err + }) +} +router.post('/', async (req, res) => { + addIngredient(req.body) + .then(v => { + res.setHeader('Content-Type', 'application/json;charset=utf-8') + res.send(JSON.stringify(v)) + }) + .catch(err => { + res.status(err.code).send(JSON.stringify( + { + "error": err.message, + "code": err.code + }) + ) + }) +}) + + +// UPDATE ONE ------------------ +const updateIngredient = (id, igr) => { + return Ingredients.updateOne(id, igr) + .catch(err => { + console.log(err) + throw err + }) +} +router.put('/:id/', async (req, res) => { + updateIngredient(req.params.id, req.body) + .then(v => { + res.setHeader('Content-Type', 'application/json;charset=utf-8') + res.send(JSON.stringify(v)) + }) + .catch(err => { + res.status(err.code).send(JSON.stringify( + { + "error": err.message, + "code": err.code + }) + ) + }) +}) + + +// DELETE ONE ------------------ +const deleteIngredient = (id) => { + return Ingredients.deleteOne(id) + .catch(err => { + console.log(err) + throw err + }) +} +router.delete('/:id/', async (req, res) => { + deleteIngredient(req.params.id) + .then(v => { + res.setHeader('Content-Type', 'application/json;charset=utf-8') + res.send(JSON.stringify(v)) + }) + .catch(err => { + res.status(err.code).send(JSON.stringify( + { + "error": err.message, + "code": err.code + }) + ) + }) +}) + // Param validation for ID // (check if id is int) (could be refactored) router.param('id', (req, res, next, id) => { diff --git a/routes/variables.js b/routes/variables.js index 1814b18..a4e649a 100644 --- a/routes/variables.js +++ b/routes/variables.js @@ -68,6 +68,80 @@ router.get('/:id/', async (req, res) => { }) +// CREATE ONE ------------------ +const addVariable = (vr) => { + return Variables.addOne(vr) + .catch(err => { + console.log(err) + throw err + }) +} +router.post('/', async (req, res) => { + addVariable(req.body) + .then(v => { + res.setHeader('Content-Type', 'application/json;charset=utf-8') + res.send(JSON.stringify(v)) + }) + .catch(err => { + res.status(err.code).send(JSON.stringify( + { + "error": err.message, + "code": err.code + }) + ) + }) +}) + + +// UPDATE ONE ------------------ +const updateVariable = (id, vr) => { + return Variables.updateOne(id, vr) + .catch(err => { + console.log(err) + throw err + }) +} +router.put('/:id/', async (req, res) => { + updateVariable(req.params.id, req.body) + .then(v => { + res.setHeader('Content-Type', 'application/json;charset=utf-8') + res.send(JSON.stringify(v)) + }) + .catch(err => { + res.status(err.code).send(JSON.stringify( + { + "error": err.message, + "code": err.code + }) + ) + }) +}) + + +// DELETE ONE ------------------ +const deleteVariable = (id) => { + return Variables.deleteOne(id) + .catch(err => { + console.log(err) + throw err + }) +} +router.delete('/:id/', async (req, res) => { + deleteVariable(req.params.id) + .then(v => { + res.setHeader('Content-Type', 'application/json;charset=utf-8') + res.send(JSON.stringify(v)) + }) + .catch(err => { + res.status(err.code).send(JSON.stringify( + { + "error": err.message, + "code": err.code + }) + ) + }) +}) + // Param validation for ID // (check if id is int) (could be refactored) router.param('id', (req, res, next, id) => { diff --git a/validations/IngredientValidation.js b/validations/IngredientValidation.js index 7bd1cef..7e7ef68 100644 --- a/validations/IngredientValidation.js +++ b/validations/IngredientValidation.js @@ -3,8 +3,9 @@ const Ingredient = { "type": Object, "properties": { "name": { "type": "string" }, + "description": { "type": "string" } }, - "required": ["name"] + "required": ["name", "description"] } module.exports = Ingredient \ No newline at end of file