diff --git a/package-lock.json b/package-lock.json index 8e2bfa7eb4..c621b92983 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,9 +25,9 @@ }, "dependencies": { "@types/node": { - "version": "10.17.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.5.tgz", - "integrity": "sha512-RElZIr/7JreF1eY6oD5RF3kpmdcreuQPjg5ri4oQ5g9sq7YWU8HkfB3eH8GwAwxf5OaCh0VPi7r4N/yoTGelrA==" + "version": "10.17.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.6.tgz", + "integrity": "sha512-0a2X6cgN3RdPBL2MIlR6Lt0KlM7fOFsutuXcdglcOq6WvLnYXgPQSh0Mx6tO1KCAE8MxbHSOSTWDoUxRq+l3DA==" } } }, @@ -100,92 +100,6 @@ "source-map": "^0.5.0" }, "dependencies": { - "@babel/generator": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.7.4.tgz", - "integrity": "sha512-m5qo2WgdOJeyYngKImbkyQrnUN1mPceaG5BV+G0E3gWsa4l/jCSryWJdM2x8OuGAOyh+3d5pVYfZWCiNFtynxg==", - "dev": true, - "requires": { - "@babel/types": "^7.7.4", - "jsesc": "^2.5.1", - "lodash": "^4.17.13", - "source-map": "^0.5.0" - } - }, - "@babel/helper-function-name": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.7.4.tgz", - "integrity": "sha512-AnkGIdiBhEuiwdoMnKm7jfPfqItZhgRaZfMg1XX3bS25INOnLPjPG1Ppnajh8eqgt5kPJnfqrRHqFqmjKDZLzQ==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.7.4", - "@babel/template": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.4.tgz", - "integrity": "sha512-QTGKEdCkjgzgfJ3bAyRwF4yyT3pg+vDgan8DSivq1eS0gwi+KGKE5x8kRcbeFTb/673mkO5SN1IZfmCfA5o+EA==", - "dev": true, - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.7.4.tgz", - "integrity": "sha512-guAg1SXFcVr04Guk9eq0S4/rWS++sbmyqosJzVs8+1fH5NI+ZcmkaSkc7dmtAFbHFva6yRJnjW3yAcGxjueDug==", - "dev": true, - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/parser": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.4.tgz", - "integrity": "sha512-jIwvLO0zCL+O/LmEJQjWA75MQTWwx3c3u2JOTDK5D3/9egrWRRA0/0hk9XXywYnXZVVpzrBYeIQTmhwUaePI9g==", - "dev": true - }, - "@babel/template": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.7.4.tgz", - "integrity": "sha512-qUzihgVPguAzXCK7WXw8pqs6cEwi54s3E+HrejlkuWO6ivMKx9hZl3Y2fSXp9i5HgyWmj7RKP+ulaYnKM4yYxw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/traverse": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.7.4.tgz", - "integrity": "sha512-P1L58hQyupn8+ezVA2z5KBm4/Zr4lCC8dwKCMYzsa5jFMDMQAzaBNy9W5VjB+KAmBjb40U7a/H6ao+Xo+9saIw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.7.4", - "@babel/helper-function-name": "^7.7.4", - "@babel/helper-split-export-declaration": "^7.7.4", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.13" - } - }, - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - }, "debug": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", @@ -210,12 +124,12 @@ } }, "@babel/generator": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.7.2.tgz", - "integrity": "sha512-WthSArvAjYLz4TcbKOi88me+KmDJdKSlfwwN8CnUYn9jBkzhq0ZEPuBfkAWIvjJ3AdEV1Cf/+eSQTnp3IDJKlQ==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.7.4.tgz", + "integrity": "sha512-m5qo2WgdOJeyYngKImbkyQrnUN1mPceaG5BV+G0E3gWsa4l/jCSryWJdM2x8OuGAOyh+3d5pVYfZWCiNFtynxg==", "dev": true, "requires": { - "@babel/types": "^7.7.2", + "@babel/types": "^7.7.4", "jsesc": "^2.5.1", "lodash": "^4.17.13", "source-map": "^0.5.0" @@ -236,19 +150,6 @@ "dev": true, "requires": { "@babel/types": "^7.7.4" - }, - "dependencies": { - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - } } }, "@babel/helper-builder-binary-assignment-operator-visitor": { @@ -259,19 +160,6 @@ "requires": { "@babel/helper-explode-assignable-expression": "^7.7.4", "@babel/types": "^7.7.4" - }, - "dependencies": { - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - } } }, "@babel/helper-call-delegate": { @@ -283,109 +171,6 @@ "@babel/helper-hoist-variables": "^7.7.4", "@babel/traverse": "^7.7.4", "@babel/types": "^7.7.4" - }, - "dependencies": { - "@babel/generator": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.7.4.tgz", - "integrity": "sha512-m5qo2WgdOJeyYngKImbkyQrnUN1mPceaG5BV+G0E3gWsa4l/jCSryWJdM2x8OuGAOyh+3d5pVYfZWCiNFtynxg==", - "dev": true, - "requires": { - "@babel/types": "^7.7.4", - "jsesc": "^2.5.1", - "lodash": "^4.17.13", - "source-map": "^0.5.0" - } - }, - "@babel/helper-function-name": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.7.4.tgz", - "integrity": "sha512-AnkGIdiBhEuiwdoMnKm7jfPfqItZhgRaZfMg1XX3bS25INOnLPjPG1Ppnajh8eqgt5kPJnfqrRHqFqmjKDZLzQ==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.7.4", - "@babel/template": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.4.tgz", - "integrity": "sha512-QTGKEdCkjgzgfJ3bAyRwF4yyT3pg+vDgan8DSivq1eS0gwi+KGKE5x8kRcbeFTb/673mkO5SN1IZfmCfA5o+EA==", - "dev": true, - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.7.4.tgz", - "integrity": "sha512-guAg1SXFcVr04Guk9eq0S4/rWS++sbmyqosJzVs8+1fH5NI+ZcmkaSkc7dmtAFbHFva6yRJnjW3yAcGxjueDug==", - "dev": true, - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/parser": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.4.tgz", - "integrity": "sha512-jIwvLO0zCL+O/LmEJQjWA75MQTWwx3c3u2JOTDK5D3/9egrWRRA0/0hk9XXywYnXZVVpzrBYeIQTmhwUaePI9g==", - "dev": true - }, - "@babel/template": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.7.4.tgz", - "integrity": "sha512-qUzihgVPguAzXCK7WXw8pqs6cEwi54s3E+HrejlkuWO6ivMKx9hZl3Y2fSXp9i5HgyWmj7RKP+ulaYnKM4yYxw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/traverse": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.7.4.tgz", - "integrity": "sha512-P1L58hQyupn8+ezVA2z5KBm4/Zr4lCC8dwKCMYzsa5jFMDMQAzaBNy9W5VjB+KAmBjb40U7a/H6ao+Xo+9saIw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.7.4", - "@babel/helper-function-name": "^7.7.4", - "@babel/helper-split-export-declaration": "^7.7.4", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.13" - } - }, - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } } }, "@babel/helper-create-regexp-features-plugin": { @@ -407,56 +192,6 @@ "@babel/helper-function-name": "^7.7.4", "@babel/types": "^7.7.4", "lodash": "^4.17.13" - }, - "dependencies": { - "@babel/helper-function-name": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.7.4.tgz", - "integrity": "sha512-AnkGIdiBhEuiwdoMnKm7jfPfqItZhgRaZfMg1XX3bS25INOnLPjPG1Ppnajh8eqgt5kPJnfqrRHqFqmjKDZLzQ==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.7.4", - "@babel/template": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.4.tgz", - "integrity": "sha512-QTGKEdCkjgzgfJ3bAyRwF4yyT3pg+vDgan8DSivq1eS0gwi+KGKE5x8kRcbeFTb/673mkO5SN1IZfmCfA5o+EA==", - "dev": true, - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/parser": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.4.tgz", - "integrity": "sha512-jIwvLO0zCL+O/LmEJQjWA75MQTWwx3c3u2JOTDK5D3/9egrWRRA0/0hk9XXywYnXZVVpzrBYeIQTmhwUaePI9g==", - "dev": true - }, - "@babel/template": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.7.4.tgz", - "integrity": "sha512-qUzihgVPguAzXCK7WXw8pqs6cEwi54s3E+HrejlkuWO6ivMKx9hZl3Y2fSXp9i5HgyWmj7RKP+ulaYnKM4yYxw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - } } }, "@babel/helper-explode-assignable-expression": { @@ -467,129 +202,26 @@ "requires": { "@babel/traverse": "^7.7.4", "@babel/types": "^7.7.4" - }, - "dependencies": { - "@babel/generator": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.7.4.tgz", - "integrity": "sha512-m5qo2WgdOJeyYngKImbkyQrnUN1mPceaG5BV+G0E3gWsa4l/jCSryWJdM2x8OuGAOyh+3d5pVYfZWCiNFtynxg==", - "dev": true, - "requires": { - "@babel/types": "^7.7.4", - "jsesc": "^2.5.1", - "lodash": "^4.17.13", - "source-map": "^0.5.0" - } - }, - "@babel/helper-function-name": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.7.4.tgz", - "integrity": "sha512-AnkGIdiBhEuiwdoMnKm7jfPfqItZhgRaZfMg1XX3bS25INOnLPjPG1Ppnajh8eqgt5kPJnfqrRHqFqmjKDZLzQ==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.7.4", - "@babel/template": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.4.tgz", - "integrity": "sha512-QTGKEdCkjgzgfJ3bAyRwF4yyT3pg+vDgan8DSivq1eS0gwi+KGKE5x8kRcbeFTb/673mkO5SN1IZfmCfA5o+EA==", - "dev": true, - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.7.4.tgz", - "integrity": "sha512-guAg1SXFcVr04Guk9eq0S4/rWS++sbmyqosJzVs8+1fH5NI+ZcmkaSkc7dmtAFbHFva6yRJnjW3yAcGxjueDug==", - "dev": true, - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/parser": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.4.tgz", - "integrity": "sha512-jIwvLO0zCL+O/LmEJQjWA75MQTWwx3c3u2JOTDK5D3/9egrWRRA0/0hk9XXywYnXZVVpzrBYeIQTmhwUaePI9g==", - "dev": true - }, - "@babel/template": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.7.4.tgz", - "integrity": "sha512-qUzihgVPguAzXCK7WXw8pqs6cEwi54s3E+HrejlkuWO6ivMKx9hZl3Y2fSXp9i5HgyWmj7RKP+ulaYnKM4yYxw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/traverse": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.7.4.tgz", - "integrity": "sha512-P1L58hQyupn8+ezVA2z5KBm4/Zr4lCC8dwKCMYzsa5jFMDMQAzaBNy9W5VjB+KAmBjb40U7a/H6ao+Xo+9saIw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.7.4", - "@babel/helper-function-name": "^7.7.4", - "@babel/helper-split-export-declaration": "^7.7.4", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.13" - } - }, - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } } }, "@babel/helper-function-name": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.7.0.tgz", - "integrity": "sha512-tDsJgMUAP00Ugv8O2aGEua5I2apkaQO7lBGUq1ocwN3G23JE5Dcq0uh3GvFTChPa4b40AWiAsLvCZOA2rdnQ7Q==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.7.4.tgz", + "integrity": "sha512-AnkGIdiBhEuiwdoMnKm7jfPfqItZhgRaZfMg1XX3bS25INOnLPjPG1Ppnajh8eqgt5kPJnfqrRHqFqmjKDZLzQ==", "dev": true, "requires": { - "@babel/helper-get-function-arity": "^7.7.0", - "@babel/template": "^7.7.0", - "@babel/types": "^7.7.0" + "@babel/helper-get-function-arity": "^7.7.4", + "@babel/template": "^7.7.4", + "@babel/types": "^7.7.4" } }, "@babel/helper-get-function-arity": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.0.tgz", - "integrity": "sha512-tLdojOTz4vWcEnHWHCuPN5P85JLZWbm5Fx5ZsMEMPhF3Uoe3O7awrbM2nQ04bDOUToH/2tH/ezKEOR8zEYzqyw==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.4.tgz", + "integrity": "sha512-QTGKEdCkjgzgfJ3bAyRwF4yyT3pg+vDgan8DSivq1eS0gwi+KGKE5x8kRcbeFTb/673mkO5SN1IZfmCfA5o+EA==", "dev": true, "requires": { - "@babel/types": "^7.7.0" + "@babel/types": "^7.7.4" } }, "@babel/helper-hoist-variables": { @@ -599,19 +231,6 @@ "dev": true, "requires": { "@babel/types": "^7.7.4" - }, - "dependencies": { - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - } } }, "@babel/helper-member-expression-to-functions": { @@ -621,19 +240,6 @@ "dev": true, "requires": { "@babel/types": "^7.7.4" - }, - "dependencies": { - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - } } }, "@babel/helper-module-imports": { @@ -643,19 +249,6 @@ "dev": true, "requires": { "@babel/types": "^7.7.4" - }, - "dependencies": { - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - } } }, "@babel/helper-module-transforms": { @@ -670,45 +263,6 @@ "@babel/template": "^7.7.4", "@babel/types": "^7.7.4", "lodash": "^4.17.13" - }, - "dependencies": { - "@babel/helper-split-export-declaration": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.7.4.tgz", - "integrity": "sha512-guAg1SXFcVr04Guk9eq0S4/rWS++sbmyqosJzVs8+1fH5NI+ZcmkaSkc7dmtAFbHFva6yRJnjW3yAcGxjueDug==", - "dev": true, - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/parser": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.4.tgz", - "integrity": "sha512-jIwvLO0zCL+O/LmEJQjWA75MQTWwx3c3u2JOTDK5D3/9egrWRRA0/0hk9XXywYnXZVVpzrBYeIQTmhwUaePI9g==", - "dev": true - }, - "@babel/template": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.7.4.tgz", - "integrity": "sha512-qUzihgVPguAzXCK7WXw8pqs6cEwi54s3E+HrejlkuWO6ivMKx9hZl3Y2fSXp9i5HgyWmj7RKP+ulaYnKM4yYxw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - } } }, "@babel/helper-optimise-call-expression": { @@ -718,19 +272,6 @@ "dev": true, "requires": { "@babel/types": "^7.7.4" - }, - "dependencies": { - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - } } }, "@babel/helper-plugin-utils": { @@ -757,111 +298,8 @@ "@babel/helper-annotate-as-pure": "^7.7.4", "@babel/helper-wrap-function": "^7.7.4", "@babel/template": "^7.7.4", - "@babel/traverse": "^7.7.4", - "@babel/types": "^7.7.4" - }, - "dependencies": { - "@babel/generator": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.7.4.tgz", - "integrity": "sha512-m5qo2WgdOJeyYngKImbkyQrnUN1mPceaG5BV+G0E3gWsa4l/jCSryWJdM2x8OuGAOyh+3d5pVYfZWCiNFtynxg==", - "dev": true, - "requires": { - "@babel/types": "^7.7.4", - "jsesc": "^2.5.1", - "lodash": "^4.17.13", - "source-map": "^0.5.0" - } - }, - "@babel/helper-function-name": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.7.4.tgz", - "integrity": "sha512-AnkGIdiBhEuiwdoMnKm7jfPfqItZhgRaZfMg1XX3bS25INOnLPjPG1Ppnajh8eqgt5kPJnfqrRHqFqmjKDZLzQ==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.7.4", - "@babel/template": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.4.tgz", - "integrity": "sha512-QTGKEdCkjgzgfJ3bAyRwF4yyT3pg+vDgan8DSivq1eS0gwi+KGKE5x8kRcbeFTb/673mkO5SN1IZfmCfA5o+EA==", - "dev": true, - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.7.4.tgz", - "integrity": "sha512-guAg1SXFcVr04Guk9eq0S4/rWS++sbmyqosJzVs8+1fH5NI+ZcmkaSkc7dmtAFbHFva6yRJnjW3yAcGxjueDug==", - "dev": true, - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/parser": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.4.tgz", - "integrity": "sha512-jIwvLO0zCL+O/LmEJQjWA75MQTWwx3c3u2JOTDK5D3/9egrWRRA0/0hk9XXywYnXZVVpzrBYeIQTmhwUaePI9g==", - "dev": true - }, - "@babel/template": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.7.4.tgz", - "integrity": "sha512-qUzihgVPguAzXCK7WXw8pqs6cEwi54s3E+HrejlkuWO6ivMKx9hZl3Y2fSXp9i5HgyWmj7RKP+ulaYnKM4yYxw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/traverse": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.7.4.tgz", - "integrity": "sha512-P1L58hQyupn8+ezVA2z5KBm4/Zr4lCC8dwKCMYzsa5jFMDMQAzaBNy9W5VjB+KAmBjb40U7a/H6ao+Xo+9saIw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.7.4", - "@babel/helper-function-name": "^7.7.4", - "@babel/helper-split-export-declaration": "^7.7.4", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.13" - } - }, - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } + "@babel/traverse": "^7.7.4", + "@babel/types": "^7.7.4" } }, "@babel/helper-replace-supers": { @@ -874,109 +312,6 @@ "@babel/helper-optimise-call-expression": "^7.7.4", "@babel/traverse": "^7.7.4", "@babel/types": "^7.7.4" - }, - "dependencies": { - "@babel/generator": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.7.4.tgz", - "integrity": "sha512-m5qo2WgdOJeyYngKImbkyQrnUN1mPceaG5BV+G0E3gWsa4l/jCSryWJdM2x8OuGAOyh+3d5pVYfZWCiNFtynxg==", - "dev": true, - "requires": { - "@babel/types": "^7.7.4", - "jsesc": "^2.5.1", - "lodash": "^4.17.13", - "source-map": "^0.5.0" - } - }, - "@babel/helper-function-name": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.7.4.tgz", - "integrity": "sha512-AnkGIdiBhEuiwdoMnKm7jfPfqItZhgRaZfMg1XX3bS25INOnLPjPG1Ppnajh8eqgt5kPJnfqrRHqFqmjKDZLzQ==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.7.4", - "@babel/template": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.4.tgz", - "integrity": "sha512-QTGKEdCkjgzgfJ3bAyRwF4yyT3pg+vDgan8DSivq1eS0gwi+KGKE5x8kRcbeFTb/673mkO5SN1IZfmCfA5o+EA==", - "dev": true, - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.7.4.tgz", - "integrity": "sha512-guAg1SXFcVr04Guk9eq0S4/rWS++sbmyqosJzVs8+1fH5NI+ZcmkaSkc7dmtAFbHFva6yRJnjW3yAcGxjueDug==", - "dev": true, - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/parser": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.4.tgz", - "integrity": "sha512-jIwvLO0zCL+O/LmEJQjWA75MQTWwx3c3u2JOTDK5D3/9egrWRRA0/0hk9XXywYnXZVVpzrBYeIQTmhwUaePI9g==", - "dev": true - }, - "@babel/template": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.7.4.tgz", - "integrity": "sha512-qUzihgVPguAzXCK7WXw8pqs6cEwi54s3E+HrejlkuWO6ivMKx9hZl3Y2fSXp9i5HgyWmj7RKP+ulaYnKM4yYxw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/traverse": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.7.4.tgz", - "integrity": "sha512-P1L58hQyupn8+ezVA2z5KBm4/Zr4lCC8dwKCMYzsa5jFMDMQAzaBNy9W5VjB+KAmBjb40U7a/H6ao+Xo+9saIw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.7.4", - "@babel/helper-function-name": "^7.7.4", - "@babel/helper-split-export-declaration": "^7.7.4", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.13" - } - }, - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } } }, "@babel/helper-simple-access": { @@ -987,45 +322,15 @@ "requires": { "@babel/template": "^7.7.4", "@babel/types": "^7.7.4" - }, - "dependencies": { - "@babel/parser": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.4.tgz", - "integrity": "sha512-jIwvLO0zCL+O/LmEJQjWA75MQTWwx3c3u2JOTDK5D3/9egrWRRA0/0hk9XXywYnXZVVpzrBYeIQTmhwUaePI9g==", - "dev": true - }, - "@babel/template": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.7.4.tgz", - "integrity": "sha512-qUzihgVPguAzXCK7WXw8pqs6cEwi54s3E+HrejlkuWO6ivMKx9hZl3Y2fSXp9i5HgyWmj7RKP+ulaYnKM4yYxw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - } } }, "@babel/helper-split-export-declaration": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.7.0.tgz", - "integrity": "sha512-HgYSI8rH08neWlAH3CcdkFg9qX9YsZysZI5GD8LjhQib/mM0jGOZOVkoUiiV2Hu978fRtjtsGsW6w0pKHUWtqA==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.7.4.tgz", + "integrity": "sha512-guAg1SXFcVr04Guk9eq0S4/rWS++sbmyqosJzVs8+1fH5NI+ZcmkaSkc7dmtAFbHFva6yRJnjW3yAcGxjueDug==", "dev": true, "requires": { - "@babel/types": "^7.7.0" + "@babel/types": "^7.7.4" } }, "@babel/helper-wrap-function": { @@ -1038,109 +343,6 @@ "@babel/template": "^7.7.4", "@babel/traverse": "^7.7.4", "@babel/types": "^7.7.4" - }, - "dependencies": { - "@babel/generator": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.7.4.tgz", - "integrity": "sha512-m5qo2WgdOJeyYngKImbkyQrnUN1mPceaG5BV+G0E3gWsa4l/jCSryWJdM2x8OuGAOyh+3d5pVYfZWCiNFtynxg==", - "dev": true, - "requires": { - "@babel/types": "^7.7.4", - "jsesc": "^2.5.1", - "lodash": "^4.17.13", - "source-map": "^0.5.0" - } - }, - "@babel/helper-function-name": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.7.4.tgz", - "integrity": "sha512-AnkGIdiBhEuiwdoMnKm7jfPfqItZhgRaZfMg1XX3bS25INOnLPjPG1Ppnajh8eqgt5kPJnfqrRHqFqmjKDZLzQ==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.7.4", - "@babel/template": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.4.tgz", - "integrity": "sha512-QTGKEdCkjgzgfJ3bAyRwF4yyT3pg+vDgan8DSivq1eS0gwi+KGKE5x8kRcbeFTb/673mkO5SN1IZfmCfA5o+EA==", - "dev": true, - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.7.4.tgz", - "integrity": "sha512-guAg1SXFcVr04Guk9eq0S4/rWS++sbmyqosJzVs8+1fH5NI+ZcmkaSkc7dmtAFbHFva6yRJnjW3yAcGxjueDug==", - "dev": true, - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/parser": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.4.tgz", - "integrity": "sha512-jIwvLO0zCL+O/LmEJQjWA75MQTWwx3c3u2JOTDK5D3/9egrWRRA0/0hk9XXywYnXZVVpzrBYeIQTmhwUaePI9g==", - "dev": true - }, - "@babel/template": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.7.4.tgz", - "integrity": "sha512-qUzihgVPguAzXCK7WXw8pqs6cEwi54s3E+HrejlkuWO6ivMKx9hZl3Y2fSXp9i5HgyWmj7RKP+ulaYnKM4yYxw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/traverse": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.7.4.tgz", - "integrity": "sha512-P1L58hQyupn8+ezVA2z5KBm4/Zr4lCC8dwKCMYzsa5jFMDMQAzaBNy9W5VjB+KAmBjb40U7a/H6ao+Xo+9saIw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.7.4", - "@babel/helper-function-name": "^7.7.4", - "@babel/helper-split-export-declaration": "^7.7.4", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.13" - } - }, - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } } }, "@babel/helpers": { @@ -1152,109 +354,6 @@ "@babel/template": "^7.7.4", "@babel/traverse": "^7.7.4", "@babel/types": "^7.7.4" - }, - "dependencies": { - "@babel/generator": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.7.4.tgz", - "integrity": "sha512-m5qo2WgdOJeyYngKImbkyQrnUN1mPceaG5BV+G0E3gWsa4l/jCSryWJdM2x8OuGAOyh+3d5pVYfZWCiNFtynxg==", - "dev": true, - "requires": { - "@babel/types": "^7.7.4", - "jsesc": "^2.5.1", - "lodash": "^4.17.13", - "source-map": "^0.5.0" - } - }, - "@babel/helper-function-name": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.7.4.tgz", - "integrity": "sha512-AnkGIdiBhEuiwdoMnKm7jfPfqItZhgRaZfMg1XX3bS25INOnLPjPG1Ppnajh8eqgt5kPJnfqrRHqFqmjKDZLzQ==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.7.4", - "@babel/template": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.4.tgz", - "integrity": "sha512-QTGKEdCkjgzgfJ3bAyRwF4yyT3pg+vDgan8DSivq1eS0gwi+KGKE5x8kRcbeFTb/673mkO5SN1IZfmCfA5o+EA==", - "dev": true, - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.7.4.tgz", - "integrity": "sha512-guAg1SXFcVr04Guk9eq0S4/rWS++sbmyqosJzVs8+1fH5NI+ZcmkaSkc7dmtAFbHFva6yRJnjW3yAcGxjueDug==", - "dev": true, - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/parser": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.4.tgz", - "integrity": "sha512-jIwvLO0zCL+O/LmEJQjWA75MQTWwx3c3u2JOTDK5D3/9egrWRRA0/0hk9XXywYnXZVVpzrBYeIQTmhwUaePI9g==", - "dev": true - }, - "@babel/template": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.7.4.tgz", - "integrity": "sha512-qUzihgVPguAzXCK7WXw8pqs6cEwi54s3E+HrejlkuWO6ivMKx9hZl3Y2fSXp9i5HgyWmj7RKP+ulaYnKM4yYxw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/traverse": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.7.4.tgz", - "integrity": "sha512-P1L58hQyupn8+ezVA2z5KBm4/Zr4lCC8dwKCMYzsa5jFMDMQAzaBNy9W5VjB+KAmBjb40U7a/H6ao+Xo+9saIw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.7.4", - "@babel/helper-function-name": "^7.7.4", - "@babel/helper-split-export-declaration": "^7.7.4", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.13" - } - }, - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } } }, "@babel/highlight": { @@ -1269,9 +368,9 @@ } }, "@babel/parser": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.2.tgz", - "integrity": "sha512-DDaR5e0g4ZTb9aP7cpSZLkACEBdoLGwJDWgHtBhrGX7Q1RjhdoMOfexICj5cqTAtpowjGQWfcvfnQG7G2kAB5w==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.4.tgz", + "integrity": "sha512-jIwvLO0zCL+O/LmEJQjWA75MQTWwx3c3u2JOTDK5D3/9egrWRRA0/0hk9XXywYnXZVVpzrBYeIQTmhwUaePI9g==", "dev": true }, "@babel/plugin-proposal-async-generator-functions": { @@ -1451,65 +550,6 @@ "@babel/helper-replace-supers": "^7.7.4", "@babel/helper-split-export-declaration": "^7.7.4", "globals": "^11.1.0" - }, - "dependencies": { - "@babel/helper-function-name": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.7.4.tgz", - "integrity": "sha512-AnkGIdiBhEuiwdoMnKm7jfPfqItZhgRaZfMg1XX3bS25INOnLPjPG1Ppnajh8eqgt5kPJnfqrRHqFqmjKDZLzQ==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.7.4", - "@babel/template": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.4.tgz", - "integrity": "sha512-QTGKEdCkjgzgfJ3bAyRwF4yyT3pg+vDgan8DSivq1eS0gwi+KGKE5x8kRcbeFTb/673mkO5SN1IZfmCfA5o+EA==", - "dev": true, - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.7.4.tgz", - "integrity": "sha512-guAg1SXFcVr04Guk9eq0S4/rWS++sbmyqosJzVs8+1fH5NI+ZcmkaSkc7dmtAFbHFva6yRJnjW3yAcGxjueDug==", - "dev": true, - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/parser": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.4.tgz", - "integrity": "sha512-jIwvLO0zCL+O/LmEJQjWA75MQTWwx3c3u2JOTDK5D3/9egrWRRA0/0hk9XXywYnXZVVpzrBYeIQTmhwUaePI9g==", - "dev": true - }, - "@babel/template": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.7.4.tgz", - "integrity": "sha512-qUzihgVPguAzXCK7WXw8pqs6cEwi54s3E+HrejlkuWO6ivMKx9hZl3Y2fSXp9i5HgyWmj7RKP+ulaYnKM4yYxw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - } } }, "@babel/plugin-transform-computed-properties": { @@ -1586,56 +626,6 @@ "requires": { "@babel/helper-function-name": "^7.7.4", "@babel/helper-plugin-utils": "^7.0.0" - }, - "dependencies": { - "@babel/helper-function-name": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.7.4.tgz", - "integrity": "sha512-AnkGIdiBhEuiwdoMnKm7jfPfqItZhgRaZfMg1XX3bS25INOnLPjPG1Ppnajh8eqgt5kPJnfqrRHqFqmjKDZLzQ==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.7.4", - "@babel/template": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.4.tgz", - "integrity": "sha512-QTGKEdCkjgzgfJ3bAyRwF4yyT3pg+vDgan8DSivq1eS0gwi+KGKE5x8kRcbeFTb/673mkO5SN1IZfmCfA5o+EA==", - "dev": true, - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/parser": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.4.tgz", - "integrity": "sha512-jIwvLO0zCL+O/LmEJQjWA75MQTWwx3c3u2JOTDK5D3/9egrWRRA0/0hk9XXywYnXZVVpzrBYeIQTmhwUaePI9g==", - "dev": true - }, - "@babel/template": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.7.4.tgz", - "integrity": "sha512-qUzihgVPguAzXCK7WXw8pqs6cEwi54s3E+HrejlkuWO6ivMKx9hZl3Y2fSXp9i5HgyWmj7RKP+ulaYnKM4yYxw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - } } }, "@babel/plugin-transform-literals": { @@ -1737,28 +727,6 @@ "@babel/helper-call-delegate": "^7.7.4", "@babel/helper-get-function-arity": "^7.7.4", "@babel/helper-plugin-utils": "^7.0.0" - }, - "dependencies": { - "@babel/helper-get-function-arity": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.4.tgz", - "integrity": "sha512-QTGKEdCkjgzgfJ3bAyRwF4yyT3pg+vDgan8DSivq1eS0gwi+KGKE5x8kRcbeFTb/673mkO5SN1IZfmCfA5o+EA==", - "dev": true, - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - } } }, "@babel/plugin-transform-property-literals": { @@ -1904,17 +872,6 @@ "semver": "^5.5.0" }, "dependencies": { - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - }, "semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", @@ -1941,28 +898,28 @@ } }, "@babel/template": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.7.0.tgz", - "integrity": "sha512-OKcwSYOW1mhWbnTBgQY5lvg1Fxg+VyfQGjcBduZFljfc044J5iDlnDSfhQ867O17XHiSCxYHUxHg2b7ryitbUQ==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.7.4.tgz", + "integrity": "sha512-qUzihgVPguAzXCK7WXw8pqs6cEwi54s3E+HrejlkuWO6ivMKx9hZl3Y2fSXp9i5HgyWmj7RKP+ulaYnKM4yYxw==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.7.0", - "@babel/types": "^7.7.0" + "@babel/parser": "^7.7.4", + "@babel/types": "^7.7.4" } }, "@babel/traverse": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.7.2.tgz", - "integrity": "sha512-TM01cXib2+rgIZrGJOLaHV/iZUAxf4A0dt5auY6KNZ+cm6aschuJGqKJM3ROTt3raPUdIDk9siAufIFEleRwtw==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.7.4.tgz", + "integrity": "sha512-P1L58hQyupn8+ezVA2z5KBm4/Zr4lCC8dwKCMYzsa5jFMDMQAzaBNy9W5VjB+KAmBjb40U7a/H6ao+Xo+9saIw==", "dev": true, "requires": { "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.7.2", - "@babel/helper-function-name": "^7.7.0", - "@babel/helper-split-export-declaration": "^7.7.0", - "@babel/parser": "^7.7.2", - "@babel/types": "^7.7.2", + "@babel/generator": "^7.7.4", + "@babel/helper-function-name": "^7.7.4", + "@babel/helper-split-export-declaration": "^7.7.4", + "@babel/parser": "^7.7.4", + "@babel/types": "^7.7.4", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.13" @@ -1980,9 +937,9 @@ } }, "@babel/types": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.2.tgz", - "integrity": "sha512-YTf6PXoh3+eZgRCBzzP25Bugd2ngmpQVrk7kXX0i5N9BO7TFBtIgZYs7WtxtOGs8e6A4ZI7ECkbBCEHeXocvOA==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", + "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", "dev": true, "requires": { "esutils": "^2.0.2", @@ -2271,9 +1228,9 @@ "integrity": "sha1-/1QEYtL7TQqIRBzq8n0oewHD2Hg=" }, "@types/koa": { - "version": "2.0.52", - "resolved": "https://registry.npmjs.org/@types/koa/-/koa-2.0.52.tgz", - "integrity": "sha512-cp/GTOhOYwomlSKqEoG0kaVEVJEzP4ojYmfa7EKaGkmkkRwJ4B/1VBLbQZ49Z+WJNvzXejQB/9GIKqMo9XLgFQ==", + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@types/koa/-/koa-2.11.0.tgz", + "integrity": "sha512-Hgx/1/rVlJvqYBrdeCsS7PDiR2qbxlMt1RnmNWD4Uxi5FF9nwkYqIldo7urjc+dfNpk+2NRGcnAYd4L5xEhCcQ==", "requires": { "@types/accepts": "*", "@types/cookies": "*", @@ -2308,9 +1265,9 @@ "dev": true }, "@types/node": { - "version": "12.12.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.6.tgz", - "integrity": "sha512-FjsYUPzEJdGXjwKqSpE0/9QEh6kzhTAeObA54rn6j3rR4C/mzpI9L0KNfoeASSPMMdxIsoJuCLDWcM/rVjIsSA==" + "version": "12.12.14", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.14.tgz", + "integrity": "sha512-u/SJDyXwuihpwjXy7hOOghagLEV1KdAST6syfnOk6QZAMzZuWZqXy5aYYZbh8Jdpd4escVFP0MvftHNDb9pruA==" }, "@types/normalize-package-data": { "version": "2.4.0", @@ -2333,9 +1290,9 @@ } }, "@types/ws": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-6.0.3.tgz", - "integrity": "sha512-yBTM0P05Tx9iXGq00BbJPo37ox68R5vaGTXivs6RGh/BQ6QP5zqZDGWdAO6JbRE/iR1l80xeGAwCQS2nMV9S/w==", + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-6.0.4.tgz", + "integrity": "sha512-PpPrX7SZW9re6+Ha8ojZG4Se8AZXgf0GK6zmfqEuCsY49LFDNXO3SByp44X3dFEqtB73lkCDAdUazhAjVPiNwg==", "requires": { "@types/node": "*" } @@ -2826,19 +1783,22 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true + "dev": true, + "optional": true }, "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==", - "dev": true + "dev": true, + "optional": true }, "arr-union": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", - "dev": true + "dev": true, + "optional": true }, "array-flatten": { "version": "1.1.1", @@ -2861,7 +1821,8 @@ "version": "0.3.2", "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true + "dev": true, + "optional": true }, "asn1": { "version": "0.2.4", @@ -2885,7 +1846,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", - "dev": true + "dev": true, + "optional": true }, "ast-types": { "version": "0.13.2", @@ -2935,12 +1897,13 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "dev": true + "dev": true, + "optional": true }, "aws-sdk": { - "version": "2.566.0", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.566.0.tgz", - "integrity": "sha512-AifnTGUUOri1xaOK7UyE4Qol97Cr309SJxA0EKuGBUkCG7KehgPYTMm13JAuXU0lyn7y0X6o1RKvNYeYsZ5DCg==", + "version": "2.580.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.580.0.tgz", + "integrity": "sha512-YUn/LgaSjWuFDCAIOiOvyXbuRpNEzTPLbwRs3GpEmrP1hJrOChXh0p7GH61sTZdeJZarCSETUOWU5ngjpCOjKA==", "requires": { "buffer": "^4.9.1", "events": "^1.1.1", @@ -2959,9 +1922,9 @@ "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" }, "aws4": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", - "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.0.tgz", + "integrity": "sha512-Uvq6hVe90D0B2WEnUqtdgY1bATGz3mw33nH9Y+dmA+w5DHvUmBgkr5rM/KCHpCsiFNRUfokW/szpPPgMK2hm4A==" }, "babel-eslint": { "version": "10.0.3", @@ -3009,6 +1972,7 @@ "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", "dev": true, + "optional": true, "requires": { "cache-base": "^1.0.1", "class-utils": "^0.3.5", @@ -3024,6 +1988,7 @@ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, + "optional": true, "requires": { "is-descriptor": "^1.0.0" } @@ -3033,6 +1998,7 @@ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, + "optional": true, "requires": { "kind-of": "^6.0.0" } @@ -3042,6 +2008,7 @@ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, + "optional": true, "requires": { "kind-of": "^6.0.0" } @@ -3051,6 +2018,7 @@ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, + "optional": true, "requires": { "is-accessor-descriptor": "^1.0.0", "is-data-descriptor": "^1.0.0", @@ -3072,14 +2040,6 @@ "requires": { "nan": "2.14.0", "node-pre-gyp": "0.13.0" - }, - "dependencies": { - "nan": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", - "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", - "optional": true - } } }, "bcrypt-nodejs": { @@ -3119,9 +2079,9 @@ } }, "bluebird": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.1.tgz", - "integrity": "sha512-DdmyoGCleJnkbp3nkbxTLJ18rjDsE4yCggEwKNXkeV123sPNfOCYeDoeuOY+F2FrSjO1YXcTU+dsy96KMy+gcg==", + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", "dev": true }, "body-parser": { @@ -3209,6 +2169,7 @@ "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", "dev": true, + "optional": true, "requires": { "arr-flatten": "^1.1.0", "array-unique": "^0.3.2", @@ -3227,6 +2188,7 @@ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, + "optional": true, "requires": { "is-extendable": "^0.1.0" } @@ -3234,25 +2196,25 @@ } }, "browserslist": { - "version": "4.7.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.7.3.tgz", - "integrity": "sha512-jWvmhqYpx+9EZm/FxcZSbUZyDEvDTLDi3nSAKbzEkyWvtI0mNSmUosey+5awDW1RUlrgXbQb5A6qY1xQH9U6MQ==", + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.8.0.tgz", + "integrity": "sha512-HYnxc/oLRWvJ3TsGegR0SRL/UDnknGq2s/a8dYYEO+kOQ9m9apKoS5oiathLKZdh/e9uE+/J3j92qPlGD/vTqA==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001010", - "electron-to-chromium": "^1.3.306", - "node-releases": "^1.1.40" + "caniuse-lite": "^1.0.30001012", + "electron-to-chromium": "^1.3.317", + "node-releases": "^1.1.41" } }, "bson": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.1.tgz", - "integrity": "sha512-jCGVYLoYMHDkOsbwJZBCqwMHyH4c+wzgI9hG7Z6SZJRXWr+x58pdIbm2i9a/jFGCkRJqRUr8eoI7lDWa0hTkxg==" + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.3.tgz", + "integrity": "sha512-TdiJxMVnodVS7r0BdL42y/pqC9cL2iKynVwA0Ho3qbsQYr428veL3l7BQyuqiw+Q5SqqoT0m4srSY/BlZ9AxXg==" }, "buffer": { - "version": "4.9.1", - "resolved": "http://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", - "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", "requires": { "base64-js": "^1.0.2", "ieee754": "^1.1.4", @@ -3326,6 +2288,7 @@ "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", "dev": true, + "optional": true, "requires": { "collection-visit": "^1.0.0", "component-emitter": "^1.2.1", @@ -3389,9 +2352,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001011", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001011.tgz", - "integrity": "sha512-h+Eqyn/YA6o6ZTqpS86PyRmNWOs1r54EBDcd2NTwwfsXQ8re1B38SnB+p2RKF8OUsyEIjeDU8XGec1RGO/wYCg==", + "version": "1.0.30001012", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001012.tgz", + "integrity": "sha512-7RR4Uh04t9K1uYRWzOJmzplgEOAXbfK72oVNokCdMzA67trrhPzy93ahKk1AWHiA0c58tD2P+NHqxrA8FZ+Trg==", "dev": true }, "caseless": { @@ -3499,6 +2462,7 @@ "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", "dev": true, + "optional": true, "requires": { "arr-union": "^3.1.0", "define-property": "^0.2.5", @@ -3511,6 +2475,7 @@ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, + "optional": true, "requires": { "is-descriptor": "^0.1.0" } @@ -3654,6 +2619,7 @@ "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", "dev": true, + "optional": true, "requires": { "map-visit": "^1.0.0", "object-visit": "^1.0.0" @@ -3732,7 +2698,8 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", @@ -3811,17 +2778,18 @@ "version": "0.1.1", "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", - "dev": true + "dev": true, + "optional": true }, "core-js": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.4.1.tgz", - "integrity": "sha512-KX/dnuY/J8FtEwbnrzmAjUYgLqtk+cxM86hfG60LGiW3MmltIc2yAmDgBgEkfm0blZhUrdr1Zd84J2Y14mLxzg==" + "version": "3.4.5", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.4.5.tgz", + "integrity": "sha512-OuvejWH6vIaUo59Ndlh89purNm4DCIy/v3QoYlcGnn+PkYI8BhNHfCuAESrWX+ZPfq9JccVJ+XXgOMy77PJexg==" }, "core-js-compat": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.4.2.tgz", - "integrity": "sha512-W0Aj+LM3EAxxjD0Kp2o4be8UlnxIZHNupBv2znqrheR4aY2nOn91794k/xoSp+SxqqriiZpTsSwBtZr60cbkwQ==", + "version": "3.4.5", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.4.5.tgz", + "integrity": "sha512-rYVvzvKJDKoefdAC+q6VP63vp5hMmeVONCi9pVUbU1qRrtVrmAk/nPhnRg+i+XFd775m1hpG2Yd5RY3X45ccuw==", "dev": true, "requires": { "browserslist": "^4.7.3", @@ -3829,9 +2797,9 @@ } }, "core-js-pure": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.4.0.tgz", - "integrity": "sha512-d49s6GiW3ePYM8vCglfLLo6bueYx+Sff6MYtjohTMSB0AoxVfABXMUSmYHtKAEvW77T9JTKMyHrhE20nZ8gYDA==" + "version": "3.4.5", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.4.5.tgz", + "integrity": "sha512-v3BoUOhmBvs4Z17jG/oM7qyv+tEEMvD1FYDDfxa6uD5W2rA/DpKvhvmyrBzxuMQTa/91UQKisaiqe0+0GuL2oA==" }, "core-util-is": { "version": "1.0.2", @@ -3878,16 +2846,15 @@ } }, "coveralls": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/coveralls/-/coveralls-3.0.7.tgz", - "integrity": "sha512-mUuH2MFOYB2oBaA4D4Ykqi9LaEYpMMlsiOMJOrv358yAjP6enPIk55fod2fNJ8AvwoYXStWQls37rA+s5e7boA==", + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/coveralls/-/coveralls-3.0.9.tgz", + "integrity": "sha512-nNBg3B1+4iDox5A5zqHKzUTiwl2ey4k2o0NEcVZYvl+GOSJdKBj4AJGKLv6h3SvWch7tABHePAQOSZWM9E2hMg==", "requires": { - "growl": "~> 1.10.0", "js-yaml": "^3.13.1", - "lcov-parse": "^0.0.10", + "lcov-parse": "^1.0.0", "log-driver": "^1.2.7", "minimist": "^1.2.0", - "request": "^2.86.0" + "request": "^2.88.0" } }, "cp-file": { @@ -3992,7 +2959,8 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", - "dev": true + "dev": true, + "optional": true }, "decompress": { "version": "4.2.0", @@ -4176,6 +3144,7 @@ "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", "dev": true, + "optional": true, "requires": { "is-descriptor": "^1.0.2", "isobject": "^3.0.1" @@ -4186,6 +3155,7 @@ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, + "optional": true, "requires": { "kind-of": "^6.0.0" } @@ -4195,6 +3165,7 @@ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, + "optional": true, "requires": { "kind-of": "^6.0.0" } @@ -4204,6 +3175,7 @@ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, + "optional": true, "requires": { "is-accessor-descriptor": "^1.0.0", "is-data-descriptor": "^1.0.0", @@ -4465,14 +3437,6 @@ "optional": true, "requires": { "nan": "^2.14.0" - }, - "dependencies": { - "nan": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", - "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", - "optional": true - } } }, "duplexer3": { @@ -4504,9 +3468,9 @@ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, "electron-to-chromium": { - "version": "1.3.311", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.311.tgz", - "integrity": "sha512-7GH6RKCzziLzJ9ejmbiBEdzHZsc6C3eRpav14dmRfTWMpNgMqpP1ukw/FU/Le2fR+ep642naq7a23xNdmh2s+A==", + "version": "1.3.321", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.321.tgz", + "integrity": "sha512-jJy/BZK2s2eAjMPXVMSaCmo7/pSY2aKkfQ+LoAb5Wk39qAhyP9r8KU74c4qTgr9cD/lPUhJgReZxxqU0n5puog==", "dev": true }, "elegant-spinner": { @@ -4572,26 +3536,26 @@ } }, "es-abstract": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.16.0.tgz", - "integrity": "sha512-xdQnfykZ9JMEiasTAJZJdMWCQ1Vm00NBw79/AWi7ELfZuuPCSOMDZbT9mkOfSctVtfhb+sAAzrm+j//GjjLHLg==", + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.16.2.tgz", + "integrity": "sha512-jYo/J8XU2emLXl3OLwfwtuFfuF2w6DYPs+xy9ZfVyPkDcrauu6LYrw/q2TyCtrbc/KUdCiC5e9UajRhgNkVopA==", "requires": { - "es-to-primitive": "^1.2.0", + "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", "has": "^1.0.3", - "has-symbols": "^1.0.0", + "has-symbols": "^1.0.1", "is-callable": "^1.1.4", "is-regex": "^1.0.4", - "object-inspect": "^1.6.0", + "object-inspect": "^1.7.0", "object-keys": "^1.1.1", "string.prototype.trimleft": "^2.1.0", "string.prototype.trimright": "^2.1.0" } }, "es-to-primitive": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", - "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", "requires": { "is-callable": "^1.1.4", "is-date-object": "^1.0.1", @@ -4599,13 +3563,13 @@ } }, "es5-ext": { - "version": "0.10.52", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.52.tgz", - "integrity": "sha512-bWCbE9fbpYQY4CU6hJbJ1vSz70EClMlDgJ7BmwI+zEJhxrwjesZRPglGJlsZhu0334U3hI+gaspwksH9IGD6ag==", + "version": "0.10.53", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", + "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", "dev": true, "requires": { "es6-iterator": "~2.0.3", - "es6-symbol": "~3.1.2", + "es6-symbol": "~3.1.3", "next-tick": "~1.0.0" } }, @@ -5070,6 +4034,7 @@ "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", "dev": true, + "optional": true, "requires": { "debug": "^2.3.3", "define-property": "^0.2.5", @@ -5085,6 +4050,7 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, + "optional": true, "requires": { "ms": "2.0.0" } @@ -5094,6 +4060,7 @@ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, + "optional": true, "requires": { "is-descriptor": "^0.1.0" } @@ -5103,6 +4070,7 @@ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, + "optional": true, "requires": { "is-extendable": "^0.1.0" } @@ -5111,7 +4079,8 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true + "dev": true, + "optional": true } } }, @@ -5178,9 +4147,9 @@ } }, "ext": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.2.0.tgz", - "integrity": "sha512-0ccUQK/9e3NreLFg6K6np8aPyRgwycx+oFGtfx1dSp7Wj00Ozw9r05FgBRlzjf2XBM7LAzwgLyDscRrtSU91hA==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", + "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==", "dev": true, "requires": { "type": "^2.0.0" @@ -5223,6 +4192,7 @@ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", "dev": true, + "optional": true, "requires": { "assign-symbols": "^1.0.0", "is-extendable": "^1.0.1" @@ -5233,6 +4203,7 @@ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "dev": true, + "optional": true, "requires": { "is-plain-object": "^2.0.4" } @@ -5255,6 +4226,7 @@ "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", "dev": true, + "optional": true, "requires": { "array-unique": "^0.3.2", "define-property": "^1.0.0", @@ -5271,6 +4243,7 @@ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, + "optional": true, "requires": { "is-descriptor": "^1.0.0" } @@ -5280,6 +4253,7 @@ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, + "optional": true, "requires": { "is-extendable": "^0.1.0" } @@ -5289,6 +4263,7 @@ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, + "optional": true, "requires": { "kind-of": "^6.0.0" } @@ -5298,6 +4273,7 @@ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, + "optional": true, "requires": { "kind-of": "^6.0.0" } @@ -5307,6 +4283,7 @@ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, + "optional": true, "requires": { "is-accessor-descriptor": "^1.0.0", "is-data-descriptor": "^1.0.0", @@ -5332,9 +4309,9 @@ "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" }, "fast-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.1.0.tgz", - "integrity": "sha512-TrUz3THiq2Vy3bjfQUB2wNyPdGBeGmdjbzzBLhfHN4YFurYptCKwGq/TfiRavbGywFRzY6U2CdmQ1zmsY5yYaw==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.1.1.tgz", + "integrity": "sha512-nTCREpBY8w8r+boyFYAx21iL6faSsQynliPHM4Uf56SbkyohCNxpVPEH9xrF5TXKy+IsjkPUHDKiUkzBVRXn9g==", "dev": true, "requires": { "@nodelib/fs.stat": "^2.0.2", @@ -5495,6 +4472,7 @@ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", "dev": true, + "optional": true, "requires": { "extend-shallow": "^2.0.1", "is-number": "^3.0.0", @@ -5507,6 +4485,7 @@ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, + "optional": true, "requires": { "is-extendable": "^0.1.0" } @@ -5660,7 +4639,8 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", - "dev": true + "dev": true, + "optional": true }, "foreground-child": { "version": "1.5.6", @@ -5729,6 +4709,7 @@ "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", "dev": true, + "optional": true, "requires": { "map-cache": "^0.2.2" } @@ -5793,24 +4774,29 @@ "dependencies": { "abbrev": { "version": "1.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true, "optional": true }, "ansi-regex": { "version": "2.1.1", - "bundled": true, - "dev": true + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", "dev": true, "optional": true }, "are-we-there-yet": { "version": "1.1.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", "dev": true, "optional": true, "requires": { @@ -5820,13 +4806,17 @@ }, "balanced-match": { "version": "1.0.0", - "bundled": true, - "dev": true + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", - "bundled": true, + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -5834,34 +4824,43 @@ }, "chownr": { "version": "1.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz", + "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==", "dev": true, "optional": true }, "code-point-at": { "version": "1.1.0", - "bundled": true, - "dev": true + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", - "bundled": true, - "dev": true + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", - "bundled": true, - "dev": true + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "dev": true, "optional": true }, "debug": { "version": "4.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", "dev": true, "optional": true, "requires": { @@ -5870,25 +4869,29 @@ }, "deep-extend": { "version": "0.6.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "dev": true, "optional": true }, "delegates": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", "dev": true, "optional": true }, "detect-libc": { "version": "1.0.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", "dev": true, "optional": true }, "fs-minipass": { "version": "1.2.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz", + "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==", "dev": true, "optional": true, "requires": { @@ -5897,13 +4900,15 @@ }, "fs.realpath": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true, "optional": true }, "gauge": { "version": "2.7.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", "dev": true, "optional": true, "requires": { @@ -5919,7 +4924,8 @@ }, "glob": { "version": "7.1.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", "dev": true, "optional": true, "requires": { @@ -5933,13 +4939,15 @@ }, "has-unicode": { "version": "2.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", "dev": true, "optional": true }, "iconv-lite": { "version": "0.4.24", - "bundled": true, + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, "optional": true, "requires": { @@ -5948,7 +4956,8 @@ }, "ignore-walk": { "version": "3.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz", + "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", "dev": true, "optional": true, "requires": { @@ -5957,7 +4966,8 @@ }, "inflight": { "version": "1.0.6", - "bundled": true, + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "optional": true, "requires": { @@ -5967,46 +4977,58 @@ }, "inherits": { "version": "2.0.3", - "bundled": true, - "dev": true + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", "dev": true, "optional": true }, "is-fullwidth-code-point": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } }, "isarray": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true, "optional": true }, "minimatch": { "version": "3.0.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } }, "minimist": { "version": "0.0.8", - "bundled": true, - "dev": true + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true, + "optional": true }, "minipass": { "version": "2.3.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz", + "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -6014,7 +5036,8 @@ }, "minizlib": { "version": "1.2.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.2.1.tgz", + "integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==", "dev": true, "optional": true, "requires": { @@ -6023,21 +5046,25 @@ }, "mkdirp": { "version": "0.5.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } }, "ms": { "version": "2.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", "dev": true, "optional": true }, "needle": { "version": "2.3.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/needle/-/needle-2.3.0.tgz", + "integrity": "sha512-QBZu7aAFR0522EyaXZM0FZ9GLpq6lvQ3uq8gteiDUp7wKdy0lSd2hPlgFwVuW1CBkfEs9PfDQsQzZghLs/psdg==", "dev": true, "optional": true, "requires": { @@ -6048,7 +5075,8 @@ }, "node-pre-gyp": { "version": "0.12.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.12.0.tgz", + "integrity": "sha512-4KghwV8vH5k+g2ylT+sLTjy5wmUOb9vPhnM8NHvRf9dHmnW/CndrFXy2aRPaPST6dugXSdHXfeaHQm77PIz/1A==", "dev": true, "optional": true, "requires": { @@ -6066,7 +5094,8 @@ }, "nopt": { "version": "4.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", + "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", "dev": true, "optional": true, "requires": { @@ -6076,13 +5105,15 @@ }, "npm-bundled": { "version": "1.0.6", - "bundled": true, + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.6.tgz", + "integrity": "sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g==", "dev": true, "optional": true }, "npm-packlist": { "version": "1.4.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.1.tgz", + "integrity": "sha512-+TcdO7HJJ8peiiYhvPxsEDhF3PJFGUGRcFsGve3vxvxdcpO2Z4Z7rkosRM0kWj6LfbK/P0gu3dzk5RU1ffvFcw==", "dev": true, "optional": true, "requires": { @@ -6092,7 +5123,8 @@ }, "npmlog": { "version": "4.1.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "dev": true, "optional": true, "requires": { @@ -6104,38 +5136,46 @@ }, "number-is-nan": { "version": "1.0.1", - "bundled": true, - "dev": true + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "dev": true, "optional": true }, "once": { "version": "1.4.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, + "optional": true, "requires": { "wrappy": "1" } }, "os-homedir": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "dev": true, "optional": true }, "os-tmpdir": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true, "optional": true }, "osenv": { "version": "0.1.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", "dev": true, "optional": true, "requires": { @@ -6145,19 +5185,22 @@ }, "path-is-absolute": { "version": "1.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true, "optional": true }, "process-nextick-args": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", "dev": true, "optional": true }, "rc": { "version": "1.2.8", - "bundled": true, + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", "dev": true, "optional": true, "requires": { @@ -6169,7 +5212,8 @@ "dependencies": { "minimist": { "version": "1.2.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true, "optional": true } @@ -6177,7 +5221,8 @@ }, "readable-stream": { "version": "2.3.6", - "bundled": true, + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "optional": true, "requires": { @@ -6192,7 +5237,8 @@ }, "rimraf": { "version": "2.6.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", "dev": true, "optional": true, "requires": { @@ -6201,43 +5247,52 @@ }, "safe-buffer": { "version": "5.1.2", - "bundled": true, - "dev": true + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true, "optional": true }, "sax": { "version": "1.2.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", "dev": true, "optional": true }, "semver": { "version": "5.7.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", "dev": true, "optional": true }, "set-blocking": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true, "optional": true }, "signal-exit": { "version": "3.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true, "optional": true }, "string-width": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -6246,7 +5301,8 @@ }, "string_decoder": { "version": "1.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "optional": true, "requires": { @@ -6255,21 +5311,25 @@ }, "strip-ansi": { "version": "3.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } }, "strip-json-comments": { "version": "2.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "dev": true, "optional": true }, "tar": { "version": "4.4.8", - "bundled": true, + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.8.tgz", + "integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==", "dev": true, "optional": true, "requires": { @@ -6284,13 +5344,15 @@ }, "util-deprecate": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true, "optional": true }, "wide-align": { "version": "1.1.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", "dev": true, "optional": true, "requires": { @@ -6299,13 +5361,17 @@ }, "wrappy": { "version": "1.0.2", - "bundled": true, - "dev": true + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true, + "optional": true }, "yallist": { "version": "3.0.3", - "bundled": true, - "dev": true + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", + "dev": true, + "optional": true } } }, @@ -6325,7 +5391,7 @@ }, "readable-stream": { "version": "1.1.14", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", "requires": { "core-util-is": "~1.0.0", @@ -6467,7 +5533,8 @@ "version": "2.0.6", "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", - "dev": true + "dev": true, + "optional": true }, "getpass": { "version": "0.1.7", @@ -6625,6 +5692,21 @@ "resolved": "https://registry.npmjs.org/graphql-list-fields/-/graphql-list-fields-2.0.2.tgz", "integrity": "sha512-9TSAwcVA3KWw7JWYep5NCk2aw3wl1ayLtbMpmG7l26vh1FZ+gZexNPP+XJfUFyJa71UU0zcKSgtgpsrsA3Xv9Q==" }, + "graphql-relay": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/graphql-relay/-/graphql-relay-0.6.0.tgz", + "integrity": "sha512-OVDi6C9/qOT542Q3KxZdXja3NrDvqzbihn1B44PH8P/c5s0Q90RyQwT6guhGqXqbYEH6zbeLJWjQqiYvcg2vVw==", + "requires": { + "prettier": "^1.16.0" + }, + "dependencies": { + "prettier": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", + "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==" + } + } + }, "graphql-subscriptions": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/graphql-subscriptions/-/graphql-subscriptions-1.1.0.tgz", @@ -6661,11 +5743,6 @@ "object-path": "^0.11.4" } }, - "growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==" - }, "handlebars": { "version": "4.5.3", "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.3.tgz", @@ -6722,9 +5799,9 @@ "dev": true }, "has-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", - "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=" + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" }, "has-to-string-tag-x": { "version": "1.4.1", @@ -6745,6 +5822,7 @@ "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", "dev": true, + "optional": true, "requires": { "get-value": "^2.0.6", "has-values": "^1.0.0", @@ -6756,6 +5834,7 @@ "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", "dev": true, + "optional": true, "requires": { "is-number": "^3.0.0", "kind-of": "^4.0.0" @@ -6766,6 +5845,7 @@ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", "dev": true, + "optional": true, "requires": { "is-buffer": "^1.1.5" } @@ -7081,6 +6161,7 @@ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "dev": true, + "optional": true, "requires": { "kind-of": "^3.0.2" }, @@ -7090,6 +6171,7 @@ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, + "optional": true, "requires": { "is-buffer": "^1.1.5" } @@ -7115,7 +6197,8 @@ "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true + "dev": true, + "optional": true }, "is-callable": { "version": "1.1.4", @@ -7127,6 +6210,7 @@ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "dev": true, + "optional": true, "requires": { "kind-of": "^3.0.2" }, @@ -7136,6 +6220,7 @@ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, + "optional": true, "requires": { "is-buffer": "^1.1.5" } @@ -7152,6 +6237,7 @@ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", "dev": true, + "optional": true, "requires": { "is-accessor-descriptor": "^0.1.6", "is-data-descriptor": "^0.1.4", @@ -7162,7 +6248,8 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true + "dev": true, + "optional": true } } }, @@ -7176,7 +6263,8 @@ "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "dev": true + "dev": true, + "optional": true }, "is-extglob": { "version": "2.1.1", @@ -7239,6 +6327,7 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, + "optional": true, "requires": { "kind-of": "^3.0.2" }, @@ -7248,6 +6337,7 @@ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, + "optional": true, "requires": { "is-buffer": "^1.1.5" } @@ -7256,7 +6346,7 @@ }, "is-obj": { "version": "1.0.1", - "resolved": "http://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", "dev": true }, @@ -7298,6 +6388,7 @@ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "dev": true, + "optional": true, "requires": { "isobject": "^3.0.1" } @@ -7334,11 +6425,11 @@ "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" }, "is-symbol": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", - "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", + "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", "requires": { - "has-symbols": "^1.0.0" + "has-symbols": "^1.0.1" } }, "is-typedarray": { @@ -7350,7 +6441,8 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true + "dev": true, + "optional": true }, "is-wsl": { "version": "1.1.0", @@ -7373,7 +6465,8 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true + "dev": true, + "optional": true }, "isstream": { "version": "0.1.2", @@ -7703,7 +6796,8 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true + "dev": true, + "optional": true }, "klaw": { "version": "3.0.0", @@ -7723,9 +6817,9 @@ } }, "lcov-parse": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/lcov-parse/-/lcov-parse-0.0.10.tgz", - "integrity": "sha1-GwuP+ayceIklBYK3C3ExXZ2m2aM=" + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcov-parse/-/lcov-parse-1.0.0.tgz", + "integrity": "sha1-6w1GtUER68VhrLTECO+TY73I9+A=" }, "ldap-filter": { "version": "0.2.2", @@ -7980,7 +7074,7 @@ }, "chalk": { "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { @@ -8460,13 +7554,15 @@ "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", - "dev": true + "dev": true, + "optional": true }, "map-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", "dev": true, + "optional": true, "requires": { "object-visit": "^1.0.0" } @@ -8573,6 +7669,7 @@ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", "dev": true, + "optional": true, "requires": { "arr-diff": "^4.0.0", "array-unique": "^0.3.2", @@ -8595,16 +7692,16 @@ "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==" }, "mime-db": { - "version": "1.40.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", - "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==" + "version": "1.42.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.42.0.tgz", + "integrity": "sha512-UbfJCR4UAVRNgMpfImz05smAXK7+c+ZntjaA26ANtkXLlOe947Aag5zdIcKQULAiF9Cq4WxBi9jUs5zkA84bYQ==" }, "mime-types": { - "version": "2.1.24", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", - "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", + "version": "2.1.25", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.25.tgz", + "integrity": "sha512-5KhStqB5xpTAeGqKBAMgwaYMnQik7teQN4IAzC7npDv6kzeU6prfkR67bc87J1kWMPGkoaZSq1npmexMgkmEVg==", "requires": { - "mime-db": "1.40.0" + "mime-db": "1.42.0" } }, "mimic-fn": { @@ -8636,6 +7733,7 @@ "version": "2.9.0", "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -8644,7 +7742,8 @@ "yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "optional": true } } }, @@ -8662,6 +7761,7 @@ "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", "dev": true, + "optional": true, "requires": { "for-in": "^1.0.2", "is-extendable": "^1.0.1" @@ -8672,6 +7772,7 @@ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "dev": true, + "optional": true, "requires": { "is-plain-object": "^2.0.4" } @@ -8680,7 +7781,7 @@ }, "mkdirp": { "version": "0.5.1", - "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "requires": { "minimist": "0.0.8" @@ -8865,7 +7966,7 @@ }, "lodash": { "version": "3.10.1", - "resolved": "http://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=", "dev": true }, @@ -8877,7 +7978,7 @@ }, "mkdirp": { "version": "0.5.0", - "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.0.tgz", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.0.tgz", "integrity": "sha1-HXMHam35hs2TROFecfzAWkyavxI=", "dev": true, "requires": { @@ -8892,7 +7993,7 @@ }, "rimraf": { "version": "2.2.6", - "resolved": "http://registry.npmjs.org/rimraf/-/rimraf-2.2.6.tgz", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.6.tgz", "integrity": "sha1-xZWXVpsU2VatKcrMQr3d9fDqT0w=", "dev": true } @@ -8934,7 +8035,7 @@ }, "jsonfile": { "version": "2.4.0", - "resolved": "http://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", "dev": true, "requires": { @@ -9072,7 +8173,6 @@ "version": "2.14.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", - "dev": true, "optional": true }, "nanomatch": { @@ -9080,6 +8180,7 @@ "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", "dev": true, + "optional": true, "requires": { "arr-diff": "^4.0.0", "array-unique": "^0.3.2", @@ -9360,7 +8461,7 @@ "dependencies": { "find-up": { "version": "3.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", "dev": true, "requires": { @@ -9369,7 +8470,7 @@ }, "locate-path": { "version": "3.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", "dev": true, "requires": { @@ -9379,7 +8480,7 @@ }, "p-locate": { "version": "3.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", "dev": true, "requires": { @@ -9388,13 +8489,13 @@ }, "path-exists": { "version": "3.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", "dev": true }, "resolve-from": { "version": "4.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true } @@ -9415,6 +8516,7 @@ "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", "dev": true, + "optional": true, "requires": { "copy-descriptor": "^0.1.0", "define-property": "^0.2.5", @@ -9426,6 +8528,7 @@ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, + "optional": true, "requires": { "is-descriptor": "^0.1.0" } @@ -9435,6 +8538,7 @@ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, + "optional": true, "requires": { "is-buffer": "^1.1.5" } @@ -9447,9 +8551,9 @@ "integrity": "sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA==" }, "object-inspect": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.6.0.tgz", - "integrity": "sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ==" + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", + "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==" }, "object-keys": { "version": "1.1.1", @@ -9466,6 +8570,7 @@ "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", "dev": true, + "optional": true, "requires": { "isobject": "^3.0.0" } @@ -9496,6 +8601,7 @@ "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", "dev": true, + "optional": true, "requires": { "isobject": "^3.0.1" } @@ -9791,7 +8897,8 @@ "version": "0.1.1", "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", - "dev": true + "dev": true, + "optional": true }, "path-dirname": { "version": "1.0.2", @@ -9812,9 +8919,9 @@ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "path-key": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.0.tgz", - "integrity": "sha512-8cChqz0RP6SHJkMt48FW0A7+qUOn+OsnOsVtzI59tZ8m+5bCSk7hzwET0pulwOM2YMn9J1efb07KB9l9f30SGg==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true }, "path-parse": { @@ -9987,7 +9094,8 @@ "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=", - "dev": true + "dev": true, + "optional": true }, "postgres-array": { "version": "2.0.0", @@ -10120,9 +9228,9 @@ "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" }, "psl": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.4.0.tgz", - "integrity": "sha512-HZzqCGPecFLyoRj5HLfuDSKYTJkAfB5thKBIkRHtGjWwY7p1dAyveIbXIq4tO0KYfDF2tHqPUgY9SDnGm00uFw==" + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.5.0.tgz", + "integrity": "sha512-4vqUjKi2huMu1OJiLhi3jN6jeeKvMZdI1tYgi/njW5zV52jNLgSAZSdN16m9bJFe61/cT8ulmw4qFitV9QRsEA==" }, "pump": { "version": "3.0.0", @@ -10283,7 +9391,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "requires": { "core-util-is": "~1.0.0", @@ -10368,6 +9476,7 @@ "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", "dev": true, + "optional": true, "requires": { "extend-shallow": "^3.0.2", "safe-regex": "^1.1.0" @@ -10436,13 +9545,15 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", - "dev": true + "dev": true, + "optional": true }, "repeat-string": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", - "dev": true + "dev": true, + "optional": true }, "request": { "version": "2.88.0", @@ -10521,9 +9632,9 @@ } }, "resolve": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", - "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==", + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.13.1.tgz", + "integrity": "sha512-CxqObCX8K8YtAhOBRg+lrcdn+LK+WYOS8tSjqSFbjtrI5PnS63QPhZl4+yKfrU9tdsbMu9Anr/amegT87M9Z6w==", "dev": true, "requires": { "path-parse": "^1.0.6" @@ -10538,7 +9649,8 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", - "dev": true + "dev": true, + "optional": true }, "restore-cursor": { "version": "3.1.0", @@ -10554,7 +9666,8 @@ "version": "0.1.15", "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", - "dev": true + "dev": true, + "optional": true }, "retry": { "version": "0.12.0", @@ -10621,6 +9734,7 @@ "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", "dev": true, + "optional": true, "requires": { "ret": "~0.1.10" } @@ -10656,7 +9770,7 @@ "dependencies": { "commander": { "version": "2.8.1", - "resolved": "http://registry.npmjs.org/commander/-/commander-2.8.1.tgz", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz", "integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=", "dev": true, "requires": { @@ -10744,6 +9858,7 @@ "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", "dev": true, + "optional": true, "requires": { "extend-shallow": "^2.0.1", "is-extendable": "^0.1.1", @@ -10756,6 +9871,7 @@ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, + "optional": true, "requires": { "is-extendable": "^0.1.0" } @@ -10839,6 +9955,7 @@ "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", "dev": true, + "optional": true, "requires": { "base": "^0.11.1", "debug": "^2.2.0", @@ -10855,6 +9972,7 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, + "optional": true, "requires": { "ms": "2.0.0" } @@ -10864,6 +9982,7 @@ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, + "optional": true, "requires": { "is-descriptor": "^0.1.0" } @@ -10873,6 +9992,7 @@ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, + "optional": true, "requires": { "is-extendable": "^0.1.0" } @@ -10881,13 +10001,15 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true + "dev": true, + "optional": true }, "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true + "dev": true, + "optional": true } } }, @@ -10896,6 +10018,7 @@ "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", "dev": true, + "optional": true, "requires": { "define-property": "^1.0.0", "isobject": "^3.0.0", @@ -10907,6 +10030,7 @@ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, + "optional": true, "requires": { "is-descriptor": "^1.0.0" } @@ -10916,6 +10040,7 @@ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, + "optional": true, "requires": { "kind-of": "^6.0.0" } @@ -10925,6 +10050,7 @@ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, + "optional": true, "requires": { "kind-of": "^6.0.0" } @@ -10934,6 +10060,7 @@ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, + "optional": true, "requires": { "is-accessor-descriptor": "^1.0.0", "is-data-descriptor": "^1.0.0", @@ -10947,6 +10074,7 @@ "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", "dev": true, + "optional": true, "requires": { "kind-of": "^3.2.0" }, @@ -10956,6 +10084,7 @@ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, + "optional": true, "requires": { "is-buffer": "^1.1.5" } @@ -11018,6 +10147,7 @@ "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", "dev": true, + "optional": true, "requires": { "atob": "^2.1.1", "decode-uri-component": "^0.2.0", @@ -11030,7 +10160,8 @@ "version": "0.4.0", "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", - "dev": true + "dev": true, + "optional": true }, "sparse-bitfield": { "version": "3.0.3", @@ -11117,6 +10248,7 @@ "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", "dev": true, + "optional": true, "requires": { "extend-shallow": "^3.0.0" } @@ -11152,6 +10284,7 @@ "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", "dev": true, + "optional": true, "requires": { "define-property": "^0.2.5", "object-copy": "^0.1.0" @@ -11162,6 +10295,7 @@ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, + "optional": true, "requires": { "is-descriptor": "^0.1.0" } @@ -11454,7 +10588,7 @@ }, "through": { "version": "2.3.8", - "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" }, "through2": { @@ -11475,7 +10609,7 @@ }, "readable-stream": { "version": "1.0.34", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, "requires": { @@ -11549,6 +10683,7 @@ "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", "dev": true, + "optional": true, "requires": { "kind-of": "^3.0.2" }, @@ -11558,6 +10693,7 @@ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, + "optional": true, "requires": { "is-buffer": "^1.1.5" } @@ -11569,6 +10705,7 @@ "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", "dev": true, + "optional": true, "requires": { "define-property": "^2.0.2", "extend-shallow": "^3.0.2", @@ -11581,6 +10718,7 @@ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", "dev": true, + "optional": true, "requires": { "is-number": "^3.0.0", "repeat-string": "^1.6.1" @@ -11778,6 +10916,7 @@ "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", "dev": true, + "optional": true, "requires": { "arr-union": "^3.1.0", "get-value": "^2.0.6", @@ -11801,6 +10940,7 @@ "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", "dev": true, + "optional": true, "requires": { "has-value": "^0.3.1", "isobject": "^3.0.0" @@ -11811,6 +10951,7 @@ "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", "dev": true, + "optional": true, "requires": { "get-value": "^2.0.3", "has-values": "^0.1.4", @@ -11822,6 +10963,7 @@ "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", "dev": true, + "optional": true, "requires": { "isarray": "1.0.0" } @@ -11832,7 +10974,8 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", - "dev": true + "dev": true, + "optional": true } } }, @@ -11861,7 +11004,8 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", - "dev": true + "dev": true, + "optional": true }, "url": { "version": "0.10.3", @@ -11898,7 +11042,8 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", - "dev": true + "dev": true, + "optional": true }, "user-home": { "version": "1.1.1", @@ -11985,9 +11130,9 @@ } }, "which": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.1.tgz", - "integrity": "sha512-N7GBZOTswtB9lkQBZA4+zAXrjEIWAUOB93AvzUiudRzRxhUdLURQ7D/gAIMY1gatT/LTbmbcv8SiYazy3eYB7w==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "requires": { "isexe": "^2.0.0" @@ -12333,9 +11478,9 @@ } }, "zen-observable": { - "version": "0.8.14", - "resolved": "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.14.tgz", - "integrity": "sha512-kQz39uonEjEESwh+qCi83kcC3rZJGh4mrZW7xjkSQYXkq//JZHTtKo+6yuVloTgMtzsIWOJrjIrKvk/dqm0L5g==" + "version": "0.8.15", + "resolved": "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.15.tgz", + "integrity": "sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==" }, "zen-observable-ts": { "version": "0.8.20", diff --git a/package.json b/package.json index 60e7cd79df..e7353d29a0 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "follow-redirects": "1.9.0", "graphql": "14.5.8", "graphql-list-fields": "2.0.2", + "graphql-relay": "^0.6.0", "graphql-tools": "^4.0.5", "graphql-upload": "8.1.0", "intersect": "1.0.1", diff --git a/spec/AuthenticationAdapters.spec.js b/spec/AuthenticationAdapters.spec.js index 4ba75eb081..2636405a07 100644 --- a/spec/AuthenticationAdapters.spec.js +++ b/spec/AuthenticationAdapters.spec.js @@ -54,8 +54,14 @@ describe('AuthenticationProviders', function() { Promise.prototype.constructor ); jequal(validateAppIdPromise.constructor, Promise.prototype.constructor); - validateAuthDataPromise.then(() => {}, () => {}); - validateAppIdPromise.then(() => {}, () => {}); + validateAuthDataPromise.then( + () => {}, + () => {} + ); + validateAppIdPromise.then( + () => {}, + () => {} + ); done(); }); diff --git a/spec/MongoTransform.spec.js b/spec/MongoTransform.spec.js index e0ce0cf878..c62a2a989f 100644 --- a/spec/MongoTransform.spec.js +++ b/spec/MongoTransform.spec.js @@ -97,7 +97,11 @@ describe('parseObjectToMongoObjectForCreate', () => { const lng3 = 65; const polygon = { __type: 'Polygon', - coordinates: [[lat1, lng1], [lat2, lng2], [lat3, lng3]], + coordinates: [ + [lat1, lng1], + [lat2, lng2], + [lat3, lng3], + ], }; const out = transform.parseObjectToMongoObjectForCreate( null, @@ -107,7 +111,12 @@ describe('parseObjectToMongoObjectForCreate', () => { } ); expect(out.location.coordinates).toEqual([ - [[lng1, lat1], [lng2, lat2], [lng3, lat3], [lng1, lat1]], + [ + [lng1, lat1], + [lng2, lat2], + [lng3, lat3], + [lng1, lat1], + ], ]); done(); }); @@ -217,7 +226,15 @@ describe('parseObjectToMongoObjectForCreate', () => { const lng = 45; // Mongo stores polygon in WGS84 lng/lat const input = { - location: { type: 'Polygon', coordinates: [[[lat, lng], [lat, lng]]] }, + location: { + type: 'Polygon', + coordinates: [ + [ + [lat, lng], + [lat, lng], + ], + ], + }, }; const output = transform.mongoObjectToParseObject(null, input, { fields: { location: { type: 'Polygon' } }, @@ -225,7 +242,10 @@ describe('parseObjectToMongoObjectForCreate', () => { expect(typeof output.location).toEqual('object'); expect(output.location).toEqual({ __type: 'Polygon', - coordinates: [[lng, lat], [lng, lat]], + coordinates: [ + [lng, lat], + [lng, lat], + ], }); done(); }); diff --git a/spec/ParseGraphQLServer.spec.js b/spec/ParseGraphQLServer.spec.js index 150af4e006..788127da03 100644 --- a/spec/ParseGraphQLServer.spec.js +++ b/spec/ParseGraphQLServer.spec.js @@ -17,6 +17,7 @@ const gql = require('graphql-tag'); const { ParseServer } = require('../'); const { ParseGraphQLServer } = require('../lib/GraphQL/ParseGraphQLServer'); const ReadPreference = require('mongodb').ReadPreference; +const uuidv4 = require('uuid/v4'); function handleError(e) { if ( @@ -260,6 +261,7 @@ describe('ParseGraphQLServer', () => { describe('Auto API', () => { let httpServer; + let parseLiveQueryServer; const headers = { 'X-Parse-Application-Id': 'test', 'X-Parse-Javascript-Key': 'test', @@ -412,7 +414,7 @@ describe('ParseGraphQLServer', () => { const expressApp = express(); httpServer = http.createServer(expressApp); expressApp.use('/parse', parseServer.app); - ParseServer.createLiveQueryServer(httpServer, { + parseLiveQueryServer = ParseServer.createLiveQueryServer(httpServer, { port: 1338, }); parseGraphQLServer.applyGraphQL(expressApp); @@ -455,6 +457,7 @@ describe('ParseGraphQLServer', () => { }); afterAll(async () => { + await parseLiveQueryServer.server.close(); await httpServer.close(); }); @@ -544,6 +547,13 @@ describe('ParseGraphQLServer', () => { }); describe('Schema', () => { + const resetGraphQLCache = async () => { + await Promise.all([ + parseGraphQLServer.parseGraphQLController.cacheController.graphQL.clear(), + parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear(), + ]); + }; + describe('Default Types', () => { it('should have Object scalar type', async () => { const objectType = (await apolloClient.query({ @@ -621,7 +631,7 @@ describe('ParseGraphQLServer', () => { expect(classType.fields.map(field => field.name).sort()).toEqual([ 'ACL', 'createdAt', - 'id', + 'objectId', 'updatedAt', ]); }); @@ -651,26 +661,6 @@ describe('ParseGraphQLServer', () => { ]); }); - it('should have FindResult object type', async () => { - const findResultType = (await apolloClient.query({ - query: gql` - query FindResultType { - __type(name: "FindResult") { - kind - fields { - name - } - } - } - `, - })).data['__type']; - expect(findResultType.kind).toEqual('OBJECT'); - expect(findResultType.fields.map(name => name.name).sort()).toEqual([ - 'count', - 'results', - ]); - }); - it('should have GraphQLUpload object type', async () => { const graphQLUploadType = (await apolloClient.query({ query: gql` @@ -704,7 +694,6 @@ describe('ParseGraphQLServer', () => { 'ParseObject', 'Date', 'FileInfo', - 'FindResult', 'ReadPreference', 'Upload', ]; @@ -714,10 +703,16 @@ describe('ParseGraphQLServer', () => { }); }); - describe('Parse Class Types', () => { - it('should have all expected types', async () => { - await parseServer.config.databaseController.loadSchema(); + describe('Relay Specific Types', () => { + beforeAll(async () => { + await resetGraphQLCache(); + }); + afterAll(async () => { + await resetGraphQLCache(); + }); + + it('should have Node interface', async () => { const schemaTypes = (await apolloClient.query({ query: gql` query SchemaTypes { @@ -730,49 +725,26 @@ describe('ParseGraphQLServer', () => { `, })).data['__schema'].types.map(type => type.name); - const expectedTypes = [ - 'Role', - 'RoleWhereInput', - 'CreateRoleFieldsInput', - 'UpdateRoleFieldsInput', - 'RoleFindResult', - 'User', - 'UserWhereInput', - 'UserFindResult', - 'SignUpFieldsInput', - 'CreateUserFieldsInput', - 'UpdateUserFieldsInput', - ]; - expect( - expectedTypes.every(type => schemaTypes.indexOf(type) !== -1) - ).toBeTruthy(JSON.stringify(schemaTypes)); + expect(schemaTypes).toContain('Node'); }); - it('should ArrayResult contains all types', async () => { - const objectType = (await apolloClient.query({ + it('should have node query', async () => { + const queryFields = (await apolloClient.query({ query: gql` - query ObjectType { - __type(name: "ArrayResult") { - kind - possibleTypes { + query UserType { + __type(name: "Query") { + fields { name } } } `, - })).data['__type']; - const possibleTypes = objectType.possibleTypes.map(o => o.name); - expect(possibleTypes).toContain('User'); - expect(possibleTypes).toContain('Role'); - expect(possibleTypes).toContain('Element'); - }); + })).data['__type'].fields.map(field => field.name); - it('should update schema when it changes', async () => { - const schemaController = await parseServer.config.databaseController.loadSchema(); - await schemaController.updateClass('_User', { - foo: { type: 'String' }, - }); + expect(queryFields).toContain('node'); + }); + it('should return global id', async () => { const userFields = (await apolloClient.query({ query: gql` query UserType { @@ -784,479 +756,695 @@ describe('ParseGraphQLServer', () => { } `, })).data['__type'].fields.map(field => field.name); - expect(userFields.indexOf('foo') !== -1).toBeTruthy(); + + expect(userFields).toContain('id'); + expect(userFields).toContain('objectId'); }); - it('should not contain password field from _User class', async () => { - const userFields = (await apolloClient.query({ + it('should have clientMutationId in create file input', async () => { + const createFileInputFields = (await apolloClient.query({ query: gql` - query UserType { - __type(name: "User") { + query { + __type(name: "CreateFileInput") { + inputFields { + name + } + } + } + `, + })).data['__type'].inputFields + .map(field => field.name) + .sort(); + + expect(createFileInputFields).toEqual(['clientMutationId', 'upload']); + }); + + it('should have clientMutationId in create file payload', async () => { + const createFilePayloadFields = (await apolloClient.query({ + query: gql` + query { + __type(name: "CreateFilePayload") { fields { name } } } `, - })).data['__type'].fields.map(field => field.name); - expect(userFields.includes('password')).toBeFalsy(); + })).data['__type'].fields + .map(field => field.name) + .sort(); + + expect(createFilePayloadFields).toEqual([ + 'clientMutationId', + 'fileInfo', + ]); }); - }); - describe('Configuration', function() { - const resetGraphQLCache = async () => { - await Promise.all([ - parseGraphQLServer.parseGraphQLController.cacheController.graphQL.clear(), - parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear(), + it('should have clientMutationId in call function input', async () => { + Parse.Cloud.define('hello', () => {}); + + const callFunctionInputFields = (await apolloClient.query({ + query: gql` + query { + __type(name: "CallCloudCodeInput") { + inputFields { + name + } + } + } + `, + })).data['__type'].inputFields + .map(field => field.name) + .sort(); + + expect(callFunctionInputFields).toEqual([ + 'clientMutationId', + 'functionName', + 'params', ]); - }; + }); - beforeEach(async () => { - await parseGraphQLServer.setGraphQLConfig({}); - await resetGraphQLCache(); + it('should have clientMutationId in call function payload', async () => { + Parse.Cloud.define('hello', () => {}); + + const callFunctionPayloadFields = (await apolloClient.query({ + query: gql` + query { + __type(name: "CallCloudCodePayload") { + fields { + name + } + } + } + `, + })).data['__type'].fields + .map(field => field.name) + .sort(); + + expect(callFunctionPayloadFields).toEqual([ + 'clientMutationId', + 'result', + ]); }); - it('should only include types in the enabledForClasses list', async () => { - const schemaController = await parseServer.config.databaseController.loadSchema(); - await schemaController.addClassIfNotExists('SuperCar', { - foo: { type: 'String' }, - }); + it('should have clientMutationId in sign up mutation input', async () => { + const inputFields = (await apolloClient.query({ + query: gql` + query { + __type(name: "SignUpInput") { + inputFields { + name + } + } + } + `, + })).data['__type'].inputFields + .map(field => field.name) + .sort(); - const graphQLConfig = { - enabledForClasses: ['SuperCar'], - }; - await parseGraphQLServer.setGraphQLConfig(graphQLConfig); - await resetGraphQLCache(); + expect(inputFields).toEqual(['clientMutationId', 'userFields']); + }); - const { data } = await apolloClient.query({ + it('should have clientMutationId in sign up mutation payload', async () => { + const payloadFields = (await apolloClient.query({ query: gql` - query UserType { - userType: __type(name: "User") { + query { + __type(name: "SignUpPayload") { fields { name } } - superCarType: __type(name: "SuperCar") { + } + `, + })).data['__type'].fields + .map(field => field.name) + .sort(); + + expect(payloadFields).toEqual(['clientMutationId', 'viewer']); + }); + + it('should have clientMutationId in log in mutation input', async () => { + const inputFields = (await apolloClient.query({ + query: gql` + query { + __type(name: "LogInInput") { + inputFields { + name + } + } + } + `, + })).data['__type'].inputFields + .map(field => field.name) + .sort(); + + expect(inputFields).toEqual([ + 'clientMutationId', + 'password', + 'username', + ]); + }); + + it('should have clientMutationId in log in mutation payload', async () => { + const payloadFields = (await apolloClient.query({ + query: gql` + query { + __type(name: "LogInPayload") { fields { name } } } `, - }); - expect(data.userType).toBeNull(); - expect(data.superCarType).toBeTruthy(); + })).data['__type'].fields + .map(field => field.name) + .sort(); + + expect(payloadFields).toEqual(['clientMutationId', 'viewer']); }); - it('should not include types in the disabledForClasses list', async () => { - const schemaController = await parseServer.config.databaseController.loadSchema(); - await schemaController.addClassIfNotExists('SuperCar', { - foo: { type: 'String' }, - }); - const graphQLConfig = { - disabledForClasses: ['SuperCar'], - }; - await parseGraphQLServer.setGraphQLConfig(graphQLConfig); - await resetGraphQLCache(); + it('should have clientMutationId in log out mutation input', async () => { + const inputFields = (await apolloClient.query({ + query: gql` + query { + __type(name: "LogOutInput") { + inputFields { + name + } + } + } + `, + })).data['__type'].inputFields + .map(field => field.name) + .sort(); - const { data } = await apolloClient.query({ + expect(inputFields).toEqual(['clientMutationId']); + }); + + it('should have clientMutationId in log out mutation payload', async () => { + const payloadFields = (await apolloClient.query({ query: gql` - query UserType { - userType: __type(name: "User") { + query { + __type(name: "LogOutPayload") { fields { name } } - superCarType: __type(name: "SuperCar") { + } + `, + })).data['__type'].fields + .map(field => field.name) + .sort(); + + expect(payloadFields).toEqual(['clientMutationId', 'viewer']); + }); + + it('should have clientMutationId in createClass mutation input', async () => { + const inputFields = (await apolloClient.query({ + query: gql` + query { + __type(name: "CreateClassInput") { + inputFields { + name + } + } + } + `, + })).data['__type'].inputFields + .map(field => field.name) + .sort(); + + expect(inputFields).toEqual([ + 'clientMutationId', + 'name', + 'schemaFields', + ]); + }); + + it('should have clientMutationId in createClass mutation payload', async () => { + const payloadFields = (await apolloClient.query({ + query: gql` + query { + __type(name: "CreateClassPayload") { fields { name } } } `, - }); - expect(data.superCarType).toBeNull(); - expect(data.userType).toBeTruthy(); + })).data['__type'].fields + .map(field => field.name) + .sort(); + + expect(payloadFields).toEqual(['class', 'clientMutationId']); }); - it('should remove query operations when disabled', async () => { - const superCar = new Parse.Object('SuperCar'); - await superCar.save({ foo: 'bar' }); - const customer = new Parse.Object('Customer'); - await customer.save({ foo: 'bar' }); - await expectAsync( - apolloClient.query({ - query: gql` - query GetSuperCar($id: ID!) { - superCar(id: $id) { - id + it('should have clientMutationId in updateClass mutation input', async () => { + const inputFields = (await apolloClient.query({ + query: gql` + query { + __type(name: "UpdateClassInput") { + inputFields { + name } } - `, - variables: { - id: superCar.id, - }, - }) - ).toBeResolved(); + } + `, + })).data['__type'].inputFields + .map(field => field.name) + .sort(); - await expectAsync( - apolloClient.query({ - query: gql` - query FindCustomer { - customers { - count + expect(inputFields).toEqual([ + 'clientMutationId', + 'name', + 'schemaFields', + ]); + }); + + it('should have clientMutationId in updateClass mutation payload', async () => { + const payloadFields = (await apolloClient.query({ + query: gql` + query { + __type(name: "UpdateClassPayload") { + fields { + name } } - `, - }) - ).toBeResolved(); + } + `, + })).data['__type'].fields + .map(field => field.name) + .sort(); - const graphQLConfig = { - classConfigs: [ - { - className: 'SuperCar', - query: { - get: false, - find: true, - }, - }, - { - className: 'Customer', - query: { - get: true, - find: false, - }, - }, - ], - }; - await parseGraphQLServer.setGraphQLConfig(graphQLConfig); - await resetGraphQLCache(); + expect(payloadFields).toEqual(['class', 'clientMutationId']); + }); - await expectAsync( - apolloClient.query({ - query: gql` - query GetSuperCar($id: ID!) { - superCar(id: $id) { - id + it('should have clientMutationId in deleteClass mutation input', async () => { + const inputFields = (await apolloClient.query({ + query: gql` + query { + __type(name: "DeleteClassInput") { + inputFields { + name } } - `, - variables: { - id: superCar.id, - }, - }) - ).toBeRejected(); - await expectAsync( - apolloClient.query({ - query: gql` - query GetCustomer($id: ID!) { - customer(id: $id) { - id - } - } - `, - variables: { - id: customer.id, - }, - }) - ).toBeResolved(); - await expectAsync( - apolloClient.query({ - query: gql` - query FindSuperCar { - superCars { - count + } + `, + })).data['__type'].inputFields + .map(field => field.name) + .sort(); + + expect(inputFields).toEqual(['clientMutationId', 'name']); + }); + + it('should have clientMutationId in deleteClass mutation payload', async () => { + const payloadFields = (await apolloClient.query({ + query: gql` + query { + __type(name: "UpdateClassPayload") { + fields { + name } } - `, - }) - ).toBeResolved(); - await expectAsync( - apolloClient.query({ - query: gql` - query FindCustomer { - customers { - count + } + `, + })).data['__type'].fields + .map(field => field.name) + .sort(); + + expect(payloadFields).toEqual(['class', 'clientMutationId']); + }); + + it('should have clientMutationId in custom create object mutation input', async () => { + const obj = new Parse.Object('SomeClass'); + await obj.save(); + + await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear(); + + const createObjectInputFields = (await apolloClient.query({ + query: gql` + query { + __type(name: "CreateSomeClassInput") { + inputFields { + name } } - `, - }) - ).toBeRejected(); + } + `, + })).data['__type'].inputFields + .map(field => field.name) + .sort(); + + expect(createObjectInputFields).toEqual([ + 'clientMutationId', + 'fields', + ]); }); - it('should remove mutation operations, create, update and delete, when disabled', async () => { - const superCar1 = new Parse.Object('SuperCar'); - await superCar1.save({ foo: 'bar' }); - const customer1 = new Parse.Object('Customer'); - await customer1.save({ foo: 'bar' }); + it('should have clientMutationId in custom create object mutation payload', async () => { + const obj = new Parse.Object('SomeClass'); + await obj.save(); - await expectAsync( - apolloClient.query({ - query: gql` - mutation UpdateSuperCar($id: ID!, $foo: String!) { - updateSuperCar(id: $id, fields: { foo: $foo }) { - updatedAt + await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear(); + + const createObjectPayloadFields = (await apolloClient.query({ + query: gql` + query { + __type(name: "CreateSomeClassPayload") { + fields { + name } } - `, - variables: { - id: superCar1.id, - foo: 'lah', - }, - }) - ).toBeResolved(); + } + `, + })).data['__type'].fields + .map(field => field.name) + .sort(); - await expectAsync( - apolloClient.query({ - query: gql` - mutation DeleteCustomer($id: ID!) { - deleteCustomer(id: $id) { - id + expect(createObjectPayloadFields).toEqual([ + 'clientMutationId', + 'someClass', + ]); + }); + + it('should have clientMutationId in custom update object mutation input', async () => { + const obj = new Parse.Object('SomeClass'); + await obj.save(); + + await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear(); + + const createObjectInputFields = (await apolloClient.query({ + query: gql` + query { + __type(name: "UpdateSomeClassInput") { + inputFields { + name } } - `, - variables: { - id: customer1.id, - }, - }) - ).toBeResolved(); + } + `, + })).data['__type'].inputFields + .map(field => field.name) + .sort(); - const { data: customerData } = await apolloClient.query({ + expect(createObjectInputFields).toEqual([ + 'clientMutationId', + 'fields', + 'id', + ]); + }); + + it('should have clientMutationId in custom update object mutation payload', async () => { + const obj = new Parse.Object('SomeClass'); + await obj.save(); + + await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear(); + + const createObjectPayloadFields = (await apolloClient.query({ query: gql` - mutation CreateCustomer($foo: String!) { - createCustomer(fields: { foo: $foo }) { - id + query { + __type(name: "UpdateSomeClassPayload") { + fields { + name + } } } `, - variables: { - foo: 'rah', - }, - }); - expect(customerData.createCustomer).toBeTruthy(); + })).data['__type'].fields + .map(field => field.name) + .sort(); - // used later - const customer2Id = customerData.createCustomer.id; + expect(createObjectPayloadFields).toEqual([ + 'clientMutationId', + 'someClass', + ]); + }); - await parseGraphQLServer.setGraphQLConfig({ - classConfigs: [ - { - className: 'SuperCar', - mutation: { - create: true, - update: false, - destroy: true, - }, - }, - { - className: 'Customer', - mutation: { - create: false, - update: true, - destroy: false, - }, - }, - ], - }); - await resetGraphQLCache(); + it('should have clientMutationId in custom delete object mutation input', async () => { + const obj = new Parse.Object('SomeClass'); + await obj.save(); - const { data: superCarData } = await apolloClient.query({ + await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear(); + + const createObjectInputFields = (await apolloClient.query({ query: gql` - mutation CreateSuperCar($foo: String!) { - createSuperCar(fields: { foo: $foo }) { - id + query { + __type(name: "DeleteSomeClassInput") { + inputFields { + name + } } } `, - variables: { - foo: 'mah', - }, - }); - expect(superCarData.createSuperCar).toBeTruthy(); - const superCar3Id = superCarData.createSuperCar.id; + })).data['__type'].inputFields + .map(field => field.name) + .sort(); - await expectAsync( - apolloClient.query({ - query: gql` - mutation UpdateSupercar($id: ID!, $foo: String!) { - updateSuperCar(id: $id, fields: { foo: $foo }) { - updatedAt + expect(createObjectInputFields).toEqual(['clientMutationId', 'id']); + }); + + it('should have clientMutationId in custom delete object mutation payload', async () => { + const obj = new Parse.Object('SomeClass'); + await obj.save(); + + await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear(); + + const createObjectPayloadFields = (await apolloClient.query({ + query: gql` + query { + __type(name: "DeleteSomeClassPayload") { + fields { + name } } - `, - variables: { - id: superCar3Id, - }, - }) - ).toBeRejected(); + } + `, + })).data['__type'].fields + .map(field => field.name) + .sort(); - await expectAsync( - apolloClient.query({ - query: gql` - mutation DeleteSuperCar($id: ID!) { - deleteSuperCar(id: $id) { - id + expect(createObjectPayloadFields).toEqual([ + 'clientMutationId', + 'someClass', + ]); + }); + }); + + describe('Parse Class Types', () => { + it('should have all expected types', async () => { + await parseServer.config.databaseController.loadSchema(); + + const schemaTypes = (await apolloClient.query({ + query: gql` + query SchemaTypes { + __schema { + types { + name } } - `, - variables: { - id: superCar3Id, - }, - }) - ).toBeResolved(); + } + `, + })).data['__schema'].types.map(type => type.name); - await expectAsync( - apolloClient.query({ - query: gql` - mutation CreateCustomer($foo: String!) { - createCustomer(fields: { foo: $foo }) { - id + const expectedTypes = [ + 'Role', + 'RoleWhereInput', + 'CreateRoleFieldsInput', + 'UpdateRoleFieldsInput', + 'RoleConnection', + 'User', + 'UserWhereInput', + 'UserConnection', + 'CreateUserFieldsInput', + 'UpdateUserFieldsInput', + ]; + expect( + expectedTypes.every(type => schemaTypes.indexOf(type) !== -1) + ).toBeTruthy(JSON.stringify(schemaTypes)); + }); + + it('should ArrayResult contains all types', async () => { + const objectType = (await apolloClient.query({ + query: gql` + query ObjectType { + __type(name: "ArrayResult") { + kind + possibleTypes { + name } } - `, - variables: { - foo: 'rah', - }, - }) - ).toBeRejected(); - await expectAsync( - apolloClient.query({ - query: gql` - mutation UpdateCustomer($id: ID!, $foo: String!) { - updateCustomer(id: $id, fields: { foo: $foo }) { - updatedAt + } + `, + })).data['__type']; + const possibleTypes = objectType.possibleTypes.map(o => o.name); + expect(possibleTypes).toContain('User'); + expect(possibleTypes).toContain('Role'); + expect(possibleTypes).toContain('Element'); + }); + + it('should update schema when it changes', async () => { + const schemaController = await parseServer.config.databaseController.loadSchema(); + await schemaController.updateClass('_User', { + foo: { type: 'String' }, + }); + + const userFields = (await apolloClient.query({ + query: gql` + query UserType { + __type(name: "User") { + fields { + name } } - `, - variables: { - id: customer2Id, - foo: 'tah', - }, - }) - ).toBeResolved(); - await expectAsync( - apolloClient.query({ - query: gql` - mutation DeleteCustomer($id: ID!, $foo: String!) { - deleteCustomer(id: $id) + } + `, + })).data['__type'].fields.map(field => field.name); + expect(userFields.indexOf('foo') !== -1).toBeTruthy(); + }); + + it('should not contain password field from _User class', async () => { + const userFields = (await apolloClient.query({ + query: gql` + query UserType { + __type(name: "User") { + fields { + name + } } - `, - variables: { - id: customer2Id, - }, - }) - ).toBeRejected(); + } + `, + })).data['__type'].fields.map(field => field.name); + expect(userFields.includes('password')).toBeFalsy(); }); - it('should only allow the supplied create and update fields for a class', async () => { + }); + + describe('Configuration', function() { + const resetGraphQLCache = async () => { + await Promise.all([ + parseGraphQLServer.parseGraphQLController.cacheController.graphQL.clear(), + parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear(), + ]); + }; + + beforeEach(async () => { + await parseGraphQLServer.setGraphQLConfig({}); + await resetGraphQLCache(); + }); + + it('should only include types in the enabledForClasses list', async () => { const schemaController = await parseServer.config.databaseController.loadSchema(); await schemaController.addClassIfNotExists('SuperCar', { - engine: { type: 'String' }, - doors: { type: 'Number' }, - price: { type: 'String' }, - mileage: { type: 'Number' }, + foo: { type: 'String' }, }); - await parseGraphQLServer.setGraphQLConfig({ - classConfigs: [ - { - className: 'SuperCar', - type: { - inputFields: { - create: ['engine', 'doors', 'price'], - update: ['price', 'mileage'], - }, - }, - }, - ], + const graphQLConfig = { + enabledForClasses: ['SuperCar'], + }; + await parseGraphQLServer.setGraphQLConfig(graphQLConfig); + await resetGraphQLCache(); + + const { data } = await apolloClient.query({ + query: gql` + query UserType { + userType: __type(name: "User") { + fields { + name + } + } + superCarType: __type(name: "SuperCar") { + fields { + name + } + } + } + `, + }); + expect(data.userType).toBeNull(); + expect(data.superCarType).toBeTruthy(); + }); + it('should not include types in the disabledForClasses list', async () => { + const schemaController = await parseServer.config.databaseController.loadSchema(); + await schemaController.addClassIfNotExists('SuperCar', { + foo: { type: 'String' }, }); + const graphQLConfig = { + disabledForClasses: ['SuperCar'], + }; + await parseGraphQLServer.setGraphQLConfig(graphQLConfig); await resetGraphQLCache(); - await expectAsync( - apolloClient.query({ - query: gql` - mutation InvalidCreateSuperCar { - createSuperCar(fields: { engine: "diesel", mileage: 1000 }) { - id + const { data } = await apolloClient.query({ + query: gql` + query UserType { + userType: __type(name: "User") { + fields { + name } } - `, - }) - ).toBeRejected(); - const { id: superCarId } = (await apolloClient.query({ - query: gql` - mutation ValidCreateSuperCar { - createSuperCar( - fields: { engine: "diesel", doors: 5, price: "£10000" } - ) { - id + superCarType: __type(name: "SuperCar") { + fields { + name + } } } `, - })).data.createSuperCar; - - expect(superCarId).toBeTruthy(); + }); + expect(data.superCarType).toBeNull(); + expect(data.userType).toBeTruthy(); + }); + it('should remove query operations when disabled', async () => { + const superCar = new Parse.Object('SuperCar'); + await superCar.save({ foo: 'bar' }); + const customer = new Parse.Object('Customer'); + await customer.save({ foo: 'bar' }); await expectAsync( apolloClient.query({ query: gql` - mutation InvalidUpdateSuperCar($id: ID!) { - updateSuperCar(id: $id, fields: { engine: "petrol" }) { - updatedAt + query GetSuperCar($id: ID!) { + superCar(id: $id) { + id } } `, variables: { - id: superCarId, + id: superCar.id, }, }) - ).toBeRejected(); + ).toBeResolved(); - const updatedSuperCar = (await apolloClient.query({ - query: gql` - mutation ValidUpdateSuperCar($id: ID!) { - updateSuperCar(id: $id, fields: { mileage: 2000 }) { - updatedAt + await expectAsync( + apolloClient.query({ + query: gql` + query FindCustomer { + customers { + count + } } - } - `, - variables: { - id: superCarId, - }, - })).data.updateSuperCar; - expect(updatedSuperCar).toBeTruthy(); - }); - - it('should only allow the supplied output fields for a class', async () => { - const schemaController = await parseServer.config.databaseController.loadSchema(); - - await schemaController.addClassIfNotExists('SuperCar', { - engine: { type: 'String' }, - doors: { type: 'Number' }, - price: { type: 'String' }, - mileage: { type: 'Number' }, - insuranceClaims: { type: 'Number' }, - }); - - const superCar = await new Parse.Object('SuperCar').save({ - engine: 'petrol', - doors: 3, - price: '£7500', - mileage: 0, - insuranceCertificate: 'private-file.pdf', - }); + `, + }) + ).toBeResolved(); - await parseGraphQLServer.setGraphQLConfig({ + const graphQLConfig = { classConfigs: [ { className: 'SuperCar', - type: { - outputFields: ['engine', 'doors', 'price', 'mileage'], + query: { + get: false, + find: true, + }, + }, + { + className: 'Customer', + query: { + get: true, + find: false, }, }, ], - }); - + }; + await parseGraphQLServer.setGraphQLConfig(graphQLConfig); await resetGraphQLCache(); await expectAsync( @@ -1265,11 +1453,6 @@ describe('ParseGraphQLServer', () => { query GetSuperCar($id: ID!) { superCar(id: $id) { id - engine - doors - price - mileage - insuranceCertificate } } `, @@ -1278,282 +1461,1352 @@ describe('ParseGraphQLServer', () => { }, }) ).toBeRejected(); - let getSuperCar = (await apolloClient.query({ - query: gql` - query GetSuperCar($id: ID!) { - superCar(id: $id) { - id - engine - doors - price - mileage + await expectAsync( + apolloClient.query({ + query: gql` + query GetCustomer($id: ID!) { + customer(id: $id) { + id + } } - } - `, - variables: { - id: superCar.id, - }, - })).data.superCar; - expect(getSuperCar).toBeTruthy(); - - await parseGraphQLServer.setGraphQLConfig({ - classConfigs: [ - { - className: 'SuperCar', - type: { - outputFields: [], - }, + `, + variables: { + id: customer.id, }, - ], - }); - - await resetGraphQLCache(); + }) + ).toBeResolved(); await expectAsync( apolloClient.query({ query: gql` - query GetSuperCar($id: ID!) { - superCar(id: $id) { - engine + query FindSuperCar { + superCars { + count } } `, - variables: { - id: superCar.id, - }, }) - ).toBeRejected(); - getSuperCar = (await apolloClient.query({ - query: gql` - query GetSuperCar($id: ID!) { - superCar(id: $id) { - id + ).toBeResolved(); + await expectAsync( + apolloClient.query({ + query: gql` + query FindCustomer { + customers { + count + } } - } - `, - variables: { - id: superCar.id, - }, - })).data.superCar; - expect(getSuperCar.id).toBe(superCar.id); + `, + }) + ).toBeRejected(); }); - it('should only allow the supplied constraint fields for a class', async () => { - try { - const schemaController = await parseServer.config.databaseController.loadSchema(); - - await schemaController.addClassIfNotExists('SuperCar', { - model: { type: 'String' }, - engine: { type: 'String' }, - doors: { type: 'Number' }, - price: { type: 'String' }, - mileage: { type: 'Number' }, - insuranceCertificate: { type: 'String' }, - }); - - await new Parse.Object('SuperCar').save({ - model: 'McLaren', - engine: 'petrol', - doors: 3, - price: '£7500', - mileage: 0, - insuranceCertificate: 'private-file.pdf', - }); - - await parseGraphQLServer.setGraphQLConfig({ - classConfigs: [ - { - className: 'SuperCar', - type: { - constraintFields: ['engine', 'doors', 'price'], - }, - }, - ], - }); - - await resetGraphQLCache(); + it('should remove mutation operations, create, update and delete, when disabled', async () => { + const superCar1 = new Parse.Object('SuperCar'); + await superCar1.save({ foo: 'bar' }); + const customer1 = new Parse.Object('Customer'); + await customer1.save({ foo: 'bar' }); - await expectAsync( - apolloClient.query({ - query: gql` - query FindSuperCar { - superCars( - where: { - insuranceCertificate: { equalTo: "private-file.pdf" } - } - ) { - count - } + await expectAsync( + apolloClient.query({ + query: gql` + mutation UpdateSuperCar($id: ID!, $foo: String!) { + updateSuperCar(input: { id: $id, fields: { foo: $foo } }) { + clientMutationId } - `, - }) - ).toBeRejected(); + } + `, + variables: { + id: superCar1.id, + foo: 'lah', + }, + }) + ).toBeResolved(); - await expectAsync( - apolloClient.query({ - query: gql` - query FindSuperCar { - superCars(where: { mileage: { equalTo: 0 } }) { - count - } + await expectAsync( + apolloClient.query({ + query: gql` + mutation DeleteCustomer($id: ID!) { + deleteCustomer(input: { id: $id }) { + clientMutationId } - `, - }) - ).toBeRejected(); + } + `, + variables: { + id: customer1.id, + }, + }) + ).toBeResolved(); - await expectAsync( - apolloClient.query({ - query: gql` - query FindSuperCar { - superCars(where: { engine: { equalTo: "petrol" } }) { - count - } + const { data: customerData } = await apolloClient.query({ + query: gql` + mutation CreateCustomer($foo: String!) { + createCustomer(input: { fields: { foo: $foo } }) { + customer { + id } - `, - }) - ).toBeResolved(); - } catch (e) { - handleError(e); - } - }); - - it('should only allow the supplied sort fields for a class', async () => { - const schemaController = await parseServer.config.databaseController.loadSchema(); - - await schemaController.addClassIfNotExists('SuperCar', { - engine: { type: 'String' }, - doors: { type: 'Number' }, - price: { type: 'String' }, - mileage: { type: 'Number' }, + } + } + `, + variables: { + foo: 'rah', + }, }); + expect(customerData.createCustomer.customer).toBeTruthy(); - await new Parse.Object('SuperCar').save({ - engine: 'petrol', - doors: 3, - price: '£7500', - mileage: 0, - }); + // used later + const customer2Id = customerData.createCustomer.customer.id; await parseGraphQLServer.setGraphQLConfig({ classConfigs: [ { className: 'SuperCar', - type: { - sortFields: [ - { - field: 'doors', - asc: true, - desc: true, - }, - { - field: 'price', - asc: true, - desc: true, - }, - { - field: 'mileage', - asc: true, - desc: false, - }, - ], + mutation: { + create: true, + update: false, + destroy: true, + }, + }, + { + className: 'Customer', + mutation: { + create: false, + update: true, + destroy: false, }, }, ], }); - await resetGraphQLCache(); + const { data: superCarData } = await apolloClient.query({ + query: gql` + mutation CreateSuperCar($foo: String!) { + createSuperCar(input: { fields: { foo: $foo } }) { + superCar { + id + } + } + } + `, + variables: { + foo: 'mah', + }, + }); + expect(superCarData.createSuperCar).toBeTruthy(); + const superCar3Id = superCarData.createSuperCar.superCar.id; + await expectAsync( apolloClient.query({ query: gql` - query FindSuperCar { - superCars(order: [engine_ASC]) { - results { - id - } + mutation UpdateSupercar($id: ID!, $foo: String!) { + updateSuperCar(input: { id: $id, fields: { foo: $foo } }) { + clientMutationId } } `, + variables: { + id: superCar3Id, + }, }) ).toBeRejected(); + await expectAsync( apolloClient.query({ query: gql` - query FindSuperCar { - superCars(order: [engine_DESC]) { - results { - id - } + mutation DeleteSuperCar($id: ID!) { + deleteSuperCar(input: { id: $id }) { + clientMutationId } } `, + variables: { + id: superCar3Id, + }, }) - ).toBeRejected(); + ).toBeResolved(); + await expectAsync( apolloClient.query({ query: gql` - query FindSuperCar { - superCars(order: [mileage_DESC]) { - results { + mutation CreateCustomer($foo: String!) { + createCustomer(input: { fields: { foo: $foo } }) { + customer { id } } } `, + variables: { + foo: 'rah', + }, }) ).toBeRejected(); - await expectAsync( apolloClient.query({ query: gql` - query FindSuperCar { - superCars(order: [mileage_ASC]) { - results { - id - } + mutation UpdateCustomer($id: ID!, $foo: String!) { + updateCustomer(input: { id: $id, fields: { foo: $foo } }) { + clientMutationId } } `, + variables: { + id: customer2Id, + foo: 'tah', + }, }) ).toBeResolved(); await expectAsync( apolloClient.query({ query: gql` - query FindSuperCar { - superCars(order: [doors_ASC]) { - results { + mutation DeleteCustomer($id: ID!, $foo: String!) { + deleteCustomer(input: { id: $id }) { + clientMutationId + } + } + `, + variables: { + id: customer2Id, + }, + }) + ).toBeRejected(); + }); + + it('should only allow the supplied create and update fields for a class', async () => { + const schemaController = await parseServer.config.databaseController.loadSchema(); + await schemaController.addClassIfNotExists('SuperCar', { + engine: { type: 'String' }, + doors: { type: 'Number' }, + price: { type: 'String' }, + mileage: { type: 'Number' }, + }); + + await parseGraphQLServer.setGraphQLConfig({ + classConfigs: [ + { + className: 'SuperCar', + type: { + inputFields: { + create: ['engine', 'doors', 'price'], + update: ['price', 'mileage'], + }, + }, + }, + ], + }); + + await resetGraphQLCache(); + + await expectAsync( + apolloClient.query({ + query: gql` + mutation InvalidCreateSuperCar { + createSuperCar( + input: { fields: { engine: "diesel", mileage: 1000 } } + ) { + superCar { id } } } `, }) - ).toBeResolved(); + ).toBeRejected(); + const { id: superCarId } = (await apolloClient.query({ + query: gql` + mutation ValidCreateSuperCar { + createSuperCar( + input: { + fields: { engine: "diesel", doors: 5, price: "£10000" } + } + ) { + superCar { + id + } + } + } + `, + })).data.createSuperCar.superCar; + + expect(superCarId).toBeTruthy(); + await expectAsync( apolloClient.query({ query: gql` - query FindSuperCar { - superCars(order: [price_DESC]) { - results { - id + mutation InvalidUpdateSuperCar($id: ID!) { + updateSuperCar( + input: { id: $id, fields: { engine: "petrol" } } + ) { + clientMutationId + } + } + `, + variables: { + id: superCarId, + }, + }) + ).toBeRejected(); + + const updatedSuperCar = (await apolloClient.query({ + query: gql` + mutation ValidUpdateSuperCar($id: ID!) { + updateSuperCar(input: { id: $id, fields: { mileage: 2000 } }) { + clientMutationId + } + } + `, + variables: { + id: superCarId, + }, + })).data.updateSuperCar; + expect(updatedSuperCar).toBeTruthy(); + }); + + it('should only allow the supplied output fields for a class', async () => { + const schemaController = await parseServer.config.databaseController.loadSchema(); + + await schemaController.addClassIfNotExists('SuperCar', { + engine: { type: 'String' }, + doors: { type: 'Number' }, + price: { type: 'String' }, + mileage: { type: 'Number' }, + insuranceClaims: { type: 'Number' }, + }); + + const superCar = await new Parse.Object('SuperCar').save({ + engine: 'petrol', + doors: 3, + price: '£7500', + mileage: 0, + insuranceCertificate: 'private-file.pdf', + }); + + await parseGraphQLServer.setGraphQLConfig({ + classConfigs: [ + { + className: 'SuperCar', + type: { + outputFields: ['engine', 'doors', 'price', 'mileage'], + }, + }, + ], + }); + + await resetGraphQLCache(); + + await expectAsync( + apolloClient.query({ + query: gql` + query GetSuperCar($id: ID!) { + superCar(id: $id) { + id + objectId + engine + doors + price + mileage + insuranceCertificate + } + } + `, + variables: { + id: superCar.id, + }, + }) + ).toBeRejected(); + let getSuperCar = (await apolloClient.query({ + query: gql` + query GetSuperCar($id: ID!) { + superCar(id: $id) { + id + objectId + engine + doors + price + mileage + } + } + `, + variables: { + id: superCar.id, + }, + })).data.superCar; + expect(getSuperCar).toBeTruthy(); + + await parseGraphQLServer.setGraphQLConfig({ + classConfigs: [ + { + className: 'SuperCar', + type: { + outputFields: [], + }, + }, + ], + }); + + await resetGraphQLCache(); + await expectAsync( + apolloClient.query({ + query: gql` + query GetSuperCar($id: ID!) { + superCar(id: $id) { + engine + } + } + `, + variables: { + id: superCar.id, + }, + }) + ).toBeRejected(); + getSuperCar = (await apolloClient.query({ + query: gql` + query GetSuperCar($id: ID!) { + superCar(id: $id) { + id + objectId + } + } + `, + variables: { + id: superCar.id, + }, + })).data.superCar; + expect(getSuperCar.objectId).toBe(superCar.id); + }); + + it('should only allow the supplied constraint fields for a class', async () => { + try { + const schemaController = await parseServer.config.databaseController.loadSchema(); + + await schemaController.addClassIfNotExists('SuperCar', { + model: { type: 'String' }, + engine: { type: 'String' }, + doors: { type: 'Number' }, + price: { type: 'String' }, + mileage: { type: 'Number' }, + insuranceCertificate: { type: 'String' }, + }); + + await new Parse.Object('SuperCar').save({ + model: 'McLaren', + engine: 'petrol', + doors: 3, + price: '£7500', + mileage: 0, + insuranceCertificate: 'private-file.pdf', + }); + + await parseGraphQLServer.setGraphQLConfig({ + classConfigs: [ + { + className: 'SuperCar', + type: { + constraintFields: ['engine', 'doors', 'price'], + }, + }, + ], + }); + + await resetGraphQLCache(); + + await expectAsync( + apolloClient.query({ + query: gql` + query FindSuperCar { + superCars( + where: { + insuranceCertificate: { equalTo: "private-file.pdf" } + } + ) { + count + } + } + `, + }) + ).toBeRejected(); + + await expectAsync( + apolloClient.query({ + query: gql` + query FindSuperCar { + superCars(where: { mileage: { equalTo: 0 } }) { + count + } + } + `, + }) + ).toBeRejected(); + + await expectAsync( + apolloClient.query({ + query: gql` + query FindSuperCar { + superCars(where: { engine: { equalTo: "petrol" } }) { + count + } + } + `, + }) + ).toBeResolved(); + } catch (e) { + handleError(e); + } + }); + + it('should only allow the supplied sort fields for a class', async () => { + const schemaController = await parseServer.config.databaseController.loadSchema(); + + await schemaController.addClassIfNotExists('SuperCar', { + engine: { type: 'String' }, + doors: { type: 'Number' }, + price: { type: 'String' }, + mileage: { type: 'Number' }, + }); + + await new Parse.Object('SuperCar').save({ + engine: 'petrol', + doors: 3, + price: '£7500', + mileage: 0, + }); + + await parseGraphQLServer.setGraphQLConfig({ + classConfigs: [ + { + className: 'SuperCar', + type: { + sortFields: [ + { + field: 'doors', + asc: true, + desc: true, + }, + { + field: 'price', + asc: true, + desc: true, + }, + { + field: 'mileage', + asc: true, + desc: false, + }, + ], + }, + }, + ], + }); + + await resetGraphQLCache(); + + await expectAsync( + apolloClient.query({ + query: gql` + query FindSuperCar { + superCars(order: [engine_ASC]) { + edges { + node { + id + } + } + } + } + `, + }) + ).toBeRejected(); + await expectAsync( + apolloClient.query({ + query: gql` + query FindSuperCar { + superCars(order: [engine_DESC]) { + edges { + node { + id + } + } + } + } + `, + }) + ).toBeRejected(); + await expectAsync( + apolloClient.query({ + query: gql` + query FindSuperCar { + superCars(order: [mileage_DESC]) { + edges { + node { + id + } + } + } + } + `, + }) + ).toBeRejected(); + + await expectAsync( + apolloClient.query({ + query: gql` + query FindSuperCar { + superCars(order: [mileage_ASC]) { + edges { + node { + id + } + } + } + } + `, + }) + ).toBeResolved(); + await expectAsync( + apolloClient.query({ + query: gql` + query FindSuperCar { + superCars(order: [doors_ASC]) { + edges { + node { + id + } + } + } + } + `, + }) + ).toBeResolved(); + await expectAsync( + apolloClient.query({ + query: gql` + query FindSuperCar { + superCars(order: [price_DESC]) { + edges { + node { + id + } + } + } + } + `, + }) + ).toBeResolved(); + await expectAsync( + apolloClient.query({ + query: gql` + query FindSuperCar { + superCars(order: [price_ASC, doors_DESC]) { + edges { + node { + id + } + } + } + } + `, + }) + ).toBeResolved(); + }); + }); + + describe('Relay Spec', () => { + beforeAll(async () => { + await resetGraphQLCache(); + }); + + afterAll(async () => { + await resetGraphQLCache(); + }); + + describe('Object Identification', () => { + it('Class get custom method should return valid gobal id', async () => { + const obj = new Parse.Object('SomeClass'); + obj.set('someField', 'some value'); + await obj.save(); + + const getResult = await apolloClient.query({ + query: gql` + query GetSomeClass($objectId: ID!) { + someClass(id: $objectId) { + id + objectId + } + } + `, + variables: { + objectId: obj.id, + }, + }); + + expect(getResult.data.someClass.objectId).toBe(obj.id); + + const nodeResult = await apolloClient.query({ + query: gql` + query Node($id: ID!) { + node(id: $id) { + id + ... on SomeClass { + objectId + someField + } + } + } + `, + variables: { + id: getResult.data.someClass.id, + }, + }); + + expect(nodeResult.data.node.id).toBe(getResult.data.someClass.id); + expect(nodeResult.data.node.objectId).toBe(obj.id); + expect(nodeResult.data.node.someField).toBe('some value'); + }); + + it('Class find custom method should return valid gobal id', async () => { + const obj1 = new Parse.Object('SomeClass'); + obj1.set('someField', 'some value 1'); + await obj1.save(); + + const obj2 = new Parse.Object('SomeClass'); + obj2.set('someField', 'some value 2'); + await obj2.save(); + + const findResult = await apolloClient.query({ + query: gql` + query FindSomeClass { + someClasses(order: [createdAt_ASC]) { + edges { + node { + id + objectId + } + } + } + } + `, + }); + + expect(findResult.data.someClasses.edges[0].node.objectId).toBe( + obj1.id + ); + expect(findResult.data.someClasses.edges[1].node.objectId).toBe( + obj2.id + ); + + const nodeResult = await apolloClient.query({ + query: gql` + query Node($id1: ID!, $id2: ID!) { + node1: node(id: $id1) { + id + ... on SomeClass { + objectId + someField + } + } + node2: node(id: $id2) { + id + ... on SomeClass { + objectId + someField + } + } + } + `, + variables: { + id1: findResult.data.someClasses.edges[0].node.id, + id2: findResult.data.someClasses.edges[1].node.id, + }, + }); + + expect(nodeResult.data.node1.id).toBe( + findResult.data.someClasses.edges[0].node.id + ); + expect(nodeResult.data.node1.objectId).toBe(obj1.id); + expect(nodeResult.data.node1.someField).toBe('some value 1'); + expect(nodeResult.data.node2.id).toBe( + findResult.data.someClasses.edges[1].node.id + ); + expect(nodeResult.data.node2.objectId).toBe(obj2.id); + expect(nodeResult.data.node2.someField).toBe('some value 2'); + }); + + it_only_db('mongo')( + 'Id inputs should work either with global id or object id', + async () => { + try { + await apolloClient.mutate({ + mutation: gql` + mutation CreateClasses { + secondaryObject: createClass( + input: { + name: "SecondaryObject" + schemaFields: { addStrings: [{ name: "someField" }] } + } + ) { + clientMutationId + } + primaryObject: createClass( + input: { + name: "PrimaryObject" + schemaFields: { + addStrings: [{ name: "stringField" }] + addArrays: [{ name: "arrayField" }] + addPointers: [ + { + name: "pointerField" + targetClassName: "SecondaryObject" + } + ] + addRelations: [ + { + name: "relationField" + targetClassName: "SecondaryObject" + } + ] + } + } + ) { + clientMutationId + } + } + `, + context: { + headers: { + 'X-Parse-Master-Key': 'test', + }, + }, + }); + + await resetGraphQLCache(); + + const createSecondaryObjectsResult = await apolloClient.mutate({ + mutation: gql` + mutation CreateSecondaryObjects { + secondaryObject1: createSecondaryObject( + input: { fields: { someField: "some value 1" } } + ) { + secondaryObject { + id + objectId + someField + } + } + secondaryObject2: createSecondaryObject( + input: { fields: { someField: "some value 2" } } + ) { + secondaryObject { + id + someField + } + } + secondaryObject3: createSecondaryObject( + input: { fields: { someField: "some value 3" } } + ) { + secondaryObject { + objectId + someField + } + } + secondaryObject4: createSecondaryObject( + input: { fields: { someField: "some value 4" } } + ) { + secondaryObject { + id + objectId + } + } + secondaryObject5: createSecondaryObject( + input: { fields: { someField: "some value 5" } } + ) { + secondaryObject { + id + } + } + secondaryObject6: createSecondaryObject( + input: { fields: { someField: "some value 6" } } + ) { + secondaryObject { + objectId + } + } + secondaryObject7: createSecondaryObject( + input: { fields: { someField: "some value 7" } } + ) { + secondaryObject { + someField + } + } + } + `, + context: { + headers: { + 'X-Parse-Master-Key': 'test', + }, + }, + }); + + const updateSecondaryObjectsResult = await apolloClient.mutate({ + mutation: gql` + mutation UpdateSecondaryObjects( + $id1: ID! + $id2: ID! + $id3: ID! + $id4: ID! + $id5: ID! + $id6: ID! + ) { + secondaryObject1: updateSecondaryObject( + input: { + id: $id1 + fields: { someField: "some value 11" } + } + ) { + secondaryObject { + id + objectId + someField + } + } + secondaryObject2: updateSecondaryObject( + input: { + id: $id2 + fields: { someField: "some value 22" } + } + ) { + secondaryObject { + id + someField + } + } + secondaryObject3: updateSecondaryObject( + input: { + id: $id3 + fields: { someField: "some value 33" } + } + ) { + secondaryObject { + objectId + someField + } + } + secondaryObject4: updateSecondaryObject( + input: { + id: $id4 + fields: { someField: "some value 44" } + } + ) { + secondaryObject { + id + objectId + } + } + secondaryObject5: updateSecondaryObject( + input: { + id: $id5 + fields: { someField: "some value 55" } + } + ) { + secondaryObject { + id + } + } + secondaryObject6: updateSecondaryObject( + input: { + id: $id6 + fields: { someField: "some value 66" } + } + ) { + secondaryObject { + objectId + } + } + } + `, + variables: { + id1: + createSecondaryObjectsResult.data.secondaryObject1 + .secondaryObject.id, + id2: + createSecondaryObjectsResult.data.secondaryObject2 + .secondaryObject.id, + id3: + createSecondaryObjectsResult.data.secondaryObject3 + .secondaryObject.objectId, + id4: + createSecondaryObjectsResult.data.secondaryObject4 + .secondaryObject.objectId, + id5: + createSecondaryObjectsResult.data.secondaryObject5 + .secondaryObject.id, + id6: + createSecondaryObjectsResult.data.secondaryObject6 + .secondaryObject.objectId, + }, + context: { + headers: { + 'X-Parse-Master-Key': 'test', + }, + }, + }); + + const deleteSecondaryObjectsResult = await apolloClient.mutate({ + mutation: gql` + mutation DeleteSecondaryObjects( + $id1: ID! + $id3: ID! + $id5: ID! + $id6: ID! + ) { + secondaryObject1: deleteSecondaryObject( + input: { id: $id1 } + ) { + secondaryObject { + id + objectId + someField + } + } + secondaryObject3: deleteSecondaryObject( + input: { id: $id3 } + ) { + secondaryObject { + objectId + someField + } + } + secondaryObject5: deleteSecondaryObject( + input: { id: $id5 } + ) { + secondaryObject { + id + } + } + secondaryObject6: deleteSecondaryObject( + input: { id: $id6 } + ) { + secondaryObject { + objectId + } + } + } + `, + variables: { + id1: + updateSecondaryObjectsResult.data.secondaryObject1 + .secondaryObject.id, + id3: + updateSecondaryObjectsResult.data.secondaryObject3 + .secondaryObject.objectId, + id5: + updateSecondaryObjectsResult.data.secondaryObject5 + .secondaryObject.id, + id6: + updateSecondaryObjectsResult.data.secondaryObject6 + .secondaryObject.objectId, + }, + context: { + headers: { + 'X-Parse-Master-Key': 'test', + }, + }, + }); + + const getSecondaryObjectsResult = await apolloClient.query({ + query: gql` + query GetSecondaryObjects($id2: ID!, $id4: ID!) { + secondaryObject2: secondaryObject(id: $id2) { + id + objectId + someField + } + secondaryObject4: secondaryObject(id: $id4) { + objectId + someField + } + } + `, + variables: { + id2: + updateSecondaryObjectsResult.data.secondaryObject2 + .secondaryObject.id, + id4: + updateSecondaryObjectsResult.data.secondaryObject4 + .secondaryObject.objectId, + }, + context: { + headers: { + 'X-Parse-Master-Key': 'test', + }, + }, + }); + + const findSecondaryObjectsResult = await apolloClient.query({ + query: gql` + query FindSecondaryObjects( + $id1: ID! + $id2: ID! + $id3: ID! + $id4: ID! + $id5: ID! + $id6: ID! + ) { + secondaryObjects( + where: { + AND: [ + { + OR: [ + { id: { equalTo: $id2 } } + { + AND: [ + { id: { equalTo: $id4 } } + { objectId: { equalTo: $id4 } } + ] + } + ] + } + { id: { notEqualTo: $id1 } } + { id: { notEqualTo: $id3 } } + { objectId: { notEqualTo: $id2 } } + { objectId: { notIn: [$id5, $id6] } } + { id: { in: [$id2, $id4] } } + ] + } + order: [id_ASC, objectId_ASC] + ) { + edges { + node { + id + objectId + someField + } + } + count + } + } + `, + variables: { + id1: + deleteSecondaryObjectsResult.data.secondaryObject1 + .secondaryObject.objectId, + id2: getSecondaryObjectsResult.data.secondaryObject2.id, + id3: + deleteSecondaryObjectsResult.data.secondaryObject3 + .secondaryObject.objectId, + id4: + getSecondaryObjectsResult.data.secondaryObject4.objectId, + id5: + deleteSecondaryObjectsResult.data.secondaryObject5 + .secondaryObject.id, + id6: + deleteSecondaryObjectsResult.data.secondaryObject6 + .secondaryObject.objectId, + }, + context: { + headers: { + 'X-Parse-Master-Key': 'test', + }, + }, + }); + + expect( + findSecondaryObjectsResult.data.secondaryObjects.count + ).toEqual(2); + expect( + findSecondaryObjectsResult.data.secondaryObjects.edges + .map(value => value.node.someField) + .sort() + ).toEqual(['some value 22', 'some value 44']); + expect( + findSecondaryObjectsResult.data.secondaryObjects.edges[0].node + .id + ).toBeLessThan( + findSecondaryObjectsResult.data.secondaryObjects.edges[1].node + .id + ); + expect( + findSecondaryObjectsResult.data.secondaryObjects.edges[0].node + .objectId + ).toBeLessThan( + findSecondaryObjectsResult.data.secondaryObjects.edges[1].node + .objectId + ); + + const createPrimaryObjectResult = await apolloClient.mutate({ + mutation: gql` + mutation CreatePrimaryObject( + $pointer: Any + $secondaryObject2: ID! + $secondaryObject4: ID! + ) { + createPrimaryObject( + input: { + fields: { + stringField: "some value" + arrayField: [1, "abc", $pointer] + pointerField: { link: $secondaryObject2 } + relationField: { + add: [$secondaryObject2, $secondaryObject4] + } + } + } + ) { + primaryObject { + id + stringField + arrayField { + ... on Element { + value + } + ... on SecondaryObject { + someField + } + } + pointerField { + id + objectId + someField + } + relationField { + edges { + node { + id + objectId + someField + } + } + } + } + } } - } - } - `, - }) - ).toBeResolved(); - await expectAsync( - apolloClient.query({ - query: gql` - query FindSuperCar { - superCars(order: [price_ASC, doors_DESC]) { - results { - id + `, + variables: { + pointer: { + __type: 'Pointer', + className: 'SecondaryObject', + objectId: + getSecondaryObjectsResult.data.secondaryObject4 + .objectId, + }, + secondaryObject2: + getSecondaryObjectsResult.data.secondaryObject2.id, + secondaryObject4: + getSecondaryObjectsResult.data.secondaryObject4.objectId, + }, + context: { + headers: { + 'X-Parse-Master-Key': 'test', + }, + }, + }); + + const updatePrimaryObjectResult = await apolloClient.mutate({ + mutation: gql` + mutation UpdatePrimaryObject( + $id: ID! + $secondaryObject2: ID! + $secondaryObject4: ID! + ) { + updatePrimaryObject( + input: { + id: $id + fields: { + pointerField: { link: $secondaryObject4 } + relationField: { + remove: [$secondaryObject2, $secondaryObject4] + } + } + } + ) { + primaryObject { + id + stringField + arrayField { + ... on Element { + value + } + ... on SecondaryObject { + someField + } + } + pointerField { + id + objectId + someField + } + relationField { + edges { + node { + id + objectId + someField + } + } + } + } + } } - } - } - `, - }) - ).toBeResolved(); + `, + variables: { + id: + createPrimaryObjectResult.data.createPrimaryObject + .primaryObject.id, + secondaryObject2: + getSecondaryObjectsResult.data.secondaryObject2.id, + secondaryObject4: + getSecondaryObjectsResult.data.secondaryObject4.objectId, + }, + context: { + headers: { + 'X-Parse-Master-Key': 'test', + }, + }, + }); + + expect( + createPrimaryObjectResult.data.createPrimaryObject + .primaryObject.stringField + ).toEqual('some value'); + expect( + createPrimaryObjectResult.data.createPrimaryObject + .primaryObject.arrayField + ).toEqual([ + { __typename: 'Element', value: 1 }, + { __typename: 'Element', value: 'abc' }, + { __typename: 'SecondaryObject', someField: 'some value 44' }, + ]); + expect( + createPrimaryObjectResult.data.createPrimaryObject + .primaryObject.pointerField.someField + ).toEqual('some value 22'); + expect( + createPrimaryObjectResult.data.createPrimaryObject.primaryObject.relationField.edges + .map(value => value.node.someField) + .sort() + ).toEqual(['some value 22', 'some value 44']); + expect( + updatePrimaryObjectResult.data.updatePrimaryObject + .primaryObject.stringField + ).toEqual('some value'); + expect( + updatePrimaryObjectResult.data.updatePrimaryObject + .primaryObject.arrayField + ).toEqual([ + { __typename: 'Element', value: 1 }, + { __typename: 'Element', value: 'abc' }, + { __typename: 'SecondaryObject', someField: 'some value 44' }, + ]); + expect( + updatePrimaryObjectResult.data.updatePrimaryObject + .primaryObject.pointerField.someField + ).toEqual('some value 44'); + expect( + updatePrimaryObjectResult.data.updatePrimaryObject + .primaryObject.relationField.edges + ).toEqual([]); + } catch (e) { + handleError(e); + } + } + ); }); }); @@ -1563,156 +2816,197 @@ describe('ParseGraphQLServer', () => { const result = await apolloClient.mutate({ mutation: gql` mutation { - class1: createClass(name: "Class1") { - name - schemaFields { + class1: createClass( + input: { name: "Class1", clientMutationId: "cmid1" } + ) { + clientMutationId + class { name - __typename + schemaFields { + name + __typename + } } } - class2: createClass(name: "Class2", schemaFields: null) { - name - schemaFields { + class2: createClass( + input: { + name: "Class2" + schemaFields: null + clientMutationId: "cmid2" + } + ) { + clientMutationId + class { name - __typename + schemaFields { + name + __typename + } } } - class3: createClass(name: "Class3", schemaFields: {}) { - name - schemaFields { + class3: createClass( + input: { + name: "Class3" + schemaFields: {} + clientMutationId: "cmid3" + } + ) { + clientMutationId + class { name - __typename + schemaFields { + name + __typename + } } } class4: createClass( - name: "Class4" - schemaFields: { - addStrings: null - addNumbers: null - addBooleans: null - addArrays: null - addObjects: null - addDates: null - addFiles: null - addGeoPoint: null - addPolygons: null - addBytes: null - addPointers: null - addRelations: null + input: { + name: "Class4" + schemaFields: { + addStrings: null + addNumbers: null + addBooleans: null + addArrays: null + addObjects: null + addDates: null + addFiles: null + addGeoPoint: null + addPolygons: null + addBytes: null + addPointers: null + addRelations: null + } + clientMutationId: "cmid4" } ) { - name - schemaFields { + clientMutationId + class { name - __typename + schemaFields { + name + __typename + } } } class5: createClass( - name: "Class5" - schemaFields: { - addStrings: [] - addNumbers: [] - addBooleans: [] - addArrays: [] - addObjects: [] - addDates: [] - addFiles: [] - addPolygons: [] - addBytes: [] - addPointers: [] - addRelations: [] + input: { + name: "Class5" + schemaFields: { + addStrings: [] + addNumbers: [] + addBooleans: [] + addArrays: [] + addObjects: [] + addDates: [] + addFiles: [] + addPolygons: [] + addBytes: [] + addPointers: [] + addRelations: [] + } + clientMutationId: "cmid5" } ) { - name - schemaFields { + clientMutationId + class { name - __typename + schemaFields { + name + __typename + } } } class6: createClass( - name: "Class6" - schemaFields: { - addStrings: [ - { name: "stringField1" } - { name: "stringField2" } - { name: "stringField3" } - ] - addNumbers: [ - { name: "numberField1" } - { name: "numberField2" } - { name: "numberField3" } - ] - addBooleans: [ - { name: "booleanField1" } - { name: "booleanField2" } - { name: "booleanField3" } - ] - addArrays: [ - { name: "arrayField1" } - { name: "arrayField2" } - { name: "arrayField3" } - ] - addObjects: [ - { name: "objectField1" } - { name: "objectField2" } - { name: "objectField3" } - ] - addDates: [ - { name: "dateField1" } - { name: "dateField2" } - { name: "dateField3" } - ] - addFiles: [ - { name: "fileField1" } - { name: "fileField2" } - { name: "fileField3" } - ] - addGeoPoint: { name: "geoPointField" } - addPolygons: [ - { name: "polygonField1" } - { name: "polygonField2" } - { name: "polygonField3" } - ] - addBytes: [ - { name: "bytesField1" } - { name: "bytesField2" } - { name: "bytesField3" } - ] - addPointers: [ - { name: "pointerField1", targetClassName: "Class1" } - { name: "pointerField2", targetClassName: "Class6" } - { name: "pointerField3", targetClassName: "Class2" } - ] - addRelations: [ - { name: "relationField1", targetClassName: "Class1" } - { name: "relationField2", targetClassName: "Class6" } - { name: "relationField3", targetClassName: "Class2" } - ] - remove: [ - { name: "stringField3" } - { name: "numberField3" } - { name: "booleanField3" } - { name: "arrayField3" } - { name: "objectField3" } - { name: "dateField3" } - { name: "fileField3" } - { name: "polygonField3" } - { name: "bytesField3" } - { name: "pointerField3" } - { name: "relationField3" } - { name: "doesNotExist" } - ] + input: { + name: "Class6" + schemaFields: { + addStrings: [ + { name: "stringField1" } + { name: "stringField2" } + { name: "stringField3" } + ] + addNumbers: [ + { name: "numberField1" } + { name: "numberField2" } + { name: "numberField3" } + ] + addBooleans: [ + { name: "booleanField1" } + { name: "booleanField2" } + { name: "booleanField3" } + ] + addArrays: [ + { name: "arrayField1" } + { name: "arrayField2" } + { name: "arrayField3" } + ] + addObjects: [ + { name: "objectField1" } + { name: "objectField2" } + { name: "objectField3" } + ] + addDates: [ + { name: "dateField1" } + { name: "dateField2" } + { name: "dateField3" } + ] + addFiles: [ + { name: "fileField1" } + { name: "fileField2" } + { name: "fileField3" } + ] + addGeoPoint: { name: "geoPointField" } + addPolygons: [ + { name: "polygonField1" } + { name: "polygonField2" } + { name: "polygonField3" } + ] + addBytes: [ + { name: "bytesField1" } + { name: "bytesField2" } + { name: "bytesField3" } + ] + addPointers: [ + { name: "pointerField1", targetClassName: "Class1" } + { name: "pointerField2", targetClassName: "Class6" } + { name: "pointerField3", targetClassName: "Class2" } + ] + addRelations: [ + { name: "relationField1", targetClassName: "Class1" } + { name: "relationField2", targetClassName: "Class6" } + { name: "relationField3", targetClassName: "Class2" } + ] + remove: [ + { name: "stringField3" } + { name: "numberField3" } + { name: "booleanField3" } + { name: "arrayField3" } + { name: "objectField3" } + { name: "dateField3" } + { name: "fileField3" } + { name: "polygonField3" } + { name: "bytesField3" } + { name: "pointerField3" } + { name: "relationField3" } + { name: "doesNotExist" } + ] + } + clientMutationId: "cmid6" } ) { - name - schemaFields { + clientMutationId + class { name - __typename - ... on SchemaPointerField { - targetClassName - } - ... on SchemaRelationField { - targetClassName + schemaFields { + name + __typename + ... on SchemaPointerField { + targetClassName + } + ... on SchemaRelationField { + targetClassName + } } } } @@ -1725,114 +3019,142 @@ describe('ParseGraphQLServer', () => { }, }); const classes = Object.keys(result.data).map(fieldName => ({ - name: result.data[fieldName].name, - schemaFields: result.data[fieldName].schemaFields.sort((a, b) => - a.name > b.name ? 1 : -1 - ), + clientMutationId: result.data[fieldName].clientMutationId, + class: { + name: result.data[fieldName].class.name, + schemaFields: result.data[fieldName].class.schemaFields.sort( + (a, b) => (a.name > b.name ? 1 : -1) + ), + __typename: result.data[fieldName].class.__typename, + }, __typename: result.data[fieldName].__typename, })); expect(classes).toEqual([ { - name: 'Class1', - schemaFields: [ - { name: 'ACL', __typename: 'SchemaACLField' }, - { name: 'createdAt', __typename: 'SchemaDateField' }, - { name: 'objectId', __typename: 'SchemaStringField' }, - { name: 'updatedAt', __typename: 'SchemaDateField' }, - ], - __typename: 'Class', + clientMutationId: 'cmid1', + class: { + name: 'Class1', + schemaFields: [ + { name: 'ACL', __typename: 'SchemaACLField' }, + { name: 'createdAt', __typename: 'SchemaDateField' }, + { name: 'objectId', __typename: 'SchemaStringField' }, + { name: 'updatedAt', __typename: 'SchemaDateField' }, + ], + __typename: 'Class', + }, + __typename: 'CreateClassPayload', }, { - name: 'Class2', - schemaFields: [ - { name: 'ACL', __typename: 'SchemaACLField' }, - { name: 'createdAt', __typename: 'SchemaDateField' }, - { name: 'objectId', __typename: 'SchemaStringField' }, - { name: 'updatedAt', __typename: 'SchemaDateField' }, - ], - __typename: 'Class', + clientMutationId: 'cmid2', + class: { + name: 'Class2', + schemaFields: [ + { name: 'ACL', __typename: 'SchemaACLField' }, + { name: 'createdAt', __typename: 'SchemaDateField' }, + { name: 'objectId', __typename: 'SchemaStringField' }, + { name: 'updatedAt', __typename: 'SchemaDateField' }, + ], + __typename: 'Class', + }, + __typename: 'CreateClassPayload', }, { - name: 'Class3', - schemaFields: [ - { name: 'ACL', __typename: 'SchemaACLField' }, - { name: 'createdAt', __typename: 'SchemaDateField' }, - { name: 'objectId', __typename: 'SchemaStringField' }, - { name: 'updatedAt', __typename: 'SchemaDateField' }, - ], - __typename: 'Class', + clientMutationId: 'cmid3', + class: { + name: 'Class3', + schemaFields: [ + { name: 'ACL', __typename: 'SchemaACLField' }, + { name: 'createdAt', __typename: 'SchemaDateField' }, + { name: 'objectId', __typename: 'SchemaStringField' }, + { name: 'updatedAt', __typename: 'SchemaDateField' }, + ], + __typename: 'Class', + }, + __typename: 'CreateClassPayload', }, { - name: 'Class4', - schemaFields: [ - { name: 'ACL', __typename: 'SchemaACLField' }, - { name: 'createdAt', __typename: 'SchemaDateField' }, - { name: 'objectId', __typename: 'SchemaStringField' }, - { name: 'updatedAt', __typename: 'SchemaDateField' }, - ], - __typename: 'Class', + clientMutationId: 'cmid4', + class: { + name: 'Class4', + schemaFields: [ + { name: 'ACL', __typename: 'SchemaACLField' }, + { name: 'createdAt', __typename: 'SchemaDateField' }, + { name: 'objectId', __typename: 'SchemaStringField' }, + { name: 'updatedAt', __typename: 'SchemaDateField' }, + ], + __typename: 'Class', + }, + __typename: 'CreateClassPayload', }, { - name: 'Class5', - schemaFields: [ - { name: 'ACL', __typename: 'SchemaACLField' }, - { name: 'createdAt', __typename: 'SchemaDateField' }, - { name: 'objectId', __typename: 'SchemaStringField' }, - { name: 'updatedAt', __typename: 'SchemaDateField' }, - ], - __typename: 'Class', + clientMutationId: 'cmid5', + class: { + name: 'Class5', + schemaFields: [ + { name: 'ACL', __typename: 'SchemaACLField' }, + { name: 'createdAt', __typename: 'SchemaDateField' }, + { name: 'objectId', __typename: 'SchemaStringField' }, + { name: 'updatedAt', __typename: 'SchemaDateField' }, + ], + __typename: 'Class', + }, + __typename: 'CreateClassPayload', }, { - name: 'Class6', - schemaFields: [ - { name: 'ACL', __typename: 'SchemaACLField' }, - { name: 'arrayField1', __typename: 'SchemaArrayField' }, - { name: 'arrayField2', __typename: 'SchemaArrayField' }, - { name: 'booleanField1', __typename: 'SchemaBooleanField' }, - { name: 'booleanField2', __typename: 'SchemaBooleanField' }, - { name: 'bytesField1', __typename: 'SchemaBytesField' }, - { name: 'bytesField2', __typename: 'SchemaBytesField' }, - { name: 'createdAt', __typename: 'SchemaDateField' }, - { name: 'dateField1', __typename: 'SchemaDateField' }, - { name: 'dateField2', __typename: 'SchemaDateField' }, - { name: 'fileField1', __typename: 'SchemaFileField' }, - { name: 'fileField2', __typename: 'SchemaFileField' }, - { - name: 'geoPointField', - __typename: 'SchemaGeoPointField', - }, - { name: 'numberField1', __typename: 'SchemaNumberField' }, - { name: 'numberField2', __typename: 'SchemaNumberField' }, - { name: 'objectField1', __typename: 'SchemaObjectField' }, - { name: 'objectField2', __typename: 'SchemaObjectField' }, - { name: 'objectId', __typename: 'SchemaStringField' }, - { - name: 'pointerField1', - __typename: 'SchemaPointerField', - targetClassName: 'Class1', - }, - { - name: 'pointerField2', - __typename: 'SchemaPointerField', - targetClassName: 'Class6', - }, - { name: 'polygonField1', __typename: 'SchemaPolygonField' }, - { name: 'polygonField2', __typename: 'SchemaPolygonField' }, - { - name: 'relationField1', - __typename: 'SchemaRelationField', - targetClassName: 'Class1', - }, - { - name: 'relationField2', - __typename: 'SchemaRelationField', - targetClassName: 'Class6', - }, - { name: 'stringField1', __typename: 'SchemaStringField' }, - { name: 'stringField2', __typename: 'SchemaStringField' }, - { name: 'updatedAt', __typename: 'SchemaDateField' }, - ], - __typename: 'Class', + clientMutationId: 'cmid6', + class: { + name: 'Class6', + schemaFields: [ + { name: 'ACL', __typename: 'SchemaACLField' }, + { name: 'arrayField1', __typename: 'SchemaArrayField' }, + { name: 'arrayField2', __typename: 'SchemaArrayField' }, + { name: 'booleanField1', __typename: 'SchemaBooleanField' }, + { name: 'booleanField2', __typename: 'SchemaBooleanField' }, + { name: 'bytesField1', __typename: 'SchemaBytesField' }, + { name: 'bytesField2', __typename: 'SchemaBytesField' }, + { name: 'createdAt', __typename: 'SchemaDateField' }, + { name: 'dateField1', __typename: 'SchemaDateField' }, + { name: 'dateField2', __typename: 'SchemaDateField' }, + { name: 'fileField1', __typename: 'SchemaFileField' }, + { name: 'fileField2', __typename: 'SchemaFileField' }, + { + name: 'geoPointField', + __typename: 'SchemaGeoPointField', + }, + { name: 'numberField1', __typename: 'SchemaNumberField' }, + { name: 'numberField2', __typename: 'SchemaNumberField' }, + { name: 'objectField1', __typename: 'SchemaObjectField' }, + { name: 'objectField2', __typename: 'SchemaObjectField' }, + { name: 'objectId', __typename: 'SchemaStringField' }, + { + name: 'pointerField1', + __typename: 'SchemaPointerField', + targetClassName: 'Class1', + }, + { + name: 'pointerField2', + __typename: 'SchemaPointerField', + targetClassName: 'Class6', + }, + { name: 'polygonField1', __typename: 'SchemaPolygonField' }, + { name: 'polygonField2', __typename: 'SchemaPolygonField' }, + { + name: 'relationField1', + __typename: 'SchemaRelationField', + targetClassName: 'Class1', + }, + { + name: 'relationField2', + __typename: 'SchemaRelationField', + targetClassName: 'Class6', + }, + { name: 'stringField1', __typename: 'SchemaStringField' }, + { name: 'stringField2', __typename: 'SchemaStringField' }, + { name: 'updatedAt', __typename: 'SchemaDateField' }, + ], + __typename: 'Class', + }, + __typename: 'CreateClassPayload', }, ]); @@ -1982,8 +3304,8 @@ describe('ParseGraphQLServer', () => { await apolloClient.mutate({ mutation: gql` mutation { - createClass(name: "SomeClass") { - name + createClass(input: { name: "SomeClass" }) { + clientMutationId } } `, @@ -2005,13 +3327,15 @@ describe('ParseGraphQLServer', () => { mutation: gql` mutation { createClass( - name: "SomeClass" - schemaFields: { - addStrings: [{ name: "someField" }] - addNumbers: [{ name: "someField" }] + input: { + name: "SomeClass" + schemaFields: { + addStrings: [{ name: "someField" }] + addNumbers: [{ name: "someField" }] + } } ) { - name + clientMutationId } } `, @@ -2034,20 +3358,26 @@ describe('ParseGraphQLServer', () => { it('should update an existing class', async () => { try { + const clientMutationId = uuidv4(); const result = await apolloClient.mutate({ mutation: gql` mutation { createClass( - name: "MyNewClass" - schemaFields: { addStrings: [{ name: "willBeRemoved" }] } + input: { + name: "MyNewClass" + schemaFields: { addStrings: [{ name: "willBeRemoved" }] } + } ) { - name - schemaFields { + class { name - __typename + schemaFields { + name + __typename + } } } - updateClass( + updateClass(input: { + clientMutationId: "${clientMutationId}" name: "MyNewClass" schemaFields: { addStrings: [ @@ -2122,16 +3452,19 @@ describe('ParseGraphQLServer', () => { { name: "doesNotExist" } ] } - ) { - name - schemaFields { + }) { + clientMutationId + class { name - __typename - ... on SchemaPointerField { - targetClassName - } - ... on SchemaRelationField { - targetClassName + schemaFields { + name + __typename + ... on SchemaPointerField { + targetClassName + } + ... on SchemaRelationField { + targetClassName + } } } } @@ -2143,76 +3476,98 @@ describe('ParseGraphQLServer', () => { }, }, }); - result.data.createClass.schemaFields = result.data.createClass.schemaFields.sort( + result.data.createClass.class.schemaFields = result.data.createClass.class.schemaFields.sort( (a, b) => (a.name > b.name ? 1 : -1) ); - result.data.updateClass.schemaFields = result.data.updateClass.schemaFields.sort( + result.data.updateClass.class.schemaFields = result.data.updateClass.class.schemaFields.sort( (a, b) => (a.name > b.name ? 1 : -1) ); expect(result).toEqual({ data: { createClass: { - name: 'MyNewClass', - schemaFields: [ - { name: 'ACL', __typename: 'SchemaACLField' }, - { name: 'createdAt', __typename: 'SchemaDateField' }, - { name: 'objectId', __typename: 'SchemaStringField' }, - { name: 'updatedAt', __typename: 'SchemaDateField' }, - { name: 'willBeRemoved', __typename: 'SchemaStringField' }, - ], - __typename: 'Class', + class: { + name: 'MyNewClass', + schemaFields: [ + { name: 'ACL', __typename: 'SchemaACLField' }, + { name: 'createdAt', __typename: 'SchemaDateField' }, + { name: 'objectId', __typename: 'SchemaStringField' }, + { name: 'updatedAt', __typename: 'SchemaDateField' }, + { + name: 'willBeRemoved', + __typename: 'SchemaStringField', + }, + ], + __typename: 'Class', + }, + __typename: 'CreateClassPayload', }, updateClass: { - name: 'MyNewClass', - schemaFields: [ - { name: 'ACL', __typename: 'SchemaACLField' }, - { name: 'arrayField1', __typename: 'SchemaArrayField' }, - { name: 'arrayField2', __typename: 'SchemaArrayField' }, - { name: 'booleanField1', __typename: 'SchemaBooleanField' }, - { name: 'booleanField2', __typename: 'SchemaBooleanField' }, - { name: 'bytesField1', __typename: 'SchemaBytesField' }, - { name: 'bytesField2', __typename: 'SchemaBytesField' }, - { name: 'createdAt', __typename: 'SchemaDateField' }, - { name: 'dateField1', __typename: 'SchemaDateField' }, - { name: 'dateField2', __typename: 'SchemaDateField' }, - { name: 'fileField1', __typename: 'SchemaFileField' }, - { name: 'fileField2', __typename: 'SchemaFileField' }, - { - name: 'geoPointField', - __typename: 'SchemaGeoPointField', - }, - { name: 'numberField1', __typename: 'SchemaNumberField' }, - { name: 'numberField2', __typename: 'SchemaNumberField' }, - { name: 'objectField1', __typename: 'SchemaObjectField' }, - { name: 'objectField2', __typename: 'SchemaObjectField' }, - { name: 'objectId', __typename: 'SchemaStringField' }, - { - name: 'pointerField1', - __typename: 'SchemaPointerField', - targetClassName: 'Class1', - }, - { - name: 'pointerField2', - __typename: 'SchemaPointerField', - targetClassName: 'Class6', - }, - { name: 'polygonField1', __typename: 'SchemaPolygonField' }, - { name: 'polygonField2', __typename: 'SchemaPolygonField' }, - { - name: 'relationField1', - __typename: 'SchemaRelationField', - targetClassName: 'Class1', - }, - { - name: 'relationField2', - __typename: 'SchemaRelationField', - targetClassName: 'Class6', - }, - { name: 'stringField1', __typename: 'SchemaStringField' }, - { name: 'stringField2', __typename: 'SchemaStringField' }, - { name: 'updatedAt', __typename: 'SchemaDateField' }, - ], - __typename: 'Class', + clientMutationId, + class: { + name: 'MyNewClass', + schemaFields: [ + { name: 'ACL', __typename: 'SchemaACLField' }, + { name: 'arrayField1', __typename: 'SchemaArrayField' }, + { name: 'arrayField2', __typename: 'SchemaArrayField' }, + { + name: 'booleanField1', + __typename: 'SchemaBooleanField', + }, + { + name: 'booleanField2', + __typename: 'SchemaBooleanField', + }, + { name: 'bytesField1', __typename: 'SchemaBytesField' }, + { name: 'bytesField2', __typename: 'SchemaBytesField' }, + { name: 'createdAt', __typename: 'SchemaDateField' }, + { name: 'dateField1', __typename: 'SchemaDateField' }, + { name: 'dateField2', __typename: 'SchemaDateField' }, + { name: 'fileField1', __typename: 'SchemaFileField' }, + { name: 'fileField2', __typename: 'SchemaFileField' }, + { + name: 'geoPointField', + __typename: 'SchemaGeoPointField', + }, + { name: 'numberField1', __typename: 'SchemaNumberField' }, + { name: 'numberField2', __typename: 'SchemaNumberField' }, + { name: 'objectField1', __typename: 'SchemaObjectField' }, + { name: 'objectField2', __typename: 'SchemaObjectField' }, + { name: 'objectId', __typename: 'SchemaStringField' }, + { + name: 'pointerField1', + __typename: 'SchemaPointerField', + targetClassName: 'Class1', + }, + { + name: 'pointerField2', + __typename: 'SchemaPointerField', + targetClassName: 'Class6', + }, + { + name: 'polygonField1', + __typename: 'SchemaPolygonField', + }, + { + name: 'polygonField2', + __typename: 'SchemaPolygonField', + }, + { + name: 'relationField1', + __typename: 'SchemaRelationField', + targetClassName: 'Class1', + }, + { + name: 'relationField2', + __typename: 'SchemaRelationField', + targetClassName: 'Class6', + }, + { name: 'stringField1', __typename: 'SchemaStringField' }, + { name: 'stringField2', __typename: 'SchemaStringField' }, + { name: 'updatedAt', __typename: 'SchemaDateField' }, + ], + __typename: 'Class', + }, + __typename: 'UpdateClassPayload', }, }, }); @@ -2308,8 +3663,8 @@ describe('ParseGraphQLServer', () => { await apolloClient.mutate({ mutation: gql` mutation { - createClass(name: "SomeClass") { - name + createClass(input: { name: "SomeClass" }) { + clientMutationId } } `, @@ -2327,8 +3682,8 @@ describe('ParseGraphQLServer', () => { await apolloClient.mutate({ mutation: gql` mutation { - updateClass(name: "SomeClass") { - name + updateClass(input: { name: "SomeClass" }) { + clientMutationId } } `, @@ -2350,10 +3705,12 @@ describe('ParseGraphQLServer', () => { mutation: gql` mutation { createClass( - name: "SomeClass" - schemaFields: { addStrings: [{ name: "someField" }] } + input: { + name: "SomeClass" + schemaFields: { addStrings: [{ name: "someField" }] } + } ) { - name + clientMutationId } } `, @@ -2372,10 +3729,12 @@ describe('ParseGraphQLServer', () => { mutation: gql` mutation { updateClass( - name: "SomeClass" - schemaFields: { addNumbers: [{ name: "someField" }] } + input: { + name: "SomeClass" + schemaFields: { addNumbers: [{ name: "someField" }] } + } ) { - name + clientMutationId } } `, @@ -2402,10 +3761,12 @@ describe('ParseGraphQLServer', () => { mutation: gql` mutation { updateClass( - name: "SomeInexistentClass" - schemaFields: { addNumbers: [{ name: "someField" }] } + input: { + name: "SomeInexistentClass" + schemaFields: { addNumbers: [{ name: "someField" }] } + } ) { - name + clientMutationId } } `, @@ -2428,23 +3789,31 @@ describe('ParseGraphQLServer', () => { it('should delete an existing class', async () => { try { + const clientMutationId = uuidv4(); const result = await apolloClient.mutate({ mutation: gql` mutation { createClass( - name: "MyNewClass" - schemaFields: { addStrings: [{ name: "willBeRemoved" }] } + input: { + name: "MyNewClass" + schemaFields: { addStrings: [{ name: "willBeRemoved" }] } + } ) { - name - schemaFields { + class { name - __typename + schemaFields { + name + __typename + } } } - deleteClass(name: "MyNewClass") { - name - schemaFields { + deleteClass(input: { clientMutationId: "${clientMutationId}" name: "MyNewClass" }) { + clientMutationId + class { name + schemaFields { + name + } } } } @@ -2455,35 +3824,48 @@ describe('ParseGraphQLServer', () => { }, }, }); - result.data.createClass.schemaFields = result.data.createClass.schemaFields.sort( + result.data.createClass.class.schemaFields = result.data.createClass.class.schemaFields.sort( (a, b) => (a.name > b.name ? 1 : -1) ); - result.data.deleteClass.schemaFields = result.data.deleteClass.schemaFields.sort( + result.data.deleteClass.class.schemaFields = result.data.deleteClass.class.schemaFields.sort( (a, b) => (a.name > b.name ? 1 : -1) ); expect(result).toEqual({ data: { createClass: { - name: 'MyNewClass', - schemaFields: [ - { name: 'ACL', __typename: 'SchemaACLField' }, - { name: 'createdAt', __typename: 'SchemaDateField' }, - { name: 'objectId', __typename: 'SchemaStringField' }, - { name: 'updatedAt', __typename: 'SchemaDateField' }, - { name: 'willBeRemoved', __typename: 'SchemaStringField' }, - ], - __typename: 'Class', - }, - deleteClass: { - name: 'MyNewClass', - schemaFields: [ - { name: 'ACL', __typename: 'SchemaACLField' }, - { name: 'createdAt', __typename: 'SchemaDateField' }, - { name: 'objectId', __typename: 'SchemaStringField' }, - { name: 'updatedAt', __typename: 'SchemaDateField' }, - { name: 'willBeRemoved', __typename: 'SchemaStringField' }, - ], - __typename: 'Class', + class: { + name: 'MyNewClass', + schemaFields: [ + { name: 'ACL', __typename: 'SchemaACLField' }, + { name: 'createdAt', __typename: 'SchemaDateField' }, + { name: 'objectId', __typename: 'SchemaStringField' }, + { name: 'updatedAt', __typename: 'SchemaDateField' }, + { + name: 'willBeRemoved', + __typename: 'SchemaStringField', + }, + ], + __typename: 'Class', + }, + __typename: 'CreateClassPayload', + }, + deleteClass: { + clientMutationId, + class: { + name: 'MyNewClass', + schemaFields: [ + { name: 'ACL', __typename: 'SchemaACLField' }, + { name: 'createdAt', __typename: 'SchemaDateField' }, + { name: 'objectId', __typename: 'SchemaStringField' }, + { name: 'updatedAt', __typename: 'SchemaDateField' }, + { + name: 'willBeRemoved', + __typename: 'SchemaStringField', + }, + ], + __typename: 'Class', + }, + __typename: 'DeleteClassPayload', }, }, }); @@ -2522,8 +3904,8 @@ describe('ParseGraphQLServer', () => { await apolloClient.mutate({ mutation: gql` mutation { - createClass(name: "SomeClass") { - name + createClass(input: { name: "SomeClass" }) { + clientMutationId } } `, @@ -2541,8 +3923,8 @@ describe('ParseGraphQLServer', () => { await apolloClient.mutate({ mutation: gql` mutation { - deleteClass(name: "SomeClass") { - name + deleteClass(input: { name: "SomeClass" }) { + clientMutationId } } `, @@ -2563,8 +3945,8 @@ describe('ParseGraphQLServer', () => { await apolloClient.mutate({ mutation: gql` mutation { - deleteClass(name: "SomeInexistentClass") { - name + deleteClass(input: { name: "SomeInexistentClass" }) { + clientMutationId } } `, @@ -2644,6 +4026,7 @@ describe('ParseGraphQLServer', () => { query GetCustomer($id: ID!) { customer(id: $id) { id + objectId someField createdAt updatedAt @@ -2655,7 +4038,7 @@ describe('ParseGraphQLServer', () => { }, })).data.customer; - expect(result.id).toEqual(obj.id); + expect(result.objectId).toEqual(obj.id); expect(result.someField).toEqual('someValue'); expect(new Date(result.createdAt)).toEqual(obj.createdAt); expect(new Date(result.updatedAt)).toEqual(obj.updatedAt); @@ -2685,10 +4068,10 @@ describe('ParseGraphQLServer', () => { query: gql` query GetCustomer($id: ID!) { customer(id: $id) { - id + objectId manyRelations { ... on Customer { - id + objectId someCustomerField arrayField { ... on Element { @@ -2697,7 +4080,7 @@ describe('ParseGraphQLServer', () => { } } ... on SomeClass { - id + objectId someClassField } } @@ -2711,14 +4094,14 @@ describe('ParseGraphQLServer', () => { }, })).data.customer; - expect(result.id).toEqual(obj3.id); + expect(result.objectId).toEqual(obj3.id); expect(result.manyRelations.length).toEqual(2); const customerSubObject = result.manyRelations.find( - o => o.id === obj1.id + o => o.objectId === obj1.id ); const someClassSubObject = result.manyRelations.find( - o => o.id === obj2.id + o => o.objectId === obj2.id ); expect(customerSubObject).toBeDefined(); @@ -2769,28 +4152,28 @@ describe('ParseGraphQLServer', () => { query: gql` query DeepComplexGraphQLQuery($id: ID!) { country(id: $id) { - id + objectId name companies { ... on Company { - id + objectId name employees { ... on Employee { - id + objectId name } } teams { ... on Team { - id + objectId name employees { ... on Employee { - id + objectId name country { - id + objectId name } } @@ -2808,33 +4191,33 @@ describe('ParseGraphQLServer', () => { })).data.country; const expectedResult = { - id: obj4.id, + objectId: obj4.id, name: 'imACountry', __typename: 'Country', companies: [ { - id: obj3.id, + objectId: obj3.id, name: 'imACompany', __typename: 'Company', employees: [ { - id: obj1.id, + objectId: obj1.id, name: 'imAnEmployee', __typename: 'Employee', }, ], teams: [ { - id: obj2.id, + objectId: obj2.id, name: 'imATeam', __typename: 'Team', employees: [ { - id: obj1.id, + objectId: obj1.id, name: 'imAnEmployee', __typename: 'Employee', country: { - id: obj4.id, + objectId: obj4.id, name: 'imACountry', __typename: 'Country', }, @@ -3264,25 +4647,27 @@ describe('ParseGraphQLServer', () => { query: gql` query FindCustomer { customers { - results { - id - someField - createdAt - updatedAt + edges { + node { + objectId + someField + createdAt + updatedAt + } } } } `, }); - expect(result.data.customers.results.length).toEqual(2); + expect(result.data.customers.edges.length).toEqual(2); - result.data.customers.results.forEach(resultObj => { - const obj = resultObj.id === obj1.id ? obj1 : obj2; - expect(resultObj.id).toEqual(obj.id); - expect(resultObj.someField).toEqual(obj.get('someField')); - expect(new Date(resultObj.createdAt)).toEqual(obj.createdAt); - expect(new Date(resultObj.updatedAt)).toEqual(obj.updatedAt); + result.data.customers.edges.forEach(resultObj => { + const obj = resultObj.node.objectId === obj1.id ? obj1 : obj2; + expect(resultObj.node.objectId).toEqual(obj.id); + expect(resultObj.node.someField).toEqual(obj.get('someField')); + expect(new Date(resultObj.node.createdAt)).toEqual(obj.createdAt); + expect(new Date(resultObj.node.updatedAt)).toEqual(obj.updatedAt); }); }); @@ -3299,9 +4684,11 @@ describe('ParseGraphQLServer', () => { query: gql` query FindSomeObjects { find: ${graphqlClassName} { - results { - id - someField + edges { + node { + id + someField + } } } } @@ -3315,62 +4702,62 @@ describe('ParseGraphQLServer', () => { } expect( - (await findObjects('GraphQLClass')).data.find.results.map( - object => object.someField + (await findObjects('GraphQLClass')).data.find.edges.map( + object => object.node.someField ) ).toEqual([]); expect( - (await findObjects('PublicClass')).data.find.results.map( - object => object.someField + (await findObjects('PublicClass')).data.find.edges.map( + object => object.node.someField ) ).toEqual(['someValue4']); expect( (await findObjects('GraphQLClass', { 'X-Parse-Master-Key': 'test', - })).data.find.results - .map(object => object.someField) + })).data.find.edges + .map(object => object.node.someField) .sort() ).toEqual(['someValue1', 'someValue2', 'someValue3']); expect( (await findObjects('PublicClass', { 'X-Parse-Master-Key': 'test', - })).data.find.results.map(object => object.someField) + })).data.find.edges.map(object => object.node.someField) ).toEqual(['someValue4']); expect( (await findObjects('GraphQLClass', { 'X-Parse-Session-Token': user1.getSessionToken(), - })).data.find.results - .map(object => object.someField) + })).data.find.edges + .map(object => object.node.someField) .sort() ).toEqual(['someValue1', 'someValue2', 'someValue3']); expect( (await findObjects('PublicClass', { 'X-Parse-Session-Token': user1.getSessionToken(), - })).data.find.results.map(object => object.someField) + })).data.find.edges.map(object => object.node.someField) ).toEqual(['someValue4']); expect( (await findObjects('GraphQLClass', { 'X-Parse-Session-Token': user2.getSessionToken(), - })).data.find.results - .map(object => object.someField) + })).data.find.edges + .map(object => object.node.someField) .sort() ).toEqual(['someValue1', 'someValue2', 'someValue3']); expect( (await findObjects('GraphQLClass', { 'X-Parse-Session-Token': user3.getSessionToken(), - })).data.find.results - .map(object => object.someField) + })).data.find.edges + .map(object => object.node.someField) .sort() ).toEqual(['someValue1', 'someValue3']); expect( (await findObjects('GraphQLClass', { 'X-Parse-Session-Token': user4.getSessionToken(), - })).data.find.results.map(object => object.someField) + })).data.find.edges.map(object => object.node.someField) ).toEqual([]); expect( (await findObjects('GraphQLClass', { 'X-Parse-Session-Token': user5.getSessionToken(), - })).data.find.results.map(object => object.someField) + })).data.find.edges.map(object => object.node.someField) ).toEqual(['someValue3']); }); @@ -3383,8 +4770,10 @@ describe('ParseGraphQLServer', () => { query: gql` query FindSomeObjects($where: GraphQLClassWhereInput) { graphQLClasses(where: $where) { - results { - someField + edges { + node { + someField + } } } } @@ -3416,8 +4805,8 @@ describe('ParseGraphQLServer', () => { }); expect( - result.data.graphQLClasses.results - .map(object => object.someField) + result.data.graphQLClasses.edges + .map(object => object.node.someField) .sort() ).toEqual(['someValue1', 'someValue3']); }); @@ -3431,8 +4820,10 @@ describe('ParseGraphQLServer', () => { query: gql` query FindSomeObjects($where: GraphQLClassWhereInput) { graphQLClasses(where: $where) { - results { - someField + edges { + node { + someField + } } } } @@ -3451,9 +4842,9 @@ describe('ParseGraphQLServer', () => { }, }); - const { results } = result.data.graphQLClasses; - expect(results.length).toBe(1); - expect(results[0].someField).toEqual('someValue3'); + const { edges } = result.data.graphQLClasses; + expect(edges.length).toBe(1); + expect(edges[0].node.someField).toEqual('someValue3'); }); it('should support OR operation', async () => { @@ -3472,8 +4863,10 @@ describe('ParseGraphQLServer', () => { ] } ) { - results { - someField + edges { + node { + someField + } } } } @@ -3486,8 +4879,8 @@ describe('ParseGraphQLServer', () => { }); expect( - result.data.graphQLClasses.results - .map(object => object.someField) + result.data.graphQLClasses.edges + .map(object => object.node.someField) .sort() ).toEqual(['someValue1', 'someValue2']); }); @@ -3507,8 +4900,10 @@ describe('ParseGraphQLServer', () => { $where: FullTextSearchTestWhereInput ) { fullTextSearchTests(where: $where) { - results { - id + edges { + node { + objectId + } } } } @@ -3531,15 +4926,15 @@ describe('ParseGraphQLServer', () => { }, }); - expect(result.data.fullTextSearchTests.results[0].id).toEqual( - obj.id - ); + expect( + result.data.fullTextSearchTests.edges[0].node.objectId + ).toEqual(obj.id); } catch (e) { handleError(e); } }); - it('should support order, skip and limit arguments', async () => { + it('should support order, skip and first arguments', async () => { const promises = []; for (let i = 0; i < 100; i++) { const obj = new Parse.Object('SomeClass'); @@ -3557,16 +4952,18 @@ describe('ParseGraphQLServer', () => { $where: SomeClassWhereInput $order: [SomeClassOrder!] $skip: Int - $limit: Int + $first: Int ) { find: someClasses( where: $where order: $order skip: $skip - limit: $limit + first: $first ) { - results { - someField + edges { + node { + someField + } } } } @@ -3579,14 +4976,165 @@ describe('ParseGraphQLServer', () => { }, order: ['numberField_DESC', 'someField_ASC'], skip: 4, - limit: 2, + first: 2, }, }); - expect(result.data.find.results.map(obj => obj.someField)).toEqual([ - 'someValue14', - 'someValue17', - ]); + expect( + result.data.find.edges.map(obj => obj.node.someField) + ).toEqual(['someValue14', 'someValue17']); + }); + + it('should support pagination', async () => { + const numberArray = (first, last) => { + const array = []; + for (let i = first; i <= last; i++) { + array.push(i); + } + return array; + }; + + const promises = []; + for (let i = 0; i < 100; i++) { + const obj = new Parse.Object('SomeClass'); + obj.set('numberField', i); + promises.push(obj.save()); + } + await Promise.all(promises); + + await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear(); + + const find = async ({ skip, after, first, before, last } = {}) => { + return await apolloClient.query({ + query: gql` + query FindSomeObjects( + $order: [SomeClassOrder!] + $skip: Int + $after: String + $first: Int + $before: String + $last: Int + ) { + someClasses( + order: $order + skip: $skip + after: $after + first: $first + before: $before + last: $last + ) { + edges { + cursor + node { + numberField + } + } + count + pageInfo { + hasPreviousPage + startCursor + endCursor + hasNextPage + } + } + } + `, + variables: { + order: ['numberField_ASC'], + skip, + after, + first, + before, + last, + }, + }); + }; + + let result = await find(); + expect( + result.data.someClasses.edges.map(edge => edge.node.numberField) + ).toEqual(numberArray(0, 99)); + expect(result.data.someClasses.count).toEqual(100); + expect(result.data.someClasses.pageInfo.hasPreviousPage).toEqual( + false + ); + expect(result.data.someClasses.pageInfo.startCursor).toEqual( + result.data.someClasses.edges[0].cursor + ); + expect(result.data.someClasses.pageInfo.endCursor).toEqual( + result.data.someClasses.edges[99].cursor + ); + expect(result.data.someClasses.pageInfo.hasNextPage).toEqual(false); + + result = await find({ first: 10 }); + expect( + result.data.someClasses.edges.map(edge => edge.node.numberField) + ).toEqual(numberArray(0, 9)); + expect(result.data.someClasses.count).toEqual(100); + expect(result.data.someClasses.pageInfo.hasPreviousPage).toEqual( + false + ); + expect(result.data.someClasses.pageInfo.startCursor).toEqual( + result.data.someClasses.edges[0].cursor + ); + expect(result.data.someClasses.pageInfo.endCursor).toEqual( + result.data.someClasses.edges[9].cursor + ); + expect(result.data.someClasses.pageInfo.hasNextPage).toEqual(true); + + result = await find({ + first: 10, + after: result.data.someClasses.pageInfo.endCursor, + }); + expect( + result.data.someClasses.edges.map(edge => edge.node.numberField) + ).toEqual(numberArray(10, 19)); + expect(result.data.someClasses.count).toEqual(100); + expect(result.data.someClasses.pageInfo.hasPreviousPage).toEqual( + true + ); + expect(result.data.someClasses.pageInfo.startCursor).toEqual( + result.data.someClasses.edges[0].cursor + ); + expect(result.data.someClasses.pageInfo.endCursor).toEqual( + result.data.someClasses.edges[9].cursor + ); + expect(result.data.someClasses.pageInfo.hasNextPage).toEqual(true); + + result = await find({ last: 10 }); + expect( + result.data.someClasses.edges.map(edge => edge.node.numberField) + ).toEqual(numberArray(90, 99)); + expect(result.data.someClasses.count).toEqual(100); + expect(result.data.someClasses.pageInfo.hasPreviousPage).toEqual( + true + ); + expect(result.data.someClasses.pageInfo.startCursor).toEqual( + result.data.someClasses.edges[0].cursor + ); + expect(result.data.someClasses.pageInfo.endCursor).toEqual( + result.data.someClasses.edges[9].cursor + ); + expect(result.data.someClasses.pageInfo.hasNextPage).toEqual(false); + + result = await find({ + last: 10, + before: result.data.someClasses.pageInfo.startCursor, + }); + expect( + result.data.someClasses.edges.map(edge => edge.node.numberField) + ).toEqual(numberArray(80, 89)); + expect(result.data.someClasses.count).toEqual(100); + expect(result.data.someClasses.pageInfo.hasPreviousPage).toEqual( + true + ); + expect(result.data.someClasses.pageInfo.startCursor).toEqual( + result.data.someClasses.edges[0].cursor + ); + expect(result.data.someClasses.pageInfo.endCursor).toEqual( + result.data.someClasses.edges[9].cursor + ); + expect(result.data.someClasses.pageInfo.hasNextPage).toEqual(true); }); it('should support count', async () => { @@ -3616,11 +5164,13 @@ describe('ParseGraphQLServer', () => { query: gql` query FindSomeObjects( $where: GraphQLClassWhereInput - $limit: Int + $first: Int ) { - find: graphQLClasses(where: $where, limit: $limit) { - results { - id + find: graphQLClasses(where: $where, first: $first) { + edges { + node { + id + } } count } @@ -3628,7 +5178,7 @@ describe('ParseGraphQLServer', () => { `, variables: { where, - limit: 0, + first: 0, }, context: { headers: { @@ -3637,7 +5187,7 @@ describe('ParseGraphQLServer', () => { }, }); - expect(result.data.find.results).toEqual([]); + expect(result.data.find.edges).toEqual([]); expect(result.data.find.count).toEqual(2); }); @@ -3682,7 +5232,7 @@ describe('ParseGraphQLServer', () => { }, }); - expect(result.data.find.results).toBeUndefined(); + expect(result.data.find.edges).toBeUndefined(); expect(result.data.find.count).toEqual(2); }); @@ -3705,10 +5255,12 @@ describe('ParseGraphQLServer', () => { query FindSomeObjects($limit: Int) { find: someClasses( where: { id: { exists: true } } - limit: $limit + first: $limit ) { - results { - id + edges { + node { + id + } } count } @@ -3724,7 +5276,7 @@ describe('ParseGraphQLServer', () => { }, }); - expect(result.data.find.results.length).toEqual(10); + expect(result.data.find.edges.length).toEqual(10); expect(result.data.find.count).toEqual(100); }); @@ -3737,8 +5289,10 @@ describe('ParseGraphQLServer', () => { query: gql` query FindSomeObject($where: GraphQLClassWhereInput) { find: graphQLClasses(where: $where) { - results { - someField + edges { + node { + someField + } } } } @@ -3759,10 +5313,12 @@ describe('ParseGraphQLServer', () => { query: gql` query FindSomeObject($where: GraphQLClassWhereInput) { find: graphQLClasses(where: $where) { - results { - someField - pointerToUser { - username + edges { + node { + someField + pointerToUser { + username + } } } } @@ -3780,10 +5336,12 @@ describe('ParseGraphQLServer', () => { }, }); - expect(result1.data.find.results[0].someField).toBeDefined(); - expect(result1.data.find.results[0].pointerToUser).toBeUndefined(); - expect(result2.data.find.results[0].someField).toBeDefined(); - expect(result2.data.find.results[0].pointerToUser).toBeDefined(); + expect(result1.data.find.edges[0].node.someField).toBeDefined(); + expect( + result1.data.find.edges[0].node.pointerToUser + ).toBeUndefined(); + expect(result2.data.find.edges[0].node.someField).toBeDefined(); + expect(result2.data.find.edges[0].node.pointerToUser).toBeDefined(); }); it('should support include argument', async () => { @@ -3801,9 +5359,11 @@ describe('ParseGraphQLServer', () => { query: gql` query FindSomeObject($where: GraphQLClassWhereInput) { find: graphQLClasses(where: $where) { - results { - pointerToUser { - id + edges { + node { + pointerToUser { + id + } } } } @@ -3823,9 +5383,11 @@ describe('ParseGraphQLServer', () => { query: gql` query FindSomeObject($where: GraphQLClassWhereInput) { find: graphQLClasses(where: $where) { - results { - pointerToUser { - username + edges { + node { + pointerToUser { + username + } } } } @@ -3841,10 +5403,10 @@ describe('ParseGraphQLServer', () => { }, }); expect( - result1.data.find.results[0].pointerToUser.username + result1.data.find.edges[0].node.pointerToUser.username ).toBeUndefined(); expect( - result2.data.find.results[0].pointerToUser.username + result2.data.find.edges[0].node.pointerToUser.username ).toBeDefined(); }); @@ -3865,9 +5427,11 @@ describe('ParseGraphQLServer', () => { query: gql` query FindSomeObjects { find: graphQLClasses { - results { - pointerToUser { - username + edges { + node { + pointerToUser { + username + } } } } @@ -3920,9 +5484,11 @@ describe('ParseGraphQLServer', () => { find: graphQLClasses( options: { readPreference: SECONDARY } ) { - results { - pointerToUser { - username + edges { + node { + pointerToUser { + username + } } } } @@ -3978,9 +5544,11 @@ describe('ParseGraphQLServer', () => { includeReadPreference: NEAREST } ) { - results { - pointerToUser { - username + edges { + node { + pointerToUser { + username + } } } } @@ -4038,8 +5606,10 @@ describe('ParseGraphQLServer', () => { subqueryReadPreference: NEAREST } ) { - results { - id + edges { + node { + id + } } } } @@ -4093,6 +5663,7 @@ describe('ParseGraphQLServer', () => { describe('Objects Mutations', () => { describe('Create', () => { it('should return specific type object using class specific mutation', async () => { + const clientMutationId = uuidv4(); const customerSchema = new Parse.Schema('Customer'); customerSchema.addString('someField'); await customerSchema.save(); @@ -4101,30 +5672,42 @@ describe('ParseGraphQLServer', () => { const result = await apolloClient.mutate({ mutation: gql` - mutation CreateCustomer($fields: CreateCustomerFieldsInput) { - createCustomer(fields: $fields) { - id - createdAt - someField + mutation CreateCustomer($input: CreateCustomerInput!) { + createCustomer(input: $input) { + clientMutationId + customer { + id + objectId + createdAt + someField + } } } `, variables: { - fields: { - someField: 'someValue', + input: { + clientMutationId, + fields: { + someField: 'someValue', + }, }, }, }); - expect(result.data.createCustomer.id).toBeDefined(); - expect(result.data.createCustomer.someField).toEqual('someValue'); + expect(result.data.createCustomer.clientMutationId).toEqual( + clientMutationId + ); + expect(result.data.createCustomer.customer.id).toBeDefined(); + expect(result.data.createCustomer.customer.someField).toEqual( + 'someValue' + ); const customer = await new Parse.Query('Customer').get( - result.data.createCustomer.id + result.data.createCustomer.customer.objectId ); expect(customer.createdAt).toEqual( - new Date(result.data.createCustomer.createdAt) + new Date(result.data.createCustomer.customer.createdAt) ); expect(customer.get('someField')).toEqual('someValue'); }); @@ -4135,12 +5718,16 @@ describe('ParseGraphQLServer', () => { await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear(); async function createObject(className, headers) { + const getClassName = + className.charAt(0).toLowerCase() + className.slice(1); const result = await apolloClient.mutate({ mutation: gql` mutation CreateSomeObject { - create${className} { - id - createdAt + create${className}(input: {}) { + ${getClassName} { + id + createdAt + } } } `, @@ -4149,7 +5736,8 @@ describe('ParseGraphQLServer', () => { }, }); - const specificCreate = result.data[`create${className}`]; + const specificCreate = + result.data[`create${className}`][getClassName]; expect(specificCreate.id).toBeDefined(); expect(specificCreate.createdAt).toBeDefined(); @@ -4207,6 +5795,7 @@ describe('ParseGraphQLServer', () => { describe('Update', () => { it('should return specific type object using class specific mutation', async () => { + const clientMutationId = uuidv4(); const obj = new Parse.Object('Customer'); obj.set('someField1', 'someField1Value1'); obj.set('someField2', 'someField2Value1'); @@ -4216,30 +5805,36 @@ describe('ParseGraphQLServer', () => { const result = await apolloClient.mutate({ mutation: gql` - mutation UpdateCustomer( - $id: ID! - $fields: UpdateCustomerFieldsInput - ) { - updateCustomer(id: $id, fields: $fields) { - updatedAt - someField1 - someField2 + mutation UpdateCustomer($input: UpdateCustomerInput!) { + updateCustomer(input: $input) { + clientMutationId + customer { + updatedAt + someField1 + someField2 + } } } `, variables: { - id: obj.id, - fields: { - someField1: 'someField1Value2', + input: { + clientMutationId, + id: obj.id, + fields: { + someField1: 'someField1Value2', + }, }, }, }); - expect(result.data.updateCustomer.updatedAt).toBeDefined(); - expect(result.data.updateCustomer.someField1).toEqual( + expect(result.data.updateCustomer.clientMutationId).toEqual( + clientMutationId + ); + expect(result.data.updateCustomer.customer.updatedAt).toBeDefined(); + expect(result.data.updateCustomer.customer.someField1).toEqual( 'someField1Value2' ); - expect(result.data.updateCustomer.someField2).toEqual( + expect(result.data.updateCustomer.customer.someField2).toEqual( 'someField2Value1' ); @@ -4263,8 +5858,11 @@ describe('ParseGraphQLServer', () => { $id: ID! $fields: UpdateCustomerFieldsInput ) { - updateCustomer(id: $id, fields: $fields) { - id + updateCustomer(input: { id: $id, fields: $fields }) { + customer { + id + objectId + } } } `, @@ -4276,7 +5874,9 @@ describe('ParseGraphQLServer', () => { }, }); - expect(result.data.updateCustomer.id).toEqual(obj.id); + expect(result.data.updateCustomer.customer.objectId).toEqual( + obj.id + ); await obj.fetch(); @@ -4296,11 +5896,12 @@ describe('ParseGraphQLServer', () => { $id: ID! $fields: Update${className}FieldsInput ) { - update: update${className}( + update: update${className}(input: { id: $id fields: $fields - ) { - updatedAt + clientMutationId: "someid" + }) { + clientMutationId } } `, @@ -4329,7 +5930,7 @@ describe('ParseGraphQLServer', () => { expect( (await updateObject(object4.className, object4.id, { someField: 'changedValue1', - })).data.update.updatedAt + })).data.update.clientMutationId ).toBeDefined(); await object4.fetch({ useMasterKey: true }); expect(object4.get('someField')).toEqual('changedValue1'); @@ -4341,7 +5942,7 @@ describe('ParseGraphQLServer', () => { obj.id, { someField: 'changedValue2' }, { 'X-Parse-Master-Key': 'test' } - )).data.update.updatedAt + )).data.update.clientMutationId ).toBeDefined(); await obj.fetch({ useMasterKey: true }); expect(obj.get('someField')).toEqual('changedValue2'); @@ -4355,7 +5956,7 @@ describe('ParseGraphQLServer', () => { obj.id, { someField: 'changedValue3' }, { 'X-Parse-Session-Token': user1.getSessionToken() } - )).data.update.updatedAt + )).data.update.clientMutationId ).toBeDefined(); await obj.fetch({ useMasterKey: true }); expect(obj.get('someField')).toEqual('changedValue3'); @@ -4369,7 +5970,7 @@ describe('ParseGraphQLServer', () => { obj.id, { someField: 'changedValue4' }, { 'X-Parse-Session-Token': user2.getSessionToken() } - )).data.update.updatedAt + )).data.update.clientMutationId ).toBeDefined(); await obj.fetch({ useMasterKey: true }); expect(obj.get('someField')).toEqual('changedValue4'); @@ -4383,7 +5984,7 @@ describe('ParseGraphQLServer', () => { obj.id, { someField: 'changedValue5' }, { 'X-Parse-Session-Token': user3.getSessionToken() } - )).data.update.updatedAt + )).data.update.clientMutationId ).toBeDefined(); await obj.fetch({ useMasterKey: true }); expect(obj.get('someField')).toEqual('changedValue5'); @@ -4421,7 +6022,7 @@ describe('ParseGraphQLServer', () => { object4.id, { someField: 'changedValue6' }, { 'X-Parse-Session-Token': user4.getSessionToken() } - )).data.update.updatedAt + )).data.update.clientMutationId ).toBeDefined(); await object4.fetch({ useMasterKey: true }); expect(object4.get('someField')).toEqual('changedValue6'); @@ -4446,7 +6047,7 @@ describe('ParseGraphQLServer', () => { object3.id, { someField: 'changedValue7' }, { 'X-Parse-Session-Token': user5.getSessionToken() } - )).data.update.updatedAt + )).data.update.clientMutationId ).toBeDefined(); await object3.fetch({ useMasterKey: true }); expect(object3.get('someField')).toEqual('changedValue7'); @@ -4456,7 +6057,7 @@ describe('ParseGraphQLServer', () => { object4.id, { someField: 'changedValue7' }, { 'X-Parse-Session-Token': user5.getSessionToken() } - )).data.update.updatedAt + )).data.update.clientMutationId ).toBeDefined(); await object4.fetch({ useMasterKey: true }); expect(object4.get('someField')).toEqual('changedValue7'); @@ -4474,11 +6075,14 @@ describe('ParseGraphQLServer', () => { $id: ID! $fields: Update${className}FieldsInput ) { - update${className}( + update${className}(input: { id: $id fields: $fields - ) { - updatedAt + }) { + ${className.charAt(0).toLowerCase() + + className.slice(1)} { + updatedAt + } } } `, @@ -4507,7 +6111,10 @@ describe('ParseGraphQLServer', () => { expect( (await updateObject(object4.className, object4.id, { someField: 'changedValue1', - })).data[`update${object4.className}`].updatedAt + })).data[`update${object4.className}`][ + object4.className.charAt(0).toLowerCase() + + object4.className.slice(1) + ].updatedAt ).toBeDefined(); await object4.fetch({ useMasterKey: true }); expect(object4.get('someField')).toEqual('changedValue1'); @@ -4519,7 +6126,10 @@ describe('ParseGraphQLServer', () => { obj.id, { someField: 'changedValue2' }, { 'X-Parse-Master-Key': 'test' } - )).data[`update${obj.className}`].updatedAt + )).data[`update${obj.className}`][ + obj.className.charAt(0).toLowerCase() + + obj.className.slice(1) + ].updatedAt ).toBeDefined(); await obj.fetch({ useMasterKey: true }); expect(obj.get('someField')).toEqual('changedValue2'); @@ -4533,7 +6143,10 @@ describe('ParseGraphQLServer', () => { obj.id, { someField: 'changedValue3' }, { 'X-Parse-Session-Token': user1.getSessionToken() } - )).data[`update${obj.className}`].updatedAt + )).data[`update${obj.className}`][ + obj.className.charAt(0).toLowerCase() + + obj.className.slice(1) + ].updatedAt ).toBeDefined(); await obj.fetch({ useMasterKey: true }); expect(obj.get('someField')).toEqual('changedValue3'); @@ -4547,7 +6160,10 @@ describe('ParseGraphQLServer', () => { obj.id, { someField: 'changedValue4' }, { 'X-Parse-Session-Token': user2.getSessionToken() } - )).data[`update${obj.className}`].updatedAt + )).data[`update${obj.className}`][ + obj.className.charAt(0).toLowerCase() + + obj.className.slice(1) + ].updatedAt ).toBeDefined(); await obj.fetch({ useMasterKey: true }); expect(obj.get('someField')).toEqual('changedValue4'); @@ -4561,7 +6177,10 @@ describe('ParseGraphQLServer', () => { obj.id, { someField: 'changedValue5' }, { 'X-Parse-Session-Token': user3.getSessionToken() } - )).data[`update${obj.className}`].updatedAt + )).data[`update${obj.className}`][ + obj.className.charAt(0).toLowerCase() + + obj.className.slice(1) + ].updatedAt ).toBeDefined(); await obj.fetch({ useMasterKey: true }); expect(obj.get('someField')).toEqual('changedValue5'); @@ -4599,7 +6218,10 @@ describe('ParseGraphQLServer', () => { object4.id, { someField: 'changedValue6' }, { 'X-Parse-Session-Token': user4.getSessionToken() } - )).data[`update${object4.className}`].updatedAt + )).data[`update${object4.className}`][ + object4.className.charAt(0).toLowerCase() + + object4.className.slice(1) + ].updatedAt ).toBeDefined(); await object4.fetch({ useMasterKey: true }); expect(object4.get('someField')).toEqual('changedValue6'); @@ -4624,7 +6246,10 @@ describe('ParseGraphQLServer', () => { object3.id, { someField: 'changedValue7' }, { 'X-Parse-Session-Token': user5.getSessionToken() } - )).data[`update${object3.className}`].updatedAt + )).data[`update${object3.className}`][ + object3.className.charAt(0).toLowerCase() + + object3.className.slice(1) + ].updatedAt ).toBeDefined(); await object3.fetch({ useMasterKey: true }); expect(object3.get('someField')).toEqual('changedValue7'); @@ -4634,7 +6259,10 @@ describe('ParseGraphQLServer', () => { object4.id, { someField: 'changedValue7' }, { 'X-Parse-Session-Token': user5.getSessionToken() } - )).data[`update${object4.className}`].updatedAt + )).data[`update${object4.className}`][ + object4.className.charAt(0).toLowerCase() + + object4.className.slice(1) + ].updatedAt ).toBeDefined(); await object4.fetch({ useMasterKey: true }); expect(object4.get('someField')).toEqual('changedValue7'); @@ -4643,6 +6271,7 @@ describe('ParseGraphQLServer', () => { describe('Delete', () => { it('should return a specific type using class specific mutation', async () => { + const clientMutationId = uuidv4(); const obj = new Parse.Object('Customer'); obj.set('someField1', 'someField1Value1'); obj.set('someField2', 'someField2Value1'); @@ -4652,24 +6281,36 @@ describe('ParseGraphQLServer', () => { const result = await apolloClient.mutate({ mutation: gql` - mutation DeleteCustomer($id: ID!) { - deleteCustomer(id: $id) { - id - someField1 - someField2 + mutation DeleteCustomer($input: DeleteCustomerInput!) { + deleteCustomer(input: $input) { + clientMutationId + customer { + id + objectId + someField1 + someField2 + } } } `, variables: { - id: obj.id, + input: { + clientMutationId, + id: obj.id, + }, }, }); - expect(result.data.deleteCustomer.id).toEqual(obj.id); - expect(result.data.deleteCustomer.someField1).toEqual( + expect(result.data.deleteCustomer.clientMutationId).toEqual( + clientMutationId + ); + expect(result.data.deleteCustomer.customer.objectId).toEqual( + obj.id + ); + expect(result.data.deleteCustomer.customer.someField1).toEqual( 'someField1Value1' ); - expect(result.data.deleteCustomer.someField2).toEqual( + expect(result.data.deleteCustomer.customer.someField2).toEqual( 'someField2Value1' ); @@ -4689,8 +6330,11 @@ describe('ParseGraphQLServer', () => { mutation DeleteSomeObject( $id: ID! ) { - delete: delete${className}(id: $id) { - id + delete: delete${className}(input: { id: $id }) { + ${className.charAt(0).toLowerCase() + + className.slice(1)} { + objectId + } } } `, @@ -4726,32 +6370,44 @@ describe('ParseGraphQLServer', () => { }) ); expect( - (await deleteObject(object4.className, object4.id)).data.delete - ).toEqual({ id: object4.id, __typename: 'PublicClass' }); + (await deleteObject(object4.className, object4.id)).data.delete[ + object4.className.charAt(0).toLowerCase() + + object4.className.slice(1) + ] + ).toEqual({ objectId: object4.id, __typename: 'PublicClass' }); await expectAsync( object4.fetch({ useMasterKey: true }) ).toBeRejectedWith(jasmine.stringMatching('Object not found')); expect( (await deleteObject(object1.className, object1.id, { 'X-Parse-Master-Key': 'test', - })).data.delete - ).toEqual({ id: object1.id, __typename: 'GraphQLClass' }); + })).data.delete[ + object1.className.charAt(0).toLowerCase() + + object1.className.slice(1) + ] + ).toEqual({ objectId: object1.id, __typename: 'GraphQLClass' }); await expectAsync( object1.fetch({ useMasterKey: true }) ).toBeRejectedWith(jasmine.stringMatching('Object not found')); expect( (await deleteObject(object2.className, object2.id, { 'X-Parse-Session-Token': user2.getSessionToken(), - })).data.delete - ).toEqual({ id: object2.id, __typename: 'GraphQLClass' }); + })).data.delete[ + object2.className.charAt(0).toLowerCase() + + object2.className.slice(1) + ] + ).toEqual({ objectId: object2.id, __typename: 'GraphQLClass' }); await expectAsync( object2.fetch({ useMasterKey: true }) ).toBeRejectedWith(jasmine.stringMatching('Object not found')); expect( (await deleteObject(object3.className, object3.id, { 'X-Parse-Session-Token': user5.getSessionToken(), - })).data.delete - ).toEqual({ id: object3.id, __typename: 'GraphQLClass' }); + })).data.delete[ + object3.className.charAt(0).toLowerCase() + + object3.className.slice(1) + ] + ).toEqual({ objectId: object3.id, __typename: 'GraphQLClass' }); await expectAsync( object3.fetch({ useMasterKey: true }) ).toBeRejectedWith(jasmine.stringMatching('Object not found')); @@ -4768,8 +6424,11 @@ describe('ParseGraphQLServer', () => { mutation DeleteSomeObject( $id: ID! ) { - delete${className}(id: $id) { - id + delete${className}(input: { id: $id }) { + ${className.charAt(0).toLowerCase() + + className.slice(1)} { + objectId + } } } `, @@ -4807,7 +6466,10 @@ describe('ParseGraphQLServer', () => { expect( (await deleteObject(object4.className, object4.id)).data[ `delete${object4.className}` - ].id + ][ + object4.className.charAt(0).toLowerCase() + + object4.className.slice(1) + ].objectId ).toEqual(object4.id); await expectAsync( object4.fetch({ useMasterKey: true }) @@ -4815,7 +6477,10 @@ describe('ParseGraphQLServer', () => { expect( (await deleteObject(object1.className, object1.id, { 'X-Parse-Master-Key': 'test', - })).data[`delete${object1.className}`].id + })).data[`delete${object1.className}`][ + object1.className.charAt(0).toLowerCase() + + object1.className.slice(1) + ].objectId ).toEqual(object1.id); await expectAsync( object1.fetch({ useMasterKey: true }) @@ -4823,7 +6488,10 @@ describe('ParseGraphQLServer', () => { expect( (await deleteObject(object2.className, object2.id, { 'X-Parse-Session-Token': user2.getSessionToken(), - })).data[`delete${object2.className}`].id + })).data[`delete${object2.className}`][ + object2.className.charAt(0).toLowerCase() + + object2.className.slice(1) + ].objectId ).toEqual(object2.id); await expectAsync( object2.fetch({ useMasterKey: true }) @@ -4831,7 +6499,10 @@ describe('ParseGraphQLServer', () => { expect( (await deleteObject(object3.className, object3.id, { 'X-Parse-Session-Token': user5.getSessionToken(), - })).data[`delete${object3.className}`].id + })).data[`delete${object3.className}`][ + object3.className.charAt(0).toLowerCase() + + object3.className.slice(1) + ].objectId ).toEqual(object3.id); await expectAsync( object3.fetch({ useMasterKey: true }) @@ -4843,6 +6514,8 @@ describe('ParseGraphQLServer', () => { describe('Files Mutations', () => { describe('Create', () => { it('should return File object', async () => { + const clientMutationId = uuidv4(); + parseServer = await global.reconfigureServer({ publicServerURL: 'http://localhost:13377/parse', }); @@ -4852,19 +6525,28 @@ describe('ParseGraphQLServer', () => { 'operations', JSON.stringify({ query: ` - mutation CreateFile($upload: Upload!) { - createFile(upload: $upload) { - name - url + mutation CreateFile($input: CreateFileInput!) { + createFile(input: $input) { + clientMutationId + fileInfo { + name + url + } } } `, variables: { - upload: null, + input: { + clientMutationId, + upload: null, + }, }, }) ); - body.append('map', JSON.stringify({ 1: ['variables.upload'] })); + body.append( + 'map', + JSON.stringify({ 1: ['variables.input.upload'] }) + ); body.append('1', 'My File Content', { filename: 'myFileName.txt', contentType: 'text/plain', @@ -4880,14 +6562,17 @@ describe('ParseGraphQLServer', () => { const result = JSON.parse(await res.text()); - expect(result.data.createFile.name).toEqual( + expect(result.data.createFile.clientMutationId).toEqual( + clientMutationId + ); + expect(result.data.createFile.fileInfo.name).toEqual( jasmine.stringMatching(/_myFileName.txt$/) ); - expect(result.data.createFile.url).toEqual( + expect(result.data.createFile.fileInfo.url).toEqual( jasmine.stringMatching(/_myFileName.txt$/) ); - res = await fetch(result.data.createFile.url); + res = await fetch(result.data.createFile.fileInfo.url); expect(res.status).toEqual(200); expect(await res.text()).toEqual('My File Content'); @@ -4912,9 +6597,11 @@ describe('ParseGraphQLServer', () => { query: gql` query GetCurrentUser { viewer { - id - username - email + user { + id + username + email + } } } `, @@ -4929,7 +6616,7 @@ describe('ParseGraphQLServer', () => { id, username: resultUserName, email: resultEmail, - } = result.data.viewer; + } = result.data.viewer.user; expect(id).toBeDefined(); expect(resultUserName).toEqual(userName); expect(resultEmail).toEqual(email); @@ -4957,10 +6644,13 @@ describe('ParseGraphQLServer', () => { query: gql` query GetCurrentUser { viewer { - id sessionToken - userFoo { - bar + user { + id + objectId + userFoo { + bar + } } } } @@ -4972,8 +6662,9 @@ describe('ParseGraphQLServer', () => { }, }); - const { id, sessionToken, userFoo: resultFoo } = result.data.viewer; - expect(id).toEqual(user.id); + const sessionToken = result.data.viewer.sessionToken; + const { objectId, userFoo: resultFoo } = result.data.viewer.user; + expect(objectId).toEqual(user.id); expect(sessionToken).toBeDefined(); expect(resultFoo).toBeDefined(); expect(resultFoo.bar).toEqual('hello'); @@ -4982,34 +6673,45 @@ describe('ParseGraphQLServer', () => { describe('Users Mutations', () => { it('should sign user up', async () => { + const clientMutationId = uuidv4(); const userSchema = new Parse.Schema('_User'); userSchema.addString('someField'); await userSchema.update(); await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear(); const result = await apolloClient.mutate({ mutation: gql` - mutation SignUp($fields: SignUpFieldsInput) { - signUp(fields: $fields) { - sessionToken - someField + mutation SignUp($input: SignUpInput!) { + signUp(input: $input) { + clientMutationId + viewer { + sessionToken + user { + someField + } + } } } `, variables: { - fields: { - username: 'user1', - password: 'user1', - someField: 'someValue', + input: { + clientMutationId, + userFields: { + username: 'user1', + password: 'user1', + someField: 'someValue', + }, }, }, }); - expect(result.data.signUp.sessionToken).toBeDefined(); - expect(result.data.signUp.someField).toEqual('someValue'); - expect(typeof result.data.signUp.sessionToken).toBe('string'); + expect(result.data.signUp.clientMutationId).toEqual(clientMutationId); + expect(result.data.signUp.viewer.sessionToken).toBeDefined(); + expect(result.data.signUp.viewer.user.someField).toEqual('someValue'); + expect(typeof result.data.signUp.viewer.sessionToken).toBe('string'); }); it('should log the user in', async () => { + const clientMutationId = uuidv4(); const user = new Parse.User(); user.setUsername('user1'); user.setPassword('user1'); @@ -5019,27 +6721,35 @@ describe('ParseGraphQLServer', () => { await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear(); const result = await apolloClient.mutate({ mutation: gql` - mutation LogInUser($fields: LogInFieldsInput) { - logIn(fields: $fields) { - sessionToken - someField + mutation LogInUser($input: LogInInput!) { + logIn(input: $input) { + clientMutationId + viewer { + sessionToken + user { + someField + } + } } } `, variables: { - fields: { + input: { + clientMutationId, username: 'user1', password: 'user1', }, }, }); - expect(result.data.logIn.sessionToken).toBeDefined(); - expect(result.data.logIn.someField).toEqual('someValue'); - expect(typeof result.data.logIn.sessionToken).toBe('string'); + expect(result.data.logIn.clientMutationId).toEqual(clientMutationId); + expect(result.data.logIn.viewer.sessionToken).toBeDefined(); + expect(result.data.logIn.viewer.user.someField).toEqual('someValue'); + expect(typeof result.data.logIn.viewer.sessionToken).toBe('string'); }); it('should log the user out', async () => { + const clientMutationId = uuidv4(); const user = new Parse.User(); user.setUsername('user1'); user.setPassword('user1'); @@ -5048,27 +6758,32 @@ describe('ParseGraphQLServer', () => { const logIn = await apolloClient.mutate({ mutation: gql` - mutation LogInUser($fields: LogInFieldsInput) { - logIn(fields: $fields) { - sessionToken + mutation LogInUser($input: LogInInput!) { + logIn(input: $input) { + viewer { + sessionToken + } } } `, variables: { - fields: { + input: { username: 'user1', password: 'user1', }, }, }); - const sessionToken = logIn.data.logIn.sessionToken; + const sessionToken = logIn.data.logIn.viewer.sessionToken; const logOut = await apolloClient.mutate({ mutation: gql` - mutation LogOutUser { - logOut { - sessionToken + mutation LogOutUser($input: LogOutInput!) { + logOut(input: $input) { + clientMutationId + viewer { + sessionToken + } } } `, @@ -5077,14 +6792,20 @@ describe('ParseGraphQLServer', () => { 'X-Parse-Session-Token': sessionToken, }, }, + variables: { + input: { + clientMutationId, + }, + }, }); - expect(logOut.data.logOut).toBeDefined(); + expect(logOut.data.logOut.clientMutationId).toEqual(clientMutationId); + expect(logOut.data.logOut.viewer.sessionToken).toEqual(sessionToken); try { await apolloClient.query({ query: gql` query GetCurrentUser { - me { + viewer { username } } @@ -5141,7 +6862,9 @@ describe('ParseGraphQLServer', () => { query: gql` query GetCurrentUser { viewer { - username + user { + username + } } } `, @@ -5170,11 +6893,15 @@ describe('ParseGraphQLServer', () => { query: gql` query GetCurrentUser { viewer { - username + user { + username + } } cars { - results { - id + edges { + node { + id + } } } } @@ -5197,19 +6924,33 @@ describe('ParseGraphQLServer', () => { describe('Functions Mutations', () => { it('can be called', async () => { try { + const clientMutationId = uuidv4(); + Parse.Cloud.define('hello', async () => { return 'Hello world!'; }); const result = await apolloClient.mutate({ mutation: gql` - mutation CallFunction { - callCloudCode(functionName: hello) + mutation CallFunction($input: CallCloudCodeInput!) { + callCloudCode(input: $input) { + clientMutationId + result + } } `, + variables: { + input: { + clientMutationId, + functionName: 'hello', + }, + }, }); - expect(result.data.callCloudCode).toEqual('Hello world!'); + expect(result.data.callCloudCode.clientMutationId).toEqual( + clientMutationId + ); + expect(result.data.callCloudCode.result).toEqual('Hello world!'); } catch (e) { handleError(e); } @@ -5224,7 +6965,9 @@ describe('ParseGraphQLServer', () => { await apolloClient.mutate({ mutation: gql` mutation CallFunction { - callCloudCode(functionName: hello) + callCloudCode(input: { functionName: hello }) { + result + } } `, }); @@ -5325,7 +7068,9 @@ describe('ParseGraphQLServer', () => { apolloClient.mutate({ mutation: gql` mutation CallFunction($params: Object) { - callCloudCode(functionName: hello, params: $params) + callCloudCode(input: { functionName: hello, params: $params }) { + result + } } `, variables: { @@ -5431,8 +7176,10 @@ describe('ParseGraphQLServer', () => { await apolloClient.mutate({ mutation: gql` mutation CreateClass($schemaFields: SchemaFieldsInput) { - createClass(name: "SomeClass", schemaFields: $schemaFields) { - name + createClass( + input: { name: "SomeClass", schemaFields: $schemaFields } + ) { + clientMutationId } } `, @@ -5456,8 +7203,10 @@ describe('ParseGraphQLServer', () => { const createResult = await apolloClient.mutate({ mutation: gql` mutation CreateSomeObject($fields: CreateSomeClassFieldsInput) { - createSomeClass(fields: $fields) { - id + createSomeClass(input: { fields: $fields }) { + someClass { + id + } } } `, @@ -5477,21 +7226,23 @@ describe('ParseGraphQLServer', () => { someClasses( where: { someField: { equalTo: $someFieldValue } } ) { - results { - someField + edges { + node { + someField + } } } } `, variables: { - id: createResult.data.createSomeClass.id, + id: createResult.data.createSomeClass.someClass.id, someFieldValue, }, }); expect(typeof getResult.data.someClass.someField).toEqual('string'); expect(getResult.data.someClass.someField).toEqual(someFieldValue); - expect(getResult.data.someClasses.results.length).toEqual(1); + expect(getResult.data.someClasses.edges.length).toEqual(1); } catch (e) { handleError(e); } @@ -5504,8 +7255,10 @@ describe('ParseGraphQLServer', () => { await apolloClient.mutate({ mutation: gql` mutation CreateClass($schemaFields: SchemaFieldsInput) { - createClass(name: "SomeClass", schemaFields: $schemaFields) { - name + createClass( + input: { name: "SomeClass", schemaFields: $schemaFields } + ) { + clientMutationId } } `, @@ -5526,8 +7279,10 @@ describe('ParseGraphQLServer', () => { const createResult = await apolloClient.mutate({ mutation: gql` mutation CreateSomeObject($fields: CreateSomeClassFieldsInput) { - createSomeClass(fields: $fields) { - id + createSomeClass(input: { fields: $fields }) { + someClass { + id + } } } `, @@ -5550,21 +7305,23 @@ describe('ParseGraphQLServer', () => { someClasses( where: { someField: { equalTo: $someFieldValue } } ) { - results { - someField + edges { + node { + someField + } } } } `, variables: { - id: createResult.data.createSomeClass.id, + id: createResult.data.createSomeClass.someClass.id, someFieldValue, }, }); expect(typeof getResult.data.someClass.someField).toEqual('number'); expect(getResult.data.someClass.someField).toEqual(someFieldValue); - expect(getResult.data.someClasses.results.length).toEqual(1); + expect(getResult.data.someClasses.edges.length).toEqual(1); } catch (e) { handleError(e); } @@ -5577,8 +7334,10 @@ describe('ParseGraphQLServer', () => { await apolloClient.mutate({ mutation: gql` mutation CreateClass($schemaFields: SchemaFieldsInput) { - createClass(name: "SomeClass", schemaFields: $schemaFields) { - name + createClass( + input: { name: "SomeClass", schemaFields: $schemaFields } + ) { + clientMutationId } } `, @@ -5602,8 +7361,10 @@ describe('ParseGraphQLServer', () => { const createResult = await apolloClient.mutate({ mutation: gql` mutation CreateSomeObject($fields: CreateSomeClassFieldsInput) { - createSomeClass(fields: $fields) { - id + createSomeClass(input: { fields: $fields }) { + someClass { + id + } } } `, @@ -5623,21 +7384,23 @@ describe('ParseGraphQLServer', () => { someClasses( where: { someField: { equalTo: $someFieldValue } } ) { - results { - someField + edges { + node { + someField + } } } } `, variables: { - id: createResult.data.createSomeClass.id, + id: createResult.data.createSomeClass.someClass.id, someFieldValue, }, }); expect(typeof getResult.data.someClass.someField).toEqual('number'); expect(getResult.data.someClass.someField).toEqual(someFieldValue); - expect(getResult.data.someClasses.results.length).toEqual(1); + expect(getResult.data.someClasses.edges.length).toEqual(1); } catch (e) { handleError(e); } @@ -5651,8 +7414,10 @@ describe('ParseGraphQLServer', () => { await apolloClient.mutate({ mutation: gql` mutation CreateClass($schemaFields: SchemaFieldsInput) { - createClass(name: "SomeClass", schemaFields: $schemaFields) { - name + createClass( + input: { name: "SomeClass", schemaFields: $schemaFields } + ) { + clientMutationId } } `, @@ -5680,8 +7445,10 @@ describe('ParseGraphQLServer', () => { const createResult = await apolloClient.mutate({ mutation: gql` mutation CreateSomeObject($fields: CreateSomeClassFieldsInput) { - createSomeClass(fields: $fields) { - id + createSomeClass(input: { fields: $fields }) { + someClass { + id + } } } `, @@ -5710,14 +7477,16 @@ describe('ParseGraphQLServer', () => { someFieldFalse: { equalTo: $someFieldValueFalse } } ) { - results { - id + edges { + node { + id + } } } } `, variables: { - id: createResult.data.createSomeClass.id, + id: createResult.data.createSomeClass.someClass.id, someFieldValueTrue, someFieldValueFalse, }, @@ -5731,7 +7500,7 @@ describe('ParseGraphQLServer', () => { ); expect(getResult.data.someClass.someFieldTrue).toEqual(true); expect(getResult.data.someClass.someFieldFalse).toEqual(false); - expect(getResult.data.someClasses.results.length).toEqual(1); + expect(getResult.data.someClasses.edges.length).toEqual(1); } catch (e) { handleError(e); } @@ -5744,8 +7513,10 @@ describe('ParseGraphQLServer', () => { await apolloClient.mutate({ mutation: gql` mutation CreateClass($schemaFields: SchemaFieldsInput) { - createClass(name: "SomeClass", schemaFields: $schemaFields) { - name + createClass( + input: { name: "SomeClass", schemaFields: $schemaFields } + ) { + clientMutationId } } `, @@ -5769,8 +7540,10 @@ describe('ParseGraphQLServer', () => { const createResult = await apolloClient.mutate({ mutation: gql` mutation CreateSomeObject($fields: CreateSomeClassFieldsInput) { - createSomeClass(fields: $fields) { - id + createSomeClass(input: { fields: $fields }) { + someClass { + id + } } } `, @@ -5788,21 +7561,23 @@ describe('ParseGraphQLServer', () => { someField } someClasses(where: { someField: { exists: true } }) { - results { - id + edges { + node { + id + } } } } `, variables: { - id: createResult.data.createSomeClass.id, + id: createResult.data.createSomeClass.someClass.id, }, }); expect(new Date(getResult.data.someClass.someField)).toEqual( someFieldValue ); - expect(getResult.data.someClasses.results.length).toEqual(1); + expect(getResult.data.someClasses.edges.length).toEqual(1); } catch (e) { handleError(e); } @@ -5812,8 +7587,8 @@ describe('ParseGraphQLServer', () => { await apolloClient.mutate({ mutation: gql` mutation CreateClass { - createClass(name: "SomeClass") { - name + createClass(input: { name: "SomeClass" }) { + clientMutationId } } `, @@ -5859,22 +7634,25 @@ describe('ParseGraphQLServer', () => { } = await apolloClient.mutate({ mutation: gql` mutation Create($fields: CreateSomeClassFieldsInput) { - createSomeClass(fields: $fields) { - id - ACL { - users { - userId - read - write - } - roles { - roleName - read - write - } - public { - read - write + createSomeClass(input: { fields: $fields }) { + someClass { + id + objectId + ACL { + users { + userId + read + write + } + roles { + roleName + read + write + } + public { + read + write + } } } } @@ -5930,7 +7708,7 @@ describe('ParseGraphQLServer', () => { public: { read: true, write: true, __typename: 'PublicACL' }, }; const query1 = new Parse.Query('SomeClass'); - const obj1 = (await query1.get(createSomeClass.id, { + const obj1 = (await query1.get(createSomeClass.someClass.objectId, { useMasterKey: true, })).toJSON(); expect(obj1.ACL[user.id]).toEqual({ read: true, write: true }); @@ -5938,36 +7716,39 @@ describe('ParseGraphQLServer', () => { expect(obj1.ACL['role:aRole']).toEqual({ read: true }); expect(obj1.ACL['role:aRole2']).toEqual({ write: true }); expect(obj1.ACL['*']).toEqual({ read: true, write: true }); - expect(createSomeClass.ACL).toEqual(expectedCreateACL); + expect(createSomeClass.someClass.ACL).toEqual(expectedCreateACL); const { data: { updateSomeClass }, } = await apolloClient.mutate({ mutation: gql` mutation Update($id: ID!, $fields: UpdateSomeClassFieldsInput) { - updateSomeClass(id: $id, fields: $fields) { - id - ACL { - users { - userId - read - write - } - roles { - roleName - read - write - } - public { - read - write + updateSomeClass(input: { id: $id, fields: $fields }) { + someClass { + id + objectId + ACL { + users { + userId + read + write + } + roles { + roleName + read + write + } + public { + read + write + } } } } } `, variables: { - id: createSomeClass.id, + id: createSomeClass.someClass.id, fields: { ACL: { roles: [{ roleName: 'aRole', write: true, read: true }], @@ -5992,14 +7773,14 @@ describe('ParseGraphQLServer', () => { }; const query2 = new Parse.Query('SomeClass'); - const obj2 = (await query2.get(createSomeClass.id, { + const obj2 = (await query2.get(createSomeClass.someClass.objectId, { useMasterKey: true, })).toJSON(); expect(obj2.ACL['role:aRole']).toEqual({ write: true, read: true }); expect(obj2.ACL[user.id]).toBeUndefined(); expect(obj2.ACL['*']).toEqual({ read: true }); - expect(updateSomeClass.ACL).toEqual(expectedUpdateACL); + expect(updateSomeClass.someClass.ACL).toEqual(expectedUpdateACL); }); it('should support pointer on create', async () => { @@ -6019,15 +7800,21 @@ describe('ParseGraphQLServer', () => { await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear(); const { - data: { createCountry: result }, + data: { + createCountry: { country: result }, + }, } = await apolloClient.mutate({ mutation: gql` mutation Create($fields: CreateCountryFieldsInput) { - createCountry(fields: $fields) { - id - company { + createCountry(input: { fields: $fields }) { + country { id - name + objectId + company { + id + objectId + name + } } } } @@ -6041,7 +7828,7 @@ describe('ParseGraphQLServer', () => { }); expect(result.id).toBeDefined(); - expect(result.company.id).toEqual(company2.id); + expect(result.company.objectId).toEqual(company2.id); expect(result.company.name).toEqual('imACompany2'); }); @@ -6058,15 +7845,19 @@ describe('ParseGraphQLServer', () => { await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear(); const { - data: { createCountry: result }, + data: { + createCountry: { country: result }, + }, } = await apolloClient.mutate({ mutation: gql` mutation Create($fields: CreateCountryFieldsInput) { - createCountry(fields: $fields) { - id - company { + createCountry(input: { fields: $fields }) { + country { id - name + company { + id + name + } } } } @@ -6105,15 +7896,21 @@ describe('ParseGraphQLServer', () => { await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear(); const { - data: { updateCountry: result }, + data: { + updateCountry: { country: result }, + }, } = await apolloClient.mutate({ mutation: gql` mutation Update($id: ID!, $fields: UpdateCountryFieldsInput) { - updateCountry(id: $id, fields: $fields) { - id - company { + updateCountry(input: { id: $id, fields: $fields }) { + country { id - name + objectId + company { + id + objectId + name + } } } } @@ -6127,7 +7924,7 @@ describe('ParseGraphQLServer', () => { }); expect(result.id).toBeDefined(); - expect(result.company.id).toEqual(company2.id); + expect(result.company.objectId).toEqual(company2.id); expect(result.company.name).toEqual('imACompany2'); }); @@ -6144,15 +7941,19 @@ describe('ParseGraphQLServer', () => { await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear(); const { - data: { updateCountry: result }, + data: { + updateCountry: { country: result }, + }, } = await apolloClient.mutate({ mutation: gql` mutation Update($id: ID!, $fields: UpdateCountryFieldsInput) { - updateCountry(id: $id, fields: $fields) { - id - company { + updateCountry(input: { id: $id, fields: $fields }) { + country { id - name + company { + id + name + } } } } @@ -6189,17 +7990,25 @@ describe('ParseGraphQLServer', () => { await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear(); const { - data: { createCountry: result }, + data: { + createCountry: { country: result }, + }, } = await apolloClient.mutate({ mutation: gql` mutation CreateCountry($fields: CreateCountryFieldsInput) { - createCountry(fields: $fields) { - id - name - companies { - results { - id - name + createCountry(input: { fields: $fields }) { + country { + id + objectId + name + companies { + edges { + node { + id + objectId + name + } + } } } } @@ -6225,15 +8034,15 @@ describe('ParseGraphQLServer', () => { expect(result.id).toBeDefined(); expect(result.name).toEqual('imACountry2'); - expect(result.companies.results.length).toEqual(3); + expect(result.companies.edges.length).toEqual(3); expect( - result.companies.results.some(o => o.id === company.id) + result.companies.edges.some(o => o.node.objectId === company.id) ).toBeTruthy(); expect( - result.companies.results.some(o => o.name === 'imACompany2') + result.companies.edges.some(o => o.node.name === 'imACompany2') ).toBeTruthy(); expect( - result.companies.results.some(o => o.name === 'imACompany3') + result.companies.edges.some(o => o.node.name === 'imACompany3') ).toBeTruthy(); } ); @@ -6256,21 +8065,29 @@ describe('ParseGraphQLServer', () => { await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear(); const { - data: { createCountry: result }, + data: { + createCountry: { country: result }, + }, } = await apolloClient.mutate({ mutation: gql` mutation CreateCountry($fields: CreateCountryFieldsInput) { - createCountry(fields: $fields) { - id - name - companies { - results { - id - name - teams { - results { + createCountry(input: { fields: $fields }) { + country { + id + name + companies { + edges { + node { id name + teams { + edges { + node { + id + name + } + } + } } } } @@ -6307,19 +8124,19 @@ describe('ParseGraphQLServer', () => { expect(result.id).toBeDefined(); expect(result.name).toEqual('imACountry2'); - expect(result.companies.results.length).toEqual(2); + expect(result.companies.edges.length).toEqual(2); expect( - result.companies.results.some( + result.companies.edges.some( c => - c.name === 'imACompany2' && - c.teams.results.some(t => t.name === 'imATeam2') + c.node.name === 'imACompany2' && + c.node.teams.edges.some(t => t.node.name === 'imATeam2') ) ).toBeTruthy(); expect( - result.companies.results.some( + result.companies.edges.some( c => - c.name === 'imACompany3' && - c.teams.results.some(t => t.name === 'imATeam3') + c.node.name === 'imACompany3' && + c.node.teams.edges.some(t => t.node.name === 'imATeam3') ) ).toBeTruthy(); }); @@ -6343,19 +8160,27 @@ describe('ParseGraphQLServer', () => { await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear(); const { - data: { updateCountry: result }, + data: { + updateCountry: { country: result }, + }, } = await apolloClient.mutate({ mutation: gql` mutation UpdateCountry( $id: ID! $fields: UpdateCountryFieldsInput ) { - updateCountry(id: $id, fields: $fields) { - id - companies { - results { - id - name + updateCountry(input: { id: $id, fields: $fields }) { + country { + id + objectId + companies { + edges { + node { + id + objectId + name + } + } } } } @@ -6377,16 +8202,16 @@ describe('ParseGraphQLServer', () => { }, }); - expect(result.id).toEqual(country.id); - expect(result.companies.results.length).toEqual(2); + expect(result.objectId).toEqual(country.id); + expect(result.companies.edges.length).toEqual(2); expect( - result.companies.results.some(o => o.id === company2.id) + result.companies.edges.some(o => o.node.objectId === company2.id) ).toBeTruthy(); expect( - result.companies.results.some(o => o.name === 'imACompany3') + result.companies.edges.some(o => o.node.name === 'imACompany3') ).toBeTruthy(); expect( - result.companies.results.some(o => o.id === company1.id) + result.companies.edges.some(o => o.node.objectId === company1.id) ).toBeFalsy(); } ); @@ -6406,20 +8231,26 @@ describe('ParseGraphQLServer', () => { await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear(); const { - data: { createCountry: result }, + data: { + createCountry: { country: result }, + }, } = await apolloClient.mutate({ mutation: gql` mutation CreateCountry( $fields: CreateCountryFieldsInput $where: CompanyWhereInput ) { - createCountry(fields: $fields) { - id - name - companies(where: $where) { - results { - id - name + createCountry(input: { fields: $fields }) { + country { + id + name + companies(where: $where) { + edges { + node { + id + name + } + } } } } @@ -6450,9 +8281,9 @@ describe('ParseGraphQLServer', () => { expect(result.id).toBeDefined(); expect(result.name).toEqual('imACountry2'); - expect(result.companies.results.length).toEqual(1); + expect(result.companies.edges.length).toEqual(1); expect( - result.companies.results.some(o => o.name === 'imACompany2') + result.companies.edges.some(o => o.node.name === 'imACompany2') ).toBeTruthy(); } ); @@ -6481,10 +8312,14 @@ describe('ParseGraphQLServer', () => { query getCountry($id: ID!) { country(id: $id) { id + objectId companies { - results { - id - name + edges { + node { + id + objectId + name + } } count } @@ -6496,13 +8331,13 @@ describe('ParseGraphQLServer', () => { }, }); - expect(result1.id).toEqual(country.id); - expect(result1.companies.results.length).toEqual(2); + expect(result1.objectId).toEqual(country.id); + expect(result1.companies.edges.length).toEqual(2); expect( - result1.companies.results.some(o => o.id === company1.id) + result1.companies.edges.some(o => o.node.objectId === company1.id) ).toBeTruthy(); expect( - result1.companies.results.some(o => o.id === company2.id) + result1.companies.edges.some(o => o.node.objectId === company2.id) ).toBeTruthy(); // With where @@ -6513,10 +8348,14 @@ describe('ParseGraphQLServer', () => { query getCountry($id: ID!, $where: CompanyWhereInput) { country(id: $id) { id + objectId companies(where: $where) { - results { - id - name + edges { + node { + id + objectId + name + } } } } @@ -6529,9 +8368,9 @@ describe('ParseGraphQLServer', () => { }, }, }); - expect(result2.id).toEqual(country.id); - expect(result2.companies.results.length).toEqual(1); - expect(result2.companies.results[0].id).toEqual(company1.id); + expect(result2.objectId).toEqual(country.id); + expect(result2.companies.edges.length).toEqual(1); + expect(result2.companies.edges[0].node.objectId).toEqual(company1.id); }); it('should support files', async () => { @@ -6545,19 +8384,26 @@ describe('ParseGraphQLServer', () => { 'operations', JSON.stringify({ query: ` - mutation CreateFile($upload: Upload!) { - createFile(upload: $upload) { - name - url + mutation CreateFile($input: CreateFileInput!) { + createFile(input: $input) { + fileInfo { + name + url + } } } `, variables: { - upload: null, + input: { + upload: null, + }, }, }) ); - body.append('map', JSON.stringify({ 1: ['variables.upload'] })); + body.append( + 'map', + JSON.stringify({ 1: ['variables.input.upload'] }) + ); body.append('1', 'My File Content', { filename: 'myFileName.txt', contentType: 'text/plain', @@ -6573,20 +8419,22 @@ describe('ParseGraphQLServer', () => { const result = JSON.parse(await res.text()); - expect(result.data.createFile.name).toEqual( + expect(result.data.createFile.fileInfo.name).toEqual( jasmine.stringMatching(/_myFileName.txt$/) ); - expect(result.data.createFile.url).toEqual( + expect(result.data.createFile.fileInfo.url).toEqual( jasmine.stringMatching(/_myFileName.txt$/) ); - const someFieldValue = result.data.createFile.name; + const someFieldValue = result.data.createFile.fileInfo.name; await apolloClient.mutate({ mutation: gql` - mutation CreaClass($schemaFields: SchemaFieldsInput) { - createClass(name: "SomeClass", schemaFields: $schemaFields) { - name + mutation CreateClass($schemaFields: SchemaFieldsInput) { + createClass( + input: { name: "SomeClass", schemaFields: $schemaFields } + ) { + clientMutationId } } `, @@ -6610,11 +8458,19 @@ describe('ParseGraphQLServer', () => { $fields1: CreateSomeClassFieldsInput $fields2: CreateSomeClassFieldsInput ) { - createSomeClass1: createSomeClass(fields: $fields1) { - id + createSomeClass1: createSomeClass( + input: { fields: $fields1 } + ) { + someClass { + id + } } - createSomeClass2: createSomeClass(fields: $fields2) { - id + createSomeClass2: createSomeClass( + input: { fields: $fields2 } + ) { + someClass { + id + } } } `, @@ -6643,39 +8499,43 @@ describe('ParseGraphQLServer', () => { findSomeClass1: someClasses( where: { someField: { exists: true } } ) { - results { - someField { - name - url + edges { + node { + someField { + name + url + } } } } findSomeClass2: someClasses( where: { someField: { exists: true } } ) { - results { - someField { - name - url + edges { + node { + someField { + name + url + } } } } } `, variables: { - id: createResult.data.createSomeClass1.id, + id: createResult.data.createSomeClass1.someClass.id, }, }); expect(typeof getResult.data.someClass.someField).toEqual('object'); expect(getResult.data.someClass.someField.name).toEqual( - result.data.createFile.name + result.data.createFile.fileInfo.name ); expect(getResult.data.someClass.someField.url).toEqual( - result.data.createFile.url + result.data.createFile.fileInfo.url ); - expect(getResult.data.findSomeClass1.results.length).toEqual(1); - expect(getResult.data.findSomeClass2.results.length).toEqual(1); + expect(getResult.data.findSomeClass1.edges.length).toEqual(1); + expect(getResult.data.findSomeClass2.edges.length).toEqual(1); res = await fetch(getResult.data.someClass.someField.url); @@ -6696,8 +8556,10 @@ describe('ParseGraphQLServer', () => { await apolloClient.mutate({ mutation: gql` mutation CreateClass($schemaFields: SchemaFieldsInput) { - createClass(name: "SomeClass", schemaFields: $schemaFields) { - name + createClass( + input: { name: "SomeClass", schemaFields: $schemaFields } + ) { + clientMutationId } } `, @@ -6721,8 +8583,10 @@ describe('ParseGraphQLServer', () => { const createResult = await apolloClient.mutate({ mutation: gql` mutation CreateSomeObject($fields: CreateSomeClassFieldsInput) { - createSomeClass(fields: $fields) { - id + createSomeClass(input: { fields: $fields }) { + someClass { + id + } } } `, @@ -6749,15 +8613,17 @@ describe('ParseGraphQLServer', () => { someField } someClasses(where: $where) { - results { - id - someField + edges { + node { + id + someField + } } } } `, variables: { - id: createResult.data.createSomeClass.id, + id: createResult.data.createSomeClass.someClass.id, where, }, }); @@ -6769,8 +8635,8 @@ describe('ParseGraphQLServer', () => { expect(someField).toEqual(someFieldValue); // Checks class query results - expect(someClasses.results.length).toEqual(1); - expect(someClasses.results[0].someField).toEqual(someFieldValue); + expect(someClasses.edges.length).toEqual(1); + expect(someClasses.edges[0].node.someField).toEqual(someFieldValue); } catch (e) { handleError(e); } @@ -6793,10 +8659,12 @@ describe('ParseGraphQLServer', () => { mutation: gql` mutation CreateClass { createClass( - name: "SomeClass" - schemaFields: { addObjects: [{ name: "someField" }] } + input: { + name: "SomeClass" + schemaFields: { addObjects: [{ name: "someField" }] } + } ) { - name + clientMutationId } } `, @@ -6815,11 +8683,15 @@ describe('ParseGraphQLServer', () => { $fields1: CreateSomeClassFieldsInput $fields2: CreateSomeClassFieldsInput ) { - create1: createSomeClass(fields: $fields1) { - id + create1: createSomeClass(input: { fields: $fields1 }) { + someClass { + id + } } - create2: createSomeClass(fields: $fields2) { - id + create2: createSomeClass(input: { fields: $fields2 }) { + someClass { + id + } } } `, @@ -6865,9 +8737,11 @@ describe('ParseGraphQLServer', () => { query: gql` query FindSomeObject($where: SomeClassWhereInput) { someClasses(where: $where) { - results { - id - someField + edges { + node { + id + someField + } } } } @@ -6881,13 +8755,15 @@ describe('ParseGraphQLServer', () => { const { someClasses } = findResult.data; // Checks class query results - const { results } = someClasses; - expect(results.length).toEqual(2); + const { edges } = someClasses; + expect(edges.length).toEqual(2); expect( - results.find(result => result.id === create1.id).someField + edges.find(result => result.node.id === create1.someClass.id).node + .someField ).toEqual(someFieldValue); expect( - results.find(result => result.id === create2.id).someField + edges.find(result => result.node.id === create2.someClass.id).node + .someField ).toEqual(someFieldValue2); } catch (e) { handleError(e); @@ -6907,8 +8783,10 @@ describe('ParseGraphQLServer', () => { await apolloClient.mutate({ mutation: gql` mutation CreateClass($schemaFields: SchemaFieldsInput) { - createClass(name: "SomeClass", schemaFields: $schemaFields) { - name + createClass( + input: { name: "SomeClass", schemaFields: $schemaFields } + ) { + clientMutationId } } `, @@ -6932,8 +8810,10 @@ describe('ParseGraphQLServer', () => { const createResult = await apolloClient.mutate({ mutation: gql` mutation CreateSomeObject($fields: CreateSomeClassFieldsInput) { - createSomeClass(fields: $fields) { - id + createSomeClass(input: { fields: $fields }) { + someClass { + id + } } } `, @@ -6955,11 +8835,13 @@ describe('ParseGraphQLServer', () => { } } someClasses(where: { someField: { exists: true } }) { - results { - id - someField { - ... on Element { - value + edges { + node { + id + someField { + ... on Element { + value + } } } } @@ -6967,7 +8849,7 @@ describe('ParseGraphQLServer', () => { } `, variables: { - id: createResult.data.createSomeClass.id, + id: createResult.data.createSomeClass.someClass.id, }, }); @@ -6976,7 +8858,7 @@ describe('ParseGraphQLServer', () => { expect(someField.map(element => element.value)).toEqual( someFieldValue ); - expect(getResult.data.someClasses.results.length).toEqual(1); + expect(getResult.data.someClasses.edges.length).toEqual(1); } catch (e) { handleError(e); } @@ -7018,18 +8900,20 @@ describe('ParseGraphQLServer', () => { mutation: gql` mutation CreateClass { createClass( - name: "SomeClass" - schemaFields: { - addStrings: [ - { name: "someStringField" } - { name: "someNullField" } - ] - addNumbers: [{ name: "someNumberField" }] - addBooleans: [{ name: "someBooleanField" }] - addObjects: [{ name: "someObjectField" }] + input: { + name: "SomeClass" + schemaFields: { + addStrings: [ + { name: "someStringField" } + { name: "someNullField" } + ] + addNumbers: [{ name: "someNumberField" }] + addBooleans: [{ name: "someBooleanField" }] + addObjects: [{ name: "someObjectField" }] + } } ) { - name + clientMutationId } } `, @@ -7045,8 +8929,10 @@ describe('ParseGraphQLServer', () => { const createResult = await apolloClient.mutate({ mutation: gql` mutation CreateSomeObject($fields: CreateSomeClassFieldsInput) { - createSomeClass(fields: $fields) { - id + createSomeClass(input: { fields: $fields }) { + someClass { + id + } } } `, @@ -7067,13 +8953,13 @@ describe('ParseGraphQLServer', () => { $id: ID! $fields: UpdateSomeClassFieldsInput ) { - updateSomeClass(id: $id, fields: $fields) { - updatedAt + updateSomeClass(input: { id: $id, fields: $fields }) { + clientMutationId } } `, variables: { - id: createResult.data.createSomeClass.id, + id: createResult.data.createSomeClass.someClass.id, fields: { someStringField: null, someNumberField: null, @@ -7097,7 +8983,7 @@ describe('ParseGraphQLServer', () => { } `, variables: { - id: createResult.data.createSomeClass.id, + id: createResult.data.createSomeClass.someClass.id, }, }); @@ -7120,8 +9006,10 @@ describe('ParseGraphQLServer', () => { await apolloClient.mutate({ mutation: gql` mutation CreateClass($schemaFields: SchemaFieldsInput) { - createClass(name: "SomeClass", schemaFields: $schemaFields) { - name + createClass( + input: { name: "SomeClass", schemaFields: $schemaFields } + ) { + clientMutationId } } `, @@ -7148,11 +9036,19 @@ describe('ParseGraphQLServer', () => { $fields1: CreateSomeClassFieldsInput $fields2: CreateSomeClassFieldsInput ) { - createSomeClass1: createSomeClass(fields: $fields1) { - id + createSomeClass1: createSomeClass( + input: { fields: $fields1 } + ) { + someClass { + id + } } - createSomeClass2: createSomeClass(fields: $fields2) { - id + createSomeClass2: createSomeClass( + input: { fields: $fields2 } + ) { + someClass { + id + } } } `, @@ -7175,22 +9071,24 @@ describe('ParseGraphQLServer', () => { someClasses( where: { someField: { equalTo: $someFieldValue } } ) { - results { - id - someField + edges { + node { + id + someField + } } } } `, variables: { - id: createResult.data.createSomeClass1.id, + id: createResult.data.createSomeClass1.someClass.id, someFieldValue, }, }); expect(typeof getResult.data.someClass.someField).toEqual('string'); expect(getResult.data.someClass.someField).toEqual(someFieldValue); - expect(getResult.data.someClasses.results.length).toEqual(2); + expect(getResult.data.someClasses.edges.length).toEqual(2); } catch (e) { handleError(e); } @@ -7207,8 +9105,10 @@ describe('ParseGraphQLServer', () => { await apolloClient.mutate({ mutation: gql` mutation CreateClass($schemaFields: SchemaFieldsInput) { - createClass(name: "SomeClass", schemaFields: $schemaFields) { - name + createClass( + input: { name: "SomeClass", schemaFields: $schemaFields } + ) { + clientMutationId } } `, @@ -7232,8 +9132,10 @@ describe('ParseGraphQLServer', () => { const createResult = await apolloClient.mutate({ mutation: gql` mutation CreateSomeObject($fields: CreateSomeClassFieldsInput) { - createSomeClass(fields: $fields) { - id + createSomeClass(input: { fields: $fields }) { + someClass { + id + } } } `, @@ -7257,24 +9159,26 @@ describe('ParseGraphQLServer', () => { } } someClasses(where: { someField: { exists: true } }) { - results { - id - someField { - latitude - longitude + edges { + node { + id + someField { + latitude + longitude + } } } } } `, variables: { - id: createResult.data.createSomeClass.id, + id: createResult.data.createSomeClass.someClass.id, }, }); expect(typeof getResult.data.someClass.someField).toEqual('object'); expect(getResult.data.someClass.someField).toEqual(someFieldValue); - expect(getResult.data.someClasses.results.length).toEqual(1); + expect(getResult.data.someClasses.edges.length).toEqual(1); } catch (e) { handleError(e); } @@ -7295,8 +9199,10 @@ describe('ParseGraphQLServer', () => { await apolloClient.mutate({ mutation: gql` mutation CreateClass($schemaFields: SchemaFieldsInput) { - createClass(name: "SomeClass", schemaFields: $schemaFields) { - name + createClass( + input: { name: "SomeClass", schemaFields: $schemaFields } + ) { + clientMutationId } } `, @@ -7320,8 +9226,10 @@ describe('ParseGraphQLServer', () => { const createResult = await apolloClient.mutate({ mutation: gql` mutation CreateSomeObject($fields: CreateSomeClassFieldsInput) { - createSomeClass(fields: $fields) { - id + createSomeClass(input: { fields: $fields }) { + someClass { + id + } } } `, @@ -7342,18 +9250,20 @@ describe('ParseGraphQLServer', () => { } } someClasses(where: { somePolygonField: { exists: true } }) { - results { - id - somePolygonField { - latitude - longitude + edges { + node { + id + somePolygonField { + latitude + longitude + } } } } } `, variables: { - id: createResult.data.createSomeClass.id, + id: createResult.data.createSomeClass.someClass.id, }, }); @@ -7366,7 +9276,7 @@ describe('ParseGraphQLServer', () => { __typename: 'GeoPoint', })) ); - expect(getResult.data.someClasses.results.length).toEqual(1); + expect(getResult.data.someClasses.edges.length).toEqual(1); } catch (e) { handleError(e); } @@ -7393,8 +9303,10 @@ describe('ParseGraphQLServer', () => { const createResult = await apolloClient.mutate({ mutation: gql` mutation CreateSomeObject($fields: CreateSomeClassFieldsInput) { - createSomeClass(fields: $fields) { - id + createSomeClass(input: { fields: $fields }) { + someClass { + id + } } } `, @@ -7414,7 +9326,7 @@ describe('ParseGraphQLServer', () => { } `, variables: { - id: createResult.data.createSomeClass.id, + id: createResult.data.createSomeClass.someClass.id, }, }); @@ -7433,28 +9345,32 @@ describe('ParseGraphQLServer', () => { $id: ID! $fields: UpdateSomeClassFieldsInput ) { - updateSomeClass(id: $id, fields: $fields) { - updatedAt + updateSomeClass(input: { id: $id, fields: $fields }) { + someClass { + updatedAt + } } } `, variables: { - id: createResult.data.createSomeClass.id, + id: createResult.data.createSomeClass.someClass.id, fields: { someField: updatedSomeFieldValue, }, }, }); - const { updatedAt } = updatedResult.data.updateSomeClass; + const { updatedAt } = updatedResult.data.updateSomeClass.someClass; expect(updatedAt).toBeDefined(); const findResult = await apolloClient.query({ query: gql` query FindSomeObject($where: SomeClassWhereInput!) { someClasses(where: $where) { - results { - id + edges { + node { + id + } } } } @@ -7467,9 +9383,11 @@ describe('ParseGraphQLServer', () => { }, }, }); - const findResults = findResult.data.someClasses.results; + const findResults = findResult.data.someClasses.edges; expect(findResults.length).toBe(1); - expect(findResults[0].id).toBe(createResult.data.createSomeClass.id); + expect(findResults[0].node.id).toBe( + createResult.data.createSomeClass.someClass.id + ); }); }); @@ -7486,7 +9404,7 @@ describe('ParseGraphQLServer', () => { query: gql` query GetSomeObject($id: ID!) { get: user(id: $id) { - id + objectId } } `, @@ -7495,7 +9413,7 @@ describe('ParseGraphQLServer', () => { }, }); - expect(getResult.data.get.id).toEqual(user.id); + expect(getResult.data.get.objectId).toEqual(user.id); }); it('should support Installation class', async () => { @@ -7510,7 +9428,7 @@ describe('ParseGraphQLServer', () => { query: gql` query GetSomeObject($id: ID!) { get: installation(id: $id) { - id + objectId } } `, @@ -7519,7 +9437,7 @@ describe('ParseGraphQLServer', () => { }, }); - expect(getResult.data.get.id).toEqual(installation.id); + expect(getResult.data.get.objectId).toEqual(installation.id); }); it('should support Role class', async () => { @@ -7534,7 +9452,7 @@ describe('ParseGraphQLServer', () => { query: gql` query GetSomeObject($id: ID!) { get: role(id: $id) { - id + objectId } } `, @@ -7543,7 +9461,7 @@ describe('ParseGraphQLServer', () => { }, }); - expect(getResult.data.get.id).toEqual(role.id); + expect(getResult.data.get.objectId).toEqual(role.id); }); it('should support Session class', async () => { @@ -7560,6 +9478,7 @@ describe('ParseGraphQLServer', () => { query GetSomeObject($id: ID!) { get: session(id: $id) { id + objectId } } `, @@ -7573,7 +9492,7 @@ describe('ParseGraphQLServer', () => { }, }); - expect(getResult.data.get.id).toEqual(session.id); + expect(getResult.data.get.objectId).toEqual(session.id); }); it('should support Product class', async () => { @@ -7596,7 +9515,7 @@ describe('ParseGraphQLServer', () => { query: gql` query GetSomeObject($id: ID!) { get: product(id: $id) { - id + objectId } } `, @@ -7610,7 +9529,7 @@ describe('ParseGraphQLServer', () => { }, }); - expect(getResult.data.get.id).toEqual(product.id); + expect(getResult.data.get.objectId).toEqual(product.id); }); }); }); @@ -7709,6 +9628,7 @@ describe('ParseGraphQLServer', () => { variables: { user: { username: 'somefolk', + password: 'somepassword', }, }, }); diff --git a/spec/graphQLObjectsQueries.js b/spec/graphQLObjectsQueries.js new file mode 100644 index 0000000000..40bdca8b6d --- /dev/null +++ b/spec/graphQLObjectsQueries.js @@ -0,0 +1,158 @@ +const { offsetToCursor } = require('graphql-relay'); +const { + calculateSkipAndLimit, +} = require('../lib/GraphQL/helpers/objectsQueries'); + +describe('GraphQL objectsQueries', () => { + describe('calculateSkipAndLimit', () => { + it('should fail with invalid params', () => { + expect(() => calculateSkipAndLimit(-1)).toThrow( + jasmine.stringMatching('Skip should be a positive number') + ); + expect(() => calculateSkipAndLimit(1, -1)).toThrow( + jasmine.stringMatching('First should be a positive number') + ); + expect(() => calculateSkipAndLimit(1, 1, offsetToCursor(-1))).toThrow( + jasmine.stringMatching('After is not a valid curso') + ); + expect(() => calculateSkipAndLimit(1, 1, offsetToCursor(1), -1)).toThrow( + jasmine.stringMatching('Last should be a positive number') + ); + expect(() => + calculateSkipAndLimit(1, 1, offsetToCursor(1), 1, offsetToCursor(-1)) + ).toThrow(jasmine.stringMatching('Before is not a valid curso')); + }); + + it('should work only with skip', () => { + expect(calculateSkipAndLimit(10)).toEqual({ + skip: 10, + limit: undefined, + needToPreCount: false, + }); + }); + + it('should work only with after', () => { + expect( + calculateSkipAndLimit(undefined, undefined, offsetToCursor(9)) + ).toEqual({ + skip: 10, + limit: undefined, + needToPreCount: false, + }); + }); + + it('should work with limit and after', () => { + expect(calculateSkipAndLimit(10, undefined, offsetToCursor(9))).toEqual({ + skip: 20, + limit: undefined, + needToPreCount: false, + }); + }); + + it('first alone should set the limit', () => { + expect(calculateSkipAndLimit(10, 30, offsetToCursor(9))).toEqual({ + skip: 20, + limit: 30, + needToPreCount: false, + }); + }); + + it('if before cursor is less than skipped items, no objects will be returned', () => { + expect( + calculateSkipAndLimit( + 10, + 30, + offsetToCursor(9), + undefined, + offsetToCursor(5) + ) + ).toEqual({ + skip: 20, + limit: 0, + needToPreCount: false, + }); + }); + + it('if before cursor is greater than returned objects set by limit, nothing is changed', () => { + expect( + calculateSkipAndLimit( + 10, + 30, + offsetToCursor(9), + undefined, + offsetToCursor(100) + ) + ).toEqual({ + skip: 20, + limit: 30, + needToPreCount: false, + }); + }); + + it('if before cursor is less than returned objects set by limit, limit is adjusted', () => { + expect( + calculateSkipAndLimit( + 10, + 30, + offsetToCursor(9), + undefined, + offsetToCursor(40) + ) + ).toEqual({ + skip: 20, + limit: 20, + needToPreCount: false, + }); + }); + + it('last should work alone but requires pre count', () => { + expect( + calculateSkipAndLimit(undefined, undefined, undefined, 10) + ).toEqual({ + skip: undefined, + limit: 10, + needToPreCount: true, + }); + }); + + it('last should be adjusted to max limit', () => { + expect( + calculateSkipAndLimit(undefined, undefined, undefined, 10, undefined, 5) + ).toEqual({ + skip: undefined, + limit: 5, + needToPreCount: true, + }); + }); + + it('no objects will be returned if last is equal to 0', () => { + expect(calculateSkipAndLimit(undefined, undefined, undefined, 0)).toEqual( + { + skip: undefined, + limit: 0, + needToPreCount: false, + } + ); + }); + + it('nothing changes if last is bigger than the calculared limit', () => { + expect( + calculateSkipAndLimit(10, 30, offsetToCursor(9), 30, offsetToCursor(40)) + ).toEqual({ + skip: 20, + limit: 20, + needToPreCount: false, + }); + }); + + it('If last is small than limit, new limit is calculated', () => { + expect( + calculateSkipAndLimit(10, 30, offsetToCursor(9), 10, offsetToCursor(40)) + ).toEqual({ + skip: 30, + limit: 10, + needToPreCount: false, + }); + }); + }); +}); diff --git a/src/GraphQL/ParseGraphQLSchema.js b/src/GraphQL/ParseGraphQLSchema.js index 5a12189f65..d01f5eee0a 100644 --- a/src/GraphQL/ParseGraphQLSchema.js +++ b/src/GraphQL/ParseGraphQLSchema.js @@ -1,5 +1,10 @@ import Parse from 'parse/node'; -import { GraphQLSchema, GraphQLObjectType } from 'graphql'; +import { + GraphQLSchema, + GraphQLObjectType, + DocumentNode, + GraphQLNamedType, +} from 'graphql'; import { mergeSchemas, SchemaDirectiveVisitor } from 'graphql-tools'; import requiredParameter from '../requiredParameter'; import * as defaultGraphQLTypes from './loaders/defaultGraphQLTypes'; @@ -16,6 +21,7 @@ import { toGraphQLError } from './parseGraphQLUtils'; import * as schemaDirectives from './loaders/schemaDirectives'; import * as schemaTypes from './loaders/schemaTypes'; import { getFunctionNames } from '../triggers'; +import * as defaultRelaySchema from './loaders/defaultRelaySchema'; const RESERVED_GRAPHQL_TYPE_NAMES = [ 'String', @@ -27,10 +33,25 @@ const RESERVED_GRAPHQL_TYPE_NAMES = [ 'Query', 'Mutation', 'Subscription', + 'CreateFileInput', + 'CreateFilePayload', 'Viewer', - 'SignUpFieldsInput', - 'LogInFieldsInput', + 'SignUpInput', + 'SignUpPayload', + 'LogInInput', + 'LogInPayload', + 'LogOutInput', + 'LogOutPayload', 'CloudCodeFunction', + 'CallCloudCodeInput', + 'CallCloudCodePayload', + 'CreateClassInput', + 'CreateClassPayload', + 'UpdateClassInput', + 'UpdateClassPayload', + 'DeleteClassInput', + 'DeleteClassPayload', + 'PageInfo', ]; const RESERVED_GRAPHQL_QUERY_NAMES = ['health', 'viewer', 'class', 'classes']; const RESERVED_GRAPHQL_MUTATION_NAMES = [ @@ -48,7 +69,14 @@ class ParseGraphQLSchema { databaseController: DatabaseController; parseGraphQLController: ParseGraphQLController; parseGraphQLConfig: ParseGraphQLConfig; - graphQLCustomTypeDefs: any; + log: any; + appId: string; + graphQLCustomTypeDefs: ?( + | string + | GraphQLSchema + | DocumentNode + | GraphQLNamedType[] + ); constructor( params: { @@ -56,6 +84,12 @@ class ParseGraphQLSchema { parseGraphQLController: ParseGraphQLController, log: any, appId: string, + graphQLCustomTypeDefs: ?( + | string + | GraphQLSchema + | DocumentNode + | GraphQLNamedType[] + ), } = {} ) { this.parseGraphQLController = @@ -105,8 +139,10 @@ class ParseGraphQLSchema { this.graphQLSubscriptions = {}; this.graphQLSchemaDirectivesDefinitions = null; this.graphQLSchemaDirectives = {}; + this.relayNodeInterface = null; defaultGraphQLTypes.load(this); + defaultRelaySchema.load(this); schemaTypes.load(this); this._getParseClassesWithConfig(parseClasses, parseGraphQLConfig).forEach( @@ -208,10 +244,16 @@ class ParseGraphQLSchema { return this.graphQLSchema; } - addGraphQLType(type, throwError = false, ignoreReserved = false) { + addGraphQLType( + type, + throwError = false, + ignoreReserved = false, + ignoreConnection = false + ) { if ( (!ignoreReserved && RESERVED_GRAPHQL_TYPE_NAMES.includes(type.name)) || - this.graphQLTypes.find(existingType => existingType.name === type.name) + this.graphQLTypes.find(existingType => existingType.name === type.name) || + (!ignoreConnection && type.name.endsWith('Connection')) ) { const message = `Type ${type.name} could not be added to the auto schema because it collided with an existing type.`; if (throwError) { diff --git a/src/GraphQL/helpers/objectsQueries.js b/src/GraphQL/helpers/objectsQueries.js index 36ebe4f09c..d374db586c 100644 --- a/src/GraphQL/helpers/objectsQueries.js +++ b/src/GraphQL/helpers/objectsQueries.js @@ -1,4 +1,5 @@ import Parse from 'parse/node'; +import { offsetToCursor, cursorToOffset } from 'graphql-relay'; import rest from '../../rest'; import { transformQueryInputToParse } from '../transformers/query'; @@ -51,8 +52,11 @@ const findObjects = async ( className, where, order, - skip, - limit, + skipInput, + first, + after, + last, + before, keys, include, includeAll, @@ -68,11 +72,52 @@ const findObjects = async ( if (!where) { where = {}; } - transformQueryInputToParse(where, fields); + transformQueryInputToParse(where, fields, className); + + const skipAndLimitCalculation = calculateSkipAndLimit( + skipInput, + first, + after, + last, + before, + config.maxLimit + ); + let { skip } = skipAndLimitCalculation; + const { limit, needToPreCount } = skipAndLimitCalculation; + let preCount = undefined; + if (needToPreCount) { + const preCountOptions = { + limit: 0, + count: true, + }; + if (readPreference) { + preCountOptions.readPreference = readPreference; + } + if (Object.keys(where).length > 0 && subqueryReadPreference) { + preCountOptions.subqueryReadPreference = subqueryReadPreference; + } + preCount = ( + await rest.find( + config, + auth, + className, + where, + preCountOptions, + info.clientSDK + ) + ).count; + if ((skip || 0) + limit < preCount) { + skip = preCount - limit; + } + } const options = {}; - if (selectedFields.includes('results')) { + if ( + selectedFields.find( + field => field.startsWith('edges.') || field.startsWith('pageInfo.') + ) + ) { if (limit || limit === 0) { options.limit = limit; } @@ -104,7 +149,12 @@ const findObjects = async ( options.limit = 0; } - if (selectedFields.includes('count')) { + if ( + (selectedFields.includes('count') || + selectedFields.includes('pageInfo.hasPreviousPage') || + selectedFields.includes('pageInfo.hasNextPage')) && + !needToPreCount + ) { options.count = true; } @@ -115,7 +165,151 @@ const findObjects = async ( options.subqueryReadPreference = subqueryReadPreference; } - return rest.find(config, auth, className, where, options, info.clientSDK); + let results, count; + if (options.count || !options.limit || (options.limit && options.limit > 0)) { + const findResult = await rest.find( + config, + auth, + className, + where, + options, + info.clientSDK + ); + results = findResult.results; + count = findResult.count; + } + + let edges = null; + let pageInfo = null; + if (results) { + edges = results.map((result, index) => ({ + cursor: offsetToCursor((skip || 0) + index), + node: result, + })); + + pageInfo = { + hasPreviousPage: + ((preCount && preCount > 0) || (count && count > 0)) && + skip !== undefined && + skip > 0, + startCursor: offsetToCursor(skip || 0), + endCursor: offsetToCursor((skip || 0) + (results.length || 1) - 1), + hasNextPage: (preCount || count) > (skip || 0) + results.length, + }; + } + + return { + edges, + pageInfo, + count: preCount || count, + }; +}; + +const calculateSkipAndLimit = ( + skipInput, + first, + after, + last, + before, + maxLimit +) => { + let skip = undefined; + let limit = undefined; + let needToPreCount = false; + + // Validates the skip input + if (skipInput || skipInput === 0) { + if (skipInput < 0) { + throw new Parse.Error( + Parse.Error.INVALID_QUERY, + 'Skip should be a positive number' + ); + } + skip = skipInput; + } + + // Validates the after param + if (after) { + after = cursorToOffset(after); + if ((!after && after !== 0) || after < 0) { + throw new Parse.Error( + Parse.Error.INVALID_QUERY, + 'After is not a valid cursor' + ); + } + + // If skip and after are passed, a new skip is calculated by adding them + skip = (skip || 0) + (after + 1); + } + + // Validates the first param + if (first || first === 0) { + if (first < 0) { + throw new Parse.Error( + Parse.Error.INVALID_QUERY, + 'First should be a positive number' + ); + } + + // The first param is translated to the limit param of the Parse legacy API + limit = first; + } + + // Validates the before param + if (before || before === 0) { + // This method converts the cursor to the index of the object + before = cursorToOffset(before); + if ((!before && before !== 0) || before < 0) { + throw new Parse.Error( + Parse.Error.INVALID_QUERY, + 'Before is not a valid cursor' + ); + } + + if ((skip || 0) >= before) { + // If the before index is less then the skip, no objects will be returned + limit = 0; + } else if ((!limit && limit !== 0) || (skip || 0) + limit > before) { + // If there is no limit set, the limit is calculated. Or, if the limit (plus skip) is bigger than the before index, the new limit is set. + limit = before - (skip || 0); + } + } + + // Validates the last param + if (last || last === 0) { + if (last < 0) { + throw new Parse.Error( + Parse.Error.INVALID_QUERY, + 'Last should be a positive number' + ); + } + + if (last > maxLimit) { + // Last can't be bigger than Parse server maxLimit config. + last = maxLimit; + } + + if (limit || limit === 0) { + // If there is a previous limit set, it may be adjusted + if (last < limit) { + // if last is less than the current limit + skip = (skip || 0) + (limit - last); // The skip is adjusted + limit = last; // the limit is adjusted + } + } else if (last === 0) { + // No objects will be returned + limit = 0; + } else { + // No previous limit set, the limit will be equal to last and pre count is needed. + limit = last; + needToPreCount = true; + } + } + return { + skip, + limit, + needToPreCount, + }; }; -export { getObject, findObjects }; +export { getObject, findObjects, calculateSkipAndLimit }; diff --git a/src/GraphQL/loaders/defaultGraphQLTypes.js b/src/GraphQL/loaders/defaultGraphQLTypes.js index 284e079ef9..7a36953e2d 100644 --- a/src/GraphQL/loaders/defaultGraphQLTypes.js +++ b/src/GraphQL/loaders/defaultGraphQLTypes.js @@ -588,10 +588,15 @@ const CLASS_NAME_ATT = { type: new GraphQLNonNull(GraphQLString), }; +const GLOBAL_OR_OBJECT_ID_ATT = { + description: + 'This is the object id. You can use either the global or the object id.', + type: OBJECT_ID, +}; + const OBJECT_ID_ATT = { description: 'This is the object id.', type: OBJECT_ID, - resolve: ({ objectId }) => objectId, }; const CREATED_AT_ATT = { @@ -611,7 +616,7 @@ const INPUT_FIELDS = { }; const CREATE_RESULT_FIELDS = { - id: OBJECT_ID_ATT, + objectId: OBJECT_ID_ATT, createdAt: CREATED_AT_ATT, }; @@ -637,7 +642,7 @@ const PARSE_OBJECT = new GraphQLInterfaceType({ }); const SESSION_TOKEN_ATT = { - description: 'The user session token', + description: 'The current user session token.', type: new GraphQLNonNull(GraphQLString), }; @@ -926,6 +931,25 @@ const options = { type: GraphQLString, }; +const ID_WHERE_INPUT = new GraphQLInputObjectType({ + name: 'IdWhereInput', + description: + 'The IdWhereInput input type is used in operations that involve filtering objects by an id.', + fields: { + equalTo: equalTo(GraphQLID), + notEqualTo: notEqualTo(GraphQLID), + lessThan: lessThan(GraphQLID), + lessThanOrEqualTo: lessThanOrEqualTo(GraphQLID), + greaterThan: greaterThan(GraphQLID), + greaterThanOrEqualTo: greaterThanOrEqualTo(GraphQLID), + in: inOp(GraphQLID), + notIn: notIn(GraphQLID), + exists, + inQueryKey, + notInQueryKey, + }, +}); + const STRING_WHERE_INPUT = new GraphQLInputObjectType({ name: 'StringWhereInput', description: @@ -1164,19 +1188,6 @@ const POLYGON_WHERE_INPUT = new GraphQLInputObjectType({ }, }); -const FIND_RESULT = new GraphQLObjectType({ - name: 'FindResult', - description: - 'The FindResult object type is used in the find queries to return the data of the matched objects.', - fields: { - results: { - description: 'This is the objects returned by the query', - type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(OBJECT))), - }, - count: COUNT_ATT, - }, -}); - const ELEMENT = new GraphQLObjectType({ name: 'Element', description: "The Element object type is used to return array items' value.", @@ -1247,6 +1258,7 @@ const load = parseGraphQLSchema => { parseGraphQLSchema.addGraphQLType(CENTER_SPHERE_INPUT, true); parseGraphQLSchema.addGraphQLType(GEO_WITHIN_INPUT, true); parseGraphQLSchema.addGraphQLType(GEO_INTERSECTS_INPUT, true); + parseGraphQLSchema.addGraphQLType(ID_WHERE_INPUT, true); parseGraphQLSchema.addGraphQLType(STRING_WHERE_INPUT, true); parseGraphQLSchema.addGraphQLType(NUMBER_WHERE_INPUT, true); parseGraphQLSchema.addGraphQLType(BOOLEAN_WHERE_INPUT, true); @@ -1258,9 +1270,7 @@ const load = parseGraphQLSchema => { parseGraphQLSchema.addGraphQLType(FILE_WHERE_INPUT, true); parseGraphQLSchema.addGraphQLType(GEO_POINT_WHERE_INPUT, true); parseGraphQLSchema.addGraphQLType(POLYGON_WHERE_INPUT, true); - parseGraphQLSchema.addGraphQLType(FIND_RESULT, true); parseGraphQLSchema.addGraphQLType(ELEMENT, true); - parseGraphQLSchema.addGraphQLType(OBJECT_ID, true); parseGraphQLSchema.addGraphQLType(ACL_INPUT, true); parseGraphQLSchema.addGraphQLType(USER_ACL_INPUT, true); parseGraphQLSchema.addGraphQLType(ROLE_ACL_INPUT, true); @@ -1296,6 +1306,7 @@ export { POLYGON, OBJECT_ID, CLASS_NAME_ATT, + GLOBAL_OR_OBJECT_ID_ATT, OBJECT_ID_ATT, UPDATED_AT_ATT, CREATED_AT_ATT, @@ -1337,6 +1348,7 @@ export { notInQueryKey, matchesRegex, options, + ID_WHERE_INPUT, STRING_WHERE_INPUT, NUMBER_WHERE_INPUT, BOOLEAN_WHERE_INPUT, @@ -1348,7 +1360,6 @@ export { FILE_WHERE_INPUT, GEO_POINT_WHERE_INPUT, POLYGON_WHERE_INPUT, - FIND_RESULT, ARRAY_RESULT, ELEMENT, ACL_INPUT, diff --git a/src/GraphQL/loaders/defaultRelaySchema.js b/src/GraphQL/loaders/defaultRelaySchema.js new file mode 100644 index 0000000000..3837bd5b9f --- /dev/null +++ b/src/GraphQL/loaders/defaultRelaySchema.js @@ -0,0 +1,51 @@ +import { nodeDefinitions, fromGlobalId } from 'graphql-relay'; +import getFieldNames from 'graphql-list-fields'; +import * as defaultGraphQLTypes from './defaultGraphQLTypes'; +import * as objectsQueries from '../helpers/objectsQueries'; +import { extractKeysAndInclude } from './parseClassTypes'; + +const GLOBAL_ID_ATT = { + description: 'This is the global id.', + type: defaultGraphQLTypes.OBJECT_ID, +}; + +const load = parseGraphQLSchema => { + const { nodeInterface, nodeField } = nodeDefinitions( + async (globalId, context, queryInfo) => { + try { + const { type, id } = fromGlobalId(globalId); + const { config, auth, info } = context; + const selectedFields = getFieldNames(queryInfo); + + const { keys, include } = extractKeysAndInclude(selectedFields); + + return { + className: type, + ...(await objectsQueries.getObject( + type, + id, + keys, + include, + undefined, + undefined, + config, + auth, + info + )), + }; + } catch (e) { + parseGraphQLSchema.handleError(e); + } + }, + obj => { + return parseGraphQLSchema.parseClassTypes[obj.className] + .classGraphQLOutputType; + } + ); + + parseGraphQLSchema.addGraphQLType(nodeInterface, true); + parseGraphQLSchema.relayNodeInterface = nodeInterface; + parseGraphQLSchema.addGraphQLQuery('node', nodeField, true); +}; + +export { GLOBAL_ID_ATT, load }; diff --git a/src/GraphQL/loaders/filesMutations.js b/src/GraphQL/loaders/filesMutations.js index 58aefa72b3..cf906a6898 100644 --- a/src/GraphQL/loaders/filesMutations.js +++ b/src/GraphQL/loaders/filesMutations.js @@ -1,80 +1,97 @@ import { GraphQLNonNull } from 'graphql'; +import { mutationWithClientMutationId } from 'graphql-relay'; import { GraphQLUpload } from 'graphql-upload'; import Parse from 'parse/node'; import * as defaultGraphQLTypes from './defaultGraphQLTypes'; import logger from '../../logger'; const load = parseGraphQLSchema => { - parseGraphQLSchema.addGraphQLMutation( - 'createFile', - { - description: - 'The create mutation can be used to create and upload a new file.', - args: { - upload: { - description: 'This is the new file to be created and uploaded', - type: new GraphQLNonNull(GraphQLUpload), - }, + const createMutation = mutationWithClientMutationId({ + name: 'CreateFile', + description: + 'The createFile mutation can be used to create and upload a new file.', + inputFields: { + upload: { + description: 'This is the new file to be created and uploaded.', + type: new GraphQLNonNull(GraphQLUpload), }, - type: new GraphQLNonNull(defaultGraphQLTypes.FILE_INFO), - async resolve(_source, args, context) { - try { - const { upload } = args; - const { config } = context; + }, + outputFields: { + fileInfo: { + description: 'This is the created file info.', + type: new GraphQLNonNull(defaultGraphQLTypes.FILE_INFO), + }, + }, + mutateAndGetPayload: async (args, context) => { + try { + const { upload } = args; + const { config } = context; - const { createReadStream, filename, mimetype } = await upload; - let data = null; - if (createReadStream) { - const stream = createReadStream(); - data = await new Promise((resolve, reject) => { - const chunks = []; - stream - .on('error', reject) - .on('data', chunk => chunks.push(chunk)) - .on('end', () => resolve(Buffer.concat(chunks))); - }); - } + const { createReadStream, filename, mimetype } = await upload; + let data = null; + if (createReadStream) { + const stream = createReadStream(); + data = await new Promise((resolve, reject) => { + const chunks = []; + stream + .on('error', reject) + .on('data', chunk => chunks.push(chunk)) + .on('end', () => resolve(Buffer.concat(chunks))); + }); + } - if (!data || !data.length) { - throw new Parse.Error( - Parse.Error.FILE_SAVE_ERROR, - 'Invalid file upload.' - ); - } + if (!data || !data.length) { + throw new Parse.Error( + Parse.Error.FILE_SAVE_ERROR, + 'Invalid file upload.' + ); + } - if (filename.length > 128) { - throw new Parse.Error( - Parse.Error.INVALID_FILE_NAME, - 'Filename too long.' - ); - } + if (filename.length > 128) { + throw new Parse.Error( + Parse.Error.INVALID_FILE_NAME, + 'Filename too long.' + ); + } - if (!filename.match(/^[_a-zA-Z0-9][a-zA-Z0-9@\.\ ~_-]*$/)) { - throw new Parse.Error( - Parse.Error.INVALID_FILE_NAME, - 'Filename contains invalid characters.' - ); - } + if (!filename.match(/^[_a-zA-Z0-9][a-zA-Z0-9@\.\ ~_-]*$/)) { + throw new Parse.Error( + Parse.Error.INVALID_FILE_NAME, + 'Filename contains invalid characters.' + ); + } - try { - return await config.filesController.createFile( + try { + return { + fileInfo: await config.filesController.createFile( config, filename, data, mimetype - ); - } catch (e) { - logger.error('Error creating a file: ', e); - throw new Parse.Error( - Parse.Error.FILE_SAVE_ERROR, - `Could not store file: ${filename}.` - ); - } + ), + }; } catch (e) { - parseGraphQLSchema.handleError(e); + logger.error('Error creating a file: ', e); + throw new Parse.Error( + Parse.Error.FILE_SAVE_ERROR, + `Could not store file: ${filename}.` + ); } - }, + } catch (e) { + parseGraphQLSchema.handleError(e); + } }, + }); + + parseGraphQLSchema.addGraphQLType( + createMutation.args.input.type.ofType, + true, + true + ); + parseGraphQLSchema.addGraphQLType(createMutation.type, true, true); + parseGraphQLSchema.addGraphQLMutation( + 'createFile', + createMutation, true, true ); diff --git a/src/GraphQL/loaders/functionsMutations.js b/src/GraphQL/loaders/functionsMutations.js index 5c3f5d0659..418791583e 100644 --- a/src/GraphQL/loaders/functionsMutations.js +++ b/src/GraphQL/loaders/functionsMutations.js @@ -1,4 +1,5 @@ import { GraphQLNonNull, GraphQLEnumType } from 'graphql'; +import { mutationWithClientMutationId } from 'graphql-relay'; import { FunctionsRouter } from '../../Routers/FunctionsRouter'; import * as defaultGraphQLTypes from './defaultGraphQLTypes'; @@ -21,28 +22,34 @@ const load = parseGraphQLSchema => { true ); - parseGraphQLSchema.addGraphQLMutation( - 'callCloudCode', - { - description: - 'The call mutation can be used to invoke a cloud code function.', - args: { - functionName: { - description: 'This is the function to be called.', - type: new GraphQLNonNull(cloudCodeFunctionEnum), - }, - params: { - description: 'These are the params to be passed to the function.', - type: defaultGraphQLTypes.OBJECT, - }, + const callCloudCodeMutation = mutationWithClientMutationId({ + name: 'CallCloudCode', + description: + 'The callCloudCode mutation can be used to invoke a cloud code function.', + inputFields: { + functionName: { + description: 'This is the function to be called.', + type: new GraphQLNonNull(cloudCodeFunctionEnum), + }, + params: { + description: 'These are the params to be passed to the function.', + type: defaultGraphQLTypes.OBJECT, }, - type: defaultGraphQLTypes.ANY, - async resolve(_source, args, context) { - try { - const { functionName, params } = args; - const { config, auth, info } = context; + }, + outputFields: { + result: { + description: + 'This is the result value of the cloud code function execution.', + type: defaultGraphQLTypes.ANY, + }, + }, + mutateAndGetPayload: async (args, context) => { + try { + const { functionName, params } = args; + const { config, auth, info } = context; - return (await FunctionsRouter.handleCloudFunction({ + return { + result: (await FunctionsRouter.handleCloudFunction({ params: { functionName, }, @@ -50,12 +57,23 @@ const load = parseGraphQLSchema => { auth, info, body: params, - })).response.result; - } catch (e) { - parseGraphQLSchema.handleError(e); - } - }, + })).response.result, + }; + } catch (e) { + parseGraphQLSchema.handleError(e); + } }, + }); + + parseGraphQLSchema.addGraphQLType( + callCloudCodeMutation.args.input.type.ofType, + true, + true + ); + parseGraphQLSchema.addGraphQLType(callCloudCodeMutation.type, true, true); + parseGraphQLSchema.addGraphQLMutation( + 'callCloudCode', + callCloudCodeMutation, true, true ); diff --git a/src/GraphQL/loaders/parseClassMutations.js b/src/GraphQL/loaders/parseClassMutations.js index e62363b87d..13294b1175 100644 --- a/src/GraphQL/loaders/parseClassMutations.js +++ b/src/GraphQL/loaders/parseClassMutations.js @@ -1,4 +1,5 @@ import { GraphQLNonNull } from 'graphql'; +import { fromGlobalId, mutationWithClientMutationId } from 'graphql-relay'; import getFieldNames from 'graphql-list-fields'; import * as defaultGraphQLTypes from './defaultGraphQLTypes'; import { @@ -17,13 +18,16 @@ const getOnlyRequiredFields = ( includedFieldsString, nativeObjectFields ) => { - const includedFields = includedFieldsString.split(','); - const selectedFields = selectedFieldsString.split(','); + const includedFields = includedFieldsString + ? includedFieldsString.split(',') + : []; + const selectedFields = selectedFieldsString + ? selectedFieldsString.split(',') + : []; const missingFields = selectedFields .filter( field => - !nativeObjectFields.includes(field) || - includedFields.includes(field) + !nativeObjectFields.includes(field) || includedFields.includes(field) ) .join(','); if (!missingFields.length) { @@ -40,6 +44,8 @@ const load = function( ) { const className = parseClass.className; const graphQLClassName = transformClassNameToGraphQL(className); + const getGraphQLQueryName = + graphQLClassName.charAt(0).toLowerCase() + graphQLClassName.slice(1); const { create: isCreateEnabled = true, @@ -55,18 +61,25 @@ const load = function( if (isCreateEnabled) { const createGraphQLMutationName = `create${graphQLClassName}`; - parseGraphQLSchema.addGraphQLMutation(createGraphQLMutationName, { + const createGraphQLMutation = mutationWithClientMutationId({ + name: `Create${graphQLClassName}`, description: `The ${createGraphQLMutationName} mutation can be used to create a new object of the ${graphQLClassName} class.`, - args: { + inputFields: { fields: { - description: 'These are the fields used to create the object.', + description: + 'These are the fields that will be used to create the new object.', type: classGraphQLCreateType || defaultGraphQLTypes.OBJECT, }, }, - type: new GraphQLNonNull( - classGraphQLOutputType || defaultGraphQLTypes.OBJECT - ), - async resolve(_source, args, context, mutationInfo) { + outputFields: { + [getGraphQLQueryName]: { + description: 'This is the created object.', + type: new GraphQLNonNull( + classGraphQLOutputType || defaultGraphQLTypes.OBJECT + ), + }, + }, + mutateAndGetPayload: async (args, context, mutationInfo) => { try { let { fields } = args; if (!fields) fields = {}; @@ -85,13 +98,15 @@ const load = function( auth, info ); - const selectedFields = getFieldNames(mutationInfo); + const selectedFields = getFieldNames(mutationInfo) + .filter(field => field.startsWith(`${getGraphQLQueryName}.`)) + .map(field => field.replace(`${getGraphQLQueryName}.`, '')); const { keys, include } = extractKeysAndInclude(selectedFields); const { keys: requiredKeys, needGet } = getOnlyRequiredFields( fields, keys, include, - ['id', 'createdAt', 'updatedAt'] + ['id', 'objectId', 'createdAt', 'updatedAt'] ); let optimizedObject = {}; if (needGet) { @@ -108,37 +123,65 @@ const load = function( ); } return { - ...createdObject, - updatedAt: createdObject.createdAt, - ...parseFields, - ...optimizedObject, + [getGraphQLQueryName]: { + ...createdObject, + updatedAt: createdObject.createdAt, + ...parseFields, + ...optimizedObject, + }, }; } catch (e) { parseGraphQLSchema.handleError(e); } }, }); + + if ( + parseGraphQLSchema.addGraphQLType( + createGraphQLMutation.args.input.type.ofType + ) && + parseGraphQLSchema.addGraphQLType(createGraphQLMutation.type) + ) { + parseGraphQLSchema.addGraphQLMutation( + createGraphQLMutationName, + createGraphQLMutation + ); + } } if (isUpdateEnabled) { const updateGraphQLMutationName = `update${graphQLClassName}`; - parseGraphQLSchema.addGraphQLMutation(updateGraphQLMutationName, { + const updateGraphQLMutation = mutationWithClientMutationId({ + name: `Update${graphQLClassName}`, description: `The ${updateGraphQLMutationName} mutation can be used to update an object of the ${graphQLClassName} class.`, - args: { - id: defaultGraphQLTypes.OBJECT_ID_ATT, + inputFields: { + id: defaultGraphQLTypes.GLOBAL_OR_OBJECT_ID_ATT, fields: { - description: 'These are the fields used to update the object.', + description: + 'These are the fields that will be used to update the object.', type: classGraphQLUpdateType || defaultGraphQLTypes.OBJECT, }, }, - type: new GraphQLNonNull( - classGraphQLOutputType || defaultGraphQLTypes.OBJECT - ), - async resolve(_source, args, context, mutationInfo) { + outputFields: { + [getGraphQLQueryName]: { + description: 'This is the updated object.', + type: new GraphQLNonNull( + classGraphQLOutputType || defaultGraphQLTypes.OBJECT + ), + }, + }, + mutateAndGetPayload: async (args, context, mutationInfo) => { try { - const { id, fields } = args; + let { id, fields } = args; + if (!fields) fields = {}; const { config, auth, info } = context; + const globalIdObject = fromGlobalId(id); + + if (globalIdObject.type === className) { + id = globalIdObject.id; + } + const parseFields = await transformTypes('update', fields, { className, parseGraphQLSchema, @@ -154,15 +197,17 @@ const load = function( info ); - const selectedFields = getFieldNames(mutationInfo); + const selectedFields = getFieldNames(mutationInfo) + .filter(field => field.startsWith(`${getGraphQLQueryName}.`)) + .map(field => field.replace(`${getGraphQLQueryName}.`, '')); const { keys, include } = extractKeysAndInclude(selectedFields); - const { keys: requiredKeys, needGet } = getOnlyRequiredFields( fields, keys, include, - ['id', 'updatedAt'] + ['id', 'objectId', 'updatedAt'] ); + let optimizedObject = {}; if (needGet) { optimizedObject = await objectsQueries.getObject( @@ -178,38 +223,69 @@ const load = function( ); } return { - id, - ...updatedObject, - ...parseFields, - ...optimizedObject, + [getGraphQLQueryName]: { + objectId: id, + ...updatedObject, + ...parseFields, + ...optimizedObject, + }, }; } catch (e) { parseGraphQLSchema.handleError(e); } }, }); + + if ( + parseGraphQLSchema.addGraphQLType( + updateGraphQLMutation.args.input.type.ofType + ) && + parseGraphQLSchema.addGraphQLType(updateGraphQLMutation.type) + ) { + parseGraphQLSchema.addGraphQLMutation( + updateGraphQLMutationName, + updateGraphQLMutation + ); + } } if (isDestroyEnabled) { const deleteGraphQLMutationName = `delete${graphQLClassName}`; - parseGraphQLSchema.addGraphQLMutation(deleteGraphQLMutationName, { + const deleteGraphQLMutation = mutationWithClientMutationId({ + name: `Delete${graphQLClassName}`, description: `The ${deleteGraphQLMutationName} mutation can be used to delete an object of the ${graphQLClassName} class.`, - args: { - id: defaultGraphQLTypes.OBJECT_ID_ATT, + inputFields: { + id: defaultGraphQLTypes.GLOBAL_OR_OBJECT_ID_ATT, + }, + outputFields: { + [getGraphQLQueryName]: { + description: 'This is the deleted object.', + type: new GraphQLNonNull( + classGraphQLOutputType || defaultGraphQLTypes.OBJECT + ), + }, }, - type: new GraphQLNonNull( - classGraphQLOutputType || defaultGraphQLTypes.OBJECT - ), - async resolve(_source, args, context, mutationInfo) { + mutateAndGetPayload: async (args, context, mutationInfo) => { try { - const { id } = args; + let { id } = args; const { config, auth, info } = context; - const selectedFields = getFieldNames(mutationInfo); - const { keys, include } = extractKeysAndInclude(selectedFields); + const globalIdObject = fromGlobalId(id); + + if (globalIdObject.type === className) { + id = globalIdObject.id; + } + + const selectedFields = getFieldNames(mutationInfo) + .filter(field => field.startsWith(`${getGraphQLQueryName}.`)) + .map(field => field.replace(`${getGraphQLQueryName}.`, '')); + const { keys, include } = extractKeysAndInclude(selectedFields); let optimizedObject = {}; - const splitedKeys = keys.split(','); - if (splitedKeys.length > 1 || splitedKeys[0] !== 'id') { + if ( + keys && + keys.split(',').filter(key => !['id', 'objectId'].includes(key)) + .length > 0 + ) { optimizedObject = await objectsQueries.getObject( className, id, @@ -229,12 +305,29 @@ const load = function( auth, info ); - return { id, ...optimizedObject }; + return { + [getGraphQLQueryName]: { + objectId: id, + ...optimizedObject, + }, + }; } catch (e) { parseGraphQLSchema.handleError(e); } }, }); + + if ( + parseGraphQLSchema.addGraphQLType( + deleteGraphQLMutation.args.input.type.ofType + ) && + parseGraphQLSchema.addGraphQLType(deleteGraphQLMutation.type) + ) { + parseGraphQLSchema.addGraphQLMutation( + deleteGraphQLMutationName, + deleteGraphQLMutation + ); + } } }; diff --git a/src/GraphQL/loaders/parseClassQueries.js b/src/GraphQL/loaders/parseClassQueries.js index 63041ad94d..1da3f2da3b 100644 --- a/src/GraphQL/loaders/parseClassQueries.js +++ b/src/GraphQL/loaders/parseClassQueries.js @@ -1,4 +1,5 @@ import { GraphQLNonNull } from 'graphql'; +import { fromGlobalId } from 'graphql-relay'; import getFieldNames from 'graphql-list-fields'; import pluralize from 'pluralize'; import * as defaultGraphQLTypes from './defaultGraphQLTypes'; @@ -14,11 +15,18 @@ const getParseClassQueryConfig = function( }; const getQuery = async (className, _source, args, context, queryInfo) => { - const { id, options } = args; + let { id } = args; + const { options } = args; const { readPreference, includeReadPreference } = options || {}; const { config, auth, info } = context; const selectedFields = getFieldNames(queryInfo); + const globalIdObject = fromGlobalId(id); + + if (globalIdObject.type === className) { + id = globalIdObject.id; + } + const { keys, include } = extractKeysAndInclude(selectedFields); return await objectsQueries.getObject( @@ -58,7 +66,7 @@ const load = function( parseGraphQLSchema.addGraphQLQuery(getGraphQLQueryName, { description: `The ${getGraphQLQueryName} query can be used to get an object of the ${graphQLClassName} class by its id.`, args: { - id: defaultGraphQLTypes.OBJECT_ID_ATT, + id: defaultGraphQLTypes.GLOBAL_OR_OBJECT_ID_ATT, options: defaultGraphQLTypes.READ_OPTIONS_ATT, }, type: new GraphQLNonNull( @@ -82,11 +90,20 @@ const load = function( description: `The ${findGraphQLQueryName} query can be used to find objects of the ${graphQLClassName} class.`, args: classGraphQLFindArgs, type: new GraphQLNonNull( - classGraphQLFindResultType || defaultGraphQLTypes.FIND_RESULT + classGraphQLFindResultType || defaultGraphQLTypes.OBJECT ), async resolve(_source, args, context, queryInfo) { try { - const { where, order, skip, limit, options } = args; + const { + where, + order, + skip, + first, + after, + last, + before, + options, + } = args; const { readPreference, includeReadPreference, @@ -97,8 +114,8 @@ const load = function( const { keys, include } = extractKeysAndInclude( selectedFields - .filter(field => field.includes('.')) - .map(field => field.slice(field.indexOf('.') + 1)) + .filter(field => field.startsWith('edges.node.')) + .map(field => field.replace('edges.node.', '')) ); const parseOrder = order && order.join(','); @@ -107,7 +124,10 @@ const load = function( where, parseOrder, skip, - limit, + first, + after, + last, + before, keys, include, false, @@ -117,7 +137,7 @@ const load = function( config, auth, info, - selectedFields.map(field => field.split('.', 1)[0]), + selectedFields, parseClass.fields ); } catch (e) { diff --git a/src/GraphQL/loaders/parseClassTypes.js b/src/GraphQL/loaders/parseClassTypes.js index 0bbbb47fd0..73a3c2da47 100644 --- a/src/GraphQL/loaders/parseClassTypes.js +++ b/src/GraphQL/loaders/parseClassTypes.js @@ -7,6 +7,11 @@ import { GraphQLNonNull, GraphQLEnumType, } from 'graphql'; +import { + globalIdField, + connectionArgs, + connectionDefinitions, +} from 'graphql-relay'; import getFieldNames from 'graphql-list-fields'; import * as defaultGraphQLTypes from './defaultGraphQLTypes'; import * as objectsQueries from '../helpers/objectsQueries'; @@ -30,9 +35,7 @@ const getInputFieldsAndConstraints = function( parseClass, parseClassConfig: ?ParseGraphQLClassConfig ) { - const classFields = Object.keys(parseClass.fields) - .filter(field => field !== 'objectId') - .concat('id'); + const classFields = Object.keys(parseClass.fields).concat('id'); const { inputFields: allowedInputFields, outputFields: allowedOutputFields, @@ -48,8 +51,9 @@ const getInputFieldsAndConstraints = function( // All allowed customs fields const classCustomFields = classFields.filter(field => { - return !Object.keys(defaultGraphQLTypes.PARSE_OBJECT_FIELDS).includes( - field + return ( + !Object.keys(defaultGraphQLTypes.PARSE_OBJECT_FIELDS).includes(field) && + field !== 'id' ); }); @@ -153,7 +157,11 @@ const load = ( ...fields, [field]: { description: `This is the object ${field}.`, - type, + type: + className === '_User' && + (field === 'username' || field === 'password') + ? new GraphQLNonNull(type) + : type, }, }; } else { @@ -209,7 +217,7 @@ const load = ( fields: () => { const fields = { link: { - description: `Link an existing object from ${graphQLClassName} class.`, + description: `Link an existing object from ${graphQLClassName} class. You can use either the global or the object id.`, type: GraphQLID, }, }; @@ -233,17 +241,17 @@ const load = ( fields: () => { const fields = { add: { - description: `Add an existing object from the ${graphQLClassName} class into the relation.`, + description: `Add existing objects from the ${graphQLClassName} class into the relation. You can use either the global or the object ids.`, type: new GraphQLList(defaultGraphQLTypes.OBJECT_ID), }, remove: { - description: `Remove an existing object from the ${graphQLClassName} class out of the relation.`, + description: `Remove existing objects from the ${graphQLClassName} class out of the relation. You can use either the global or the object ids.`, type: new GraphQLList(defaultGraphQLTypes.OBJECT_ID), }, }; if (isCreateEnabled) { fields['createAndAdd'] = { - description: `Create and add an object of the ${graphQLClassName} class into the relation.`, + description: `Create and add objects of the ${graphQLClassName} class into the relation.`, type: new GraphQLList(new GraphQLNonNull(classGraphQLCreateType)), }; } @@ -268,12 +276,12 @@ const load = ( notInQueryKey: defaultGraphQLTypes.notInQueryKey, inQuery: { description: - 'This is the inQuery operator to specify a constraint to select the objects where a field equals to any of the ids in the result of a different query.', + 'This is the inQuery operator to specify a constraint to select the objects where a field equals to any of the object ids in the result of a different query.', type: defaultGraphQLTypes.SUBQUERY_INPUT, }, notInQuery: { description: - 'This is the notInQuery operator to specify a constraint to select the objects where a field do not equal to any of the ids in the result of a different query.', + 'This is the notInQuery operator to specify a constraint to select the objects where a field do not equal to any of the object ids in the result of a different query.', type: defaultGraphQLTypes.SUBQUERY_INPUT, }, }, @@ -298,7 +306,8 @@ const load = ( const type = transformConstraintTypeToGraphQL( parseClass.fields[parseField].type, parseClass.fields[parseField].targetClass, - parseGraphQLSchema.parseClassTypes + parseGraphQLSchema.parseClassTypes, + field ); if (type) { return { @@ -339,11 +348,12 @@ const load = ( const updatedSortFields = { ...sortFields, }; + const value = field === 'id' ? 'objectId' : field; if (asc) { - updatedSortFields[`${field}_ASC`] = { value: field }; + updatedSortFields[`${field}_ASC`] = { value }; } if (desc) { - updatedSortFields[`${field}_DESC`] = { value: `-${field}` }; + updatedSortFields[`${field}_DESC`] = { value: `-${value}` }; } return updatedSortFields; }, {}), @@ -365,10 +375,18 @@ const load = ( : GraphQLString, }, skip: defaultGraphQLTypes.SKIP_ATT, - limit: defaultGraphQLTypes.LIMIT_ATT, + ...connectionArgs, options: defaultGraphQLTypes.READ_OPTIONS_ATT, }; const classGraphQLOutputTypeName = `${graphQLClassName}`; + const interfaces = [ + defaultGraphQLTypes.PARSE_OBJECT, + parseGraphQLSchema.relayNodeInterface, + ]; + const parseObjectFields = { + id: globalIdField(className, obj => obj.objectId), + ...defaultGraphQLTypes.PARSE_OBJECT_FIELDS, + }; const outputFields = () => { return classOutputFields.reduce((fields, field) => { const type = transformOutputTypeToGraphQL( @@ -392,7 +410,16 @@ const load = ( type, async resolve(source, args, context, queryInfo) { try { - const { where, order, skip, limit, options } = args; + const { + where, + order, + skip, + first, + after, + last, + before, + options, + } = args; const { readPreference, includeReadPreference, @@ -403,9 +430,11 @@ const load = ( const { keys, include } = extractKeysAndInclude( selectedFields - .filter(field => field.includes('.')) - .map(field => field.slice(field.indexOf('.') + 1)) + .filter(field => field.startsWith('edges.node.')) + .map(field => field.replace('edges.node.', '')) ); + const parseOrder = order && order.join(','); + return await objectsQueries.findObjects( source[field].className, { @@ -419,9 +448,12 @@ const load = ( }, ...(where || {}), }, - order, + parseOrder, skip, - limit, + first, + after, + last, + before, keys, include, false, @@ -431,8 +463,11 @@ const load = ( config, auth, info, - selectedFields.map(field => field.split('.', 1)[0]), - parseClass.fields + selectedFields, + parseGraphQLSchema.parseClasses.find( + parseClass => + parseClass.className === source[field].className + ).fields ); } catch (e) { parseGraphQLSchema.handleError(e); @@ -491,39 +526,32 @@ const load = ( } else { return fields; } - }, defaultGraphQLTypes.PARSE_OBJECT_FIELDS); + }, parseObjectFields); }; let classGraphQLOutputType = new GraphQLObjectType({ name: classGraphQLOutputTypeName, description: `The ${classGraphQLOutputTypeName} object type is used in operations that involve outputting objects of ${graphQLClassName} class.`, - interfaces: [defaultGraphQLTypes.PARSE_OBJECT], + interfaces, fields: outputFields, }); classGraphQLOutputType = parseGraphQLSchema.addGraphQLType( classGraphQLOutputType ); - const classGraphQLFindResultTypeName = `${graphQLClassName}FindResult`; - let classGraphQLFindResultType = new GraphQLObjectType({ - name: classGraphQLFindResultTypeName, - description: `The ${classGraphQLFindResultTypeName} object type is used in the ${graphQLClassName} find query to return the data of the matched objects.`, - fields: { - results: { - description: 'This is the objects returned by the query', - type: new GraphQLNonNull( - new GraphQLList( - new GraphQLNonNull( - classGraphQLOutputType || defaultGraphQLTypes.OBJECT - ) - ) - ), - }, + const { connectionType, edgeType } = connectionDefinitions({ + name: graphQLClassName, + connectionFields: { count: defaultGraphQLTypes.COUNT_ATT, }, + nodeType: classGraphQLOutputType || defaultGraphQLTypes.OBJECT, }); - classGraphQLFindResultType = parseGraphQLSchema.addGraphQLType( - classGraphQLFindResultType - ); + let classGraphQLFindResultType = undefined; + if ( + parseGraphQLSchema.addGraphQLType(edgeType) && + parseGraphQLSchema.addGraphQLType(connectionType, false, false, true) + ) { + classGraphQLFindResultType = connectionType; + } parseGraphQLSchema.parseClassTypes[className] = { classGraphQLPointerType, @@ -546,67 +574,16 @@ const load = ( const viewerType = new GraphQLObjectType({ name: 'Viewer', description: `The Viewer object type is used in operations that involve outputting the current user data.`, - interfaces: [defaultGraphQLTypes.PARSE_OBJECT], fields: () => ({ - ...outputFields(), sessionToken: defaultGraphQLTypes.SESSION_TOKEN_ATT, + user: { + description: 'This is the current user.', + type: new GraphQLNonNull(classGraphQLOutputType), + }, }), }); - parseGraphQLSchema.viewerType = viewerType; parseGraphQLSchema.addGraphQLType(viewerType, true, true); - - const userSignUpInputTypeName = 'SignUpFieldsInput'; - const userSignUpInputType = new GraphQLInputObjectType({ - name: userSignUpInputTypeName, - description: `The ${userSignUpInputTypeName} input type is used in operations that involve inputting objects of ${graphQLClassName} class when signing up.`, - fields: () => - classCreateFields.reduce((fields, field) => { - const type = transformInputTypeToGraphQL( - parseClass.fields[field].type, - parseClass.fields[field].targetClass, - parseGraphQLSchema.parseClassTypes - ); - if (type) { - return { - ...fields, - [field]: { - description: `This is the object ${field}.`, - type: - field === 'username' || field === 'password' - ? new GraphQLNonNull(type) - : type, - }, - }; - } else { - return fields; - } - }, {}), - }); - parseGraphQLSchema.addGraphQLType(userSignUpInputType, true, true); - - const userLogInInputTypeName = 'LogInFieldsInput'; - const userLogInInputType = new GraphQLInputObjectType({ - name: userLogInInputTypeName, - description: `The ${userLogInInputTypeName} input type is used to login.`, - fields: { - username: { - description: 'This is the username used to log the user in.', - type: new GraphQLNonNull(GraphQLString), - }, - password: { - description: 'This is the password used to log the user in.', - type: new GraphQLNonNull(GraphQLString), - }, - }, - }); - parseGraphQLSchema.addGraphQLType(userLogInInputType, true, true); - - parseGraphQLSchema.parseClassTypes[ - className - ].signUpInputType = userSignUpInputType; - parseGraphQLSchema.parseClassTypes[ - className - ].logInInputType = userLogInInputType; + parseGraphQLSchema.viewerType = viewerType; } }; diff --git a/src/GraphQL/loaders/schemaMutations.js b/src/GraphQL/loaders/schemaMutations.js index 71ac32d43d..12a828e9d7 100644 --- a/src/GraphQL/loaders/schemaMutations.js +++ b/src/GraphQL/loaders/schemaMutations.js @@ -1,5 +1,6 @@ import Parse from 'parse/node'; import { GraphQLNonNull } from 'graphql'; +import { mutationWithClientMutationId } from 'graphql-relay'; import * as schemaTypes from './schemaTypes'; import { transformToParse, @@ -9,135 +10,183 @@ import { enforceMasterKeyAccess } from '../parseGraphQLUtils'; import { getClass } from './schemaQueries'; const load = parseGraphQLSchema => { - parseGraphQLSchema.addGraphQLMutation( - 'createClass', - { - description: - 'The createClass mutation can be used to create the schema for a new object class.', - args: { - name: schemaTypes.CLASS_NAME_ATT, - schemaFields: { - description: "These are the schema's fields of the object class.", - type: schemaTypes.SCHEMA_FIELDS_INPUT, - }, + const createClassMutation = mutationWithClientMutationId({ + name: 'CreateClass', + description: + 'The createClass mutation can be used to create the schema for a new object class.', + inputFields: { + name: schemaTypes.CLASS_NAME_ATT, + schemaFields: { + description: "These are the schema's fields of the object class.", + type: schemaTypes.SCHEMA_FIELDS_INPUT, + }, + }, + outputFields: { + class: { + description: 'This is the created class.', + type: new GraphQLNonNull(schemaTypes.CLASS), }, - type: new GraphQLNonNull(schemaTypes.CLASS), - resolve: async (_source, args, context) => { - try { - const { name, schemaFields } = args; - const { config, auth } = context; - - enforceMasterKeyAccess(auth); - - if (auth.isReadOnly) { - throw new Parse.Error( - Parse.Error.OPERATION_FORBIDDEN, - "read-only masterKey isn't allowed to create a schema." - ); - } - - const schema = await config.database.loadSchema({ clearCache: true }); - const parseClass = await schema.addClassIfNotExists( - name, - transformToParse(schemaFields) + }, + mutateAndGetPayload: async (args, context) => { + try { + const { name, schemaFields } = args; + const { config, auth } = context; + + enforceMasterKeyAccess(auth); + + if (auth.isReadOnly) { + throw new Parse.Error( + Parse.Error.OPERATION_FORBIDDEN, + "read-only masterKey isn't allowed to create a schema." ); - return { + } + + const schema = await config.database.loadSchema({ clearCache: true }); + const parseClass = await schema.addClassIfNotExists( + name, + transformToParse(schemaFields) + ); + return { + class: { name: parseClass.className, schemaFields: transformToGraphQL(parseClass.fields), - }; - } catch (e) { - parseGraphQLSchema.handleError(e); - } - }, + }, + }; + } catch (e) { + parseGraphQLSchema.handleError(e); + } }, + }); + + parseGraphQLSchema.addGraphQLType( + createClassMutation.args.input.type.ofType, true, true ); - + parseGraphQLSchema.addGraphQLType(createClassMutation.type, true, true); parseGraphQLSchema.addGraphQLMutation( - 'updateClass', - { - description: - 'The updateClass mutation can be used to update the schema for an existing object class.', - args: { - name: schemaTypes.CLASS_NAME_ATT, - schemaFields: { - description: "These are the schema's fields of the object class.", - type: schemaTypes.SCHEMA_FIELDS_INPUT, - }, + 'createClass', + createClassMutation, + true, + true + ); + + const updateClassMutation = mutationWithClientMutationId({ + name: 'UpdateClass', + description: + 'The updateClass mutation can be used to update the schema for an existing object class.', + inputFields: { + name: schemaTypes.CLASS_NAME_ATT, + schemaFields: { + description: "These are the schema's fields of the object class.", + type: schemaTypes.SCHEMA_FIELDS_INPUT, }, - type: new GraphQLNonNull(schemaTypes.CLASS), - resolve: async (_source, args, context) => { - try { - const { name, schemaFields } = args; - const { config, auth } = context; - - enforceMasterKeyAccess(auth); - - if (auth.isReadOnly) { - throw new Parse.Error( - Parse.Error.OPERATION_FORBIDDEN, - "read-only masterKey isn't allowed to update a schema." - ); - } - - const schema = await config.database.loadSchema({ clearCache: true }); - const existingParseClass = await getClass(name, schema); - const parseClass = await schema.updateClass( - name, - transformToParse(schemaFields, existingParseClass.fields), - undefined, - undefined, - config.database + }, + outputFields: { + class: { + description: 'This is the updated class.', + type: new GraphQLNonNull(schemaTypes.CLASS), + }, + }, + mutateAndGetPayload: async (args, context) => { + try { + const { name, schemaFields } = args; + const { config, auth } = context; + + enforceMasterKeyAccess(auth); + + if (auth.isReadOnly) { + throw new Parse.Error( + Parse.Error.OPERATION_FORBIDDEN, + "read-only masterKey isn't allowed to update a schema." ); - return { + } + + const schema = await config.database.loadSchema({ clearCache: true }); + const existingParseClass = await getClass(name, schema); + const parseClass = await schema.updateClass( + name, + transformToParse(schemaFields, existingParseClass.fields), + undefined, + undefined, + config.database + ); + return { + class: { name: parseClass.className, schemaFields: transformToGraphQL(parseClass.fields), - }; - } catch (e) { - parseGraphQLSchema.handleError(e); - } - }, + }, + }; + } catch (e) { + parseGraphQLSchema.handleError(e); + } }, + }); + + parseGraphQLSchema.addGraphQLType( + updateClassMutation.args.input.type.ofType, true, true ); - + parseGraphQLSchema.addGraphQLType(updateClassMutation.type, true, true); parseGraphQLSchema.addGraphQLMutation( - 'deleteClass', - { - description: - 'The deleteClass mutation can be used to delete an existing object class.', - args: { - name: schemaTypes.CLASS_NAME_ATT, + 'updateClass', + updateClassMutation, + true, + true + ); + + const deleteClassMutation = mutationWithClientMutationId({ + name: 'DeleteClass', + description: + 'The deleteClass mutation can be used to delete an existing object class.', + inputFields: { + name: schemaTypes.CLASS_NAME_ATT, + }, + outputFields: { + class: { + description: 'This is the deleted class.', + type: new GraphQLNonNull(schemaTypes.CLASS), }, - type: new GraphQLNonNull(schemaTypes.CLASS), - resolve: async (_source, args, context) => { - try { - const { name } = args; - const { config, auth } = context; - - enforceMasterKeyAccess(auth); - - if (auth.isReadOnly) { - throw new Parse.Error( - Parse.Error.OPERATION_FORBIDDEN, - "read-only masterKey isn't allowed to delete a schema." - ); - } - - const schema = await config.database.loadSchema({ clearCache: true }); - const existingParseClass = await getClass(name, schema); - await config.database.deleteSchema(name); - return { + }, + mutateAndGetPayload: async (args, context) => { + try { + const { name } = args; + const { config, auth } = context; + + enforceMasterKeyAccess(auth); + + if (auth.isReadOnly) { + throw new Parse.Error( + Parse.Error.OPERATION_FORBIDDEN, + "read-only masterKey isn't allowed to delete a schema." + ); + } + + const schema = await config.database.loadSchema({ clearCache: true }); + const existingParseClass = await getClass(name, schema); + await config.database.deleteSchema(name); + return { + class: { name: existingParseClass.className, schemaFields: transformToGraphQL(existingParseClass.fields), - }; - } catch (e) { - parseGraphQLSchema.handleError(e); - } - }, + }, + }; + } catch (e) { + parseGraphQLSchema.handleError(e); + } }, + }); + + parseGraphQLSchema.addGraphQLType( + deleteClassMutation.args.input.type.ofType, + true, + true + ); + parseGraphQLSchema.addGraphQLType(deleteClassMutation.type, true, true); + parseGraphQLSchema.addGraphQLMutation( + 'deleteClass', + deleteClassMutation, true, true ); diff --git a/src/GraphQL/loaders/usersMutations.js b/src/GraphQL/loaders/usersMutations.js index a4fb69faca..0a8677b610 100644 --- a/src/GraphQL/loaders/usersMutations.js +++ b/src/GraphQL/loaders/usersMutations.js @@ -1,4 +1,5 @@ -import { GraphQLNonNull } from 'graphql'; +import { GraphQLNonNull, GraphQLString } from 'graphql'; +import { mutationWithClientMutationId } from 'graphql-relay'; import UsersRouter from '../../Routers/UsersRouter'; import * as objectsMutations from '../helpers/objectsMutations'; import { getUserFromSessionToken } from './usersQueries'; @@ -10,110 +11,166 @@ const load = parseGraphQLSchema => { return; } - parseGraphQLSchema.addGraphQLMutation( - 'signUp', - { - description: 'The signUp mutation can be used to sign the user up.', - args: { - fields: { - descriptions: 'These are the fields of the user.', - type: parseGraphQLSchema.parseClassTypes['_User'].signUpInputType, - }, + const signUpMutation = mutationWithClientMutationId({ + name: 'SignUp', + description: + 'The signUp mutation can be used to create and sign up a new user.', + inputFields: { + userFields: { + descriptions: + 'These are the fields of the new user to be created and signed up.', + type: + parseGraphQLSchema.parseClassTypes['_User'].classGraphQLCreateType, }, - type: new GraphQLNonNull(parseGraphQLSchema.viewerType), - async resolve(_source, args, context, mutationInfo) { - try { - const { fields } = args; + }, + outputFields: { + viewer: { + description: + 'This is the new user that was created, signed up and returned as a viewer.', + type: new GraphQLNonNull(parseGraphQLSchema.viewerType), + }, + }, + mutateAndGetPayload: async (args, context, mutationInfo) => { + try { + const { userFields } = args; + const { config, auth, info } = context; - const { config, auth, info } = context; + const { sessionToken } = await objectsMutations.createObject( + '_User', + userFields, + config, + auth, + info + ); - const { sessionToken } = await objectsMutations.createObject( - '_User', - fields, - config, - auth, - info - ); + info.sessionToken = sessionToken; - info.sessionToken = sessionToken; - - return await getUserFromSessionToken(config, info, mutationInfo); - } catch (e) { - parseGraphQLSchema.handleError(e); - } - }, + return { + viewer: await getUserFromSessionToken( + config, + info, + mutationInfo, + 'viewer.user.', + true + ), + }; + } catch (e) { + parseGraphQLSchema.handleError(e); + } }, + }); + + parseGraphQLSchema.addGraphQLType( + signUpMutation.args.input.type.ofType, true, true ); + parseGraphQLSchema.addGraphQLType(signUpMutation.type, true, true); + parseGraphQLSchema.addGraphQLMutation('signUp', signUpMutation, true, true); - parseGraphQLSchema.addGraphQLMutation( - 'logIn', - { - description: 'The logIn mutation can be used to log the user in.', - args: { - fields: { - description: 'This is data needed to login', - type: parseGraphQLSchema.parseClassTypes['_User'].logInInputType, - }, + const logInMutation = mutationWithClientMutationId({ + name: 'LogIn', + description: 'The logIn mutation can be used to log in an existing user.', + inputFields: { + username: { + description: 'This is the username used to log in the user.', + type: new GraphQLNonNull(GraphQLString), + }, + password: { + description: 'This is the password used to log in the user.', + type: new GraphQLNonNull(GraphQLString), }, - type: new GraphQLNonNull(parseGraphQLSchema.viewerType), - async resolve(_source, args, context) { - try { - const { - fields: { username, password }, - } = args; - const { config, auth, info } = context; - - return (await usersRouter.handleLogIn({ - body: { - username, - password, - }, - query: {}, + }, + outputFields: { + viewer: { + description: + 'This is the existing user that was logged in and returned as a viewer.', + type: new GraphQLNonNull(parseGraphQLSchema.viewerType), + }, + }, + mutateAndGetPayload: async (args, context, mutationInfo) => { + try { + const { username, password } = args; + const { config, auth, info } = context; + + const { sessionToken } = (await usersRouter.handleLogIn({ + body: { + username, + password, + }, + query: {}, + config, + auth, + info, + })).response; + + info.sessionToken = sessionToken; + + return { + viewer: await getUserFromSessionToken( config, - auth, info, - })).response; - } catch (e) { - parseGraphQLSchema.handleError(e); - } - }, + mutationInfo, + 'viewer.user.', + true + ), + }; + } catch (e) { + parseGraphQLSchema.handleError(e); + } }, + }); + + parseGraphQLSchema.addGraphQLType( + logInMutation.args.input.type.ofType, true, true ); + parseGraphQLSchema.addGraphQLType(logInMutation.type, true, true); + parseGraphQLSchema.addGraphQLMutation('logIn', logInMutation, true, true); - parseGraphQLSchema.addGraphQLMutation( - 'logOut', - { - description: 'The logOut mutation can be used to log the user out.', - type: new GraphQLNonNull(parseGraphQLSchema.viewerType), - async resolve(_source, _args, context, mutationInfo) { - try { - const { config, auth, info } = context; + const logOutMutation = mutationWithClientMutationId({ + name: 'LogOut', + description: 'The logOut mutation can be used to log out an existing user.', + outputFields: { + viewer: { + description: + 'This is the existing user that was logged out and returned as a viewer.', + type: new GraphQLNonNull(parseGraphQLSchema.viewerType), + }, + }, + mutateAndGetPayload: async (_args, context, mutationInfo) => { + try { + const { config, auth, info } = context; - const viewer = await getUserFromSessionToken( - config, - info, - mutationInfo - ); + const viewer = await getUserFromSessionToken( + config, + info, + mutationInfo, + 'viewer.user.', + true + ); - await usersRouter.handleLogOut({ - config, - auth, - info, - }); + await usersRouter.handleLogOut({ + config, + auth, + info, + }); - return viewer; - } catch (e) { - parseGraphQLSchema.handleError(e); - } - }, + return { viewer }; + } catch (e) { + parseGraphQLSchema.handleError(e); + } }, + }); + + parseGraphQLSchema.addGraphQLType( + logOutMutation.args.input.type.ofType, true, true ); + parseGraphQLSchema.addGraphQLType(logOutMutation.type, true, true); + parseGraphQLSchema.addGraphQLMutation('logOut', logOutMutation, true, true); }; export { load }; diff --git a/src/GraphQL/loaders/usersQueries.js b/src/GraphQL/loaders/usersQueries.js index 19c98e9453..7c00d19315 100644 --- a/src/GraphQL/loaders/usersQueries.js +++ b/src/GraphQL/loaders/usersQueries.js @@ -5,7 +5,13 @@ import rest from '../../rest'; import Auth from '../../Auth'; import { extractKeysAndInclude } from './parseClassTypes'; -const getUserFromSessionToken = async (config, info, queryInfo) => { +const getUserFromSessionToken = async ( + config, + info, + queryInfo, + keysPrefix, + validatedToken +) => { if (!info || !info.sessionToken) { throw new Parse.Error( Parse.Error.INVALID_SESSION_TOKEN, @@ -13,20 +19,42 @@ const getUserFromSessionToken = async (config, info, queryInfo) => { ); } const sessionToken = info.sessionToken; - const selectedFields = getFieldNames(queryInfo); + const selectedFields = getFieldNames(queryInfo) + .filter(field => field.startsWith(keysPrefix)) + .map(field => field.replace(keysPrefix, '')); + + const keysAndInclude = extractKeysAndInclude(selectedFields); + const { keys } = keysAndInclude; + let { include } = keysAndInclude; + + if (validatedToken && !keys && !include) { + return { + sessionToken, + }; + } else if (keys && !include) { + include = 'user'; + } + + const options = {}; + if (keys) { + options.keys = keys + .split(',') + .map(key => `user.${key}`) + .join(','); + } + if (include) { + options.include = include + .split(',') + .map(included => `user.${included}`) + .join(','); + } - const { include } = extractKeysAndInclude(selectedFields); const response = await rest.find( config, Auth.master(config), '_Session', { sessionToken }, - { - include: include - .split(',') - .map(included => `user.${included}`) - .join(','), - }, + options, info.clientVersion ); if ( @@ -40,8 +68,10 @@ const getUserFromSessionToken = async (config, info, queryInfo) => { ); } else { const user = response.results[0].user; - user.sessionToken = sessionToken; - return user; + return { + sessionToken, + user, + }; } }; @@ -59,7 +89,13 @@ const load = parseGraphQLSchema => { async resolve(_source, _args, context, queryInfo) { try { const { config, info } = context; - return await getUserFromSessionToken(config, info, queryInfo); + return await getUserFromSessionToken( + config, + info, + queryInfo, + 'user.', + false + ); } catch (e) { parseGraphQLSchema.handleError(e); } diff --git a/src/GraphQL/transformers/constraintType.js b/src/GraphQL/transformers/constraintType.js index 83c4e4b264..16f0b5d671 100644 --- a/src/GraphQL/transformers/constraintType.js +++ b/src/GraphQL/transformers/constraintType.js @@ -3,8 +3,13 @@ import * as defaultGraphQLTypes from '../loaders/defaultGraphQLTypes'; const transformConstraintTypeToGraphQL = ( parseType, targetClass, - parseClassTypes + parseClassTypes, + fieldName ) => { + if (fieldName === 'id' || fieldName === 'objectId') { + return defaultGraphQLTypes.ID_WHERE_INPUT; + } + switch (parseType) { case 'String': return defaultGraphQLTypes.STRING_WHERE_INPUT; diff --git a/src/GraphQL/transformers/mutation.js b/src/GraphQL/transformers/mutation.js index b177d36f57..7bf55eee10 100644 --- a/src/GraphQL/transformers/mutation.js +++ b/src/GraphQL/transformers/mutation.js @@ -1,3 +1,5 @@ +import Parse from 'parse/node'; +import { fromGlobalId } from 'graphql-relay'; import * as defaultGraphQLTypes from '../loaders/defaultGraphQLTypes'; import * as objectsMutations from '../helpers/objectsMutations'; @@ -108,8 +110,9 @@ const transformers = { { config, auth, info } ) => { if (Object.keys(value) === 0) - throw new Error( - `You need to provide atleast one operation on the relation mutation of field ${field}` + throw new Parse.Error( + Parse.Error.INVALID_POINTER, + `You need to provide at least one operation on the relation mutation of field ${field}` ); const op = { @@ -143,11 +146,17 @@ const transformers = { if (value.add || nestedObjectsToAdd.length > 0) { if (!value.add) value.add = []; - value.add = value.add.map(input => ({ - __type: 'Pointer', - className: targetClass, - objectId: input, - })); + value.add = value.add.map(input => { + const globalIdObject = fromGlobalId(input); + if (globalIdObject.type === targetClass) { + input = globalIdObject.id; + } + return { + __type: 'Pointer', + className: targetClass, + objectId: input, + }; + }); op.ops.push({ __op: 'AddRelation', objects: [...value.add, ...nestedObjectsToAdd], @@ -157,11 +166,17 @@ const transformers = { if (value.remove) { op.ops.push({ __op: 'RemoveRelation', - objects: value.remove.map(input => ({ - __type: 'Pointer', - className: targetClass, - objectId: input, - })), + objects: value.remove.map(input => { + const globalIdObject = fromGlobalId(input); + if (globalIdObject.type === targetClass) { + input = globalIdObject.id; + } + return { + __type: 'Pointer', + className: targetClass, + objectId: input, + }; + }), }); } return op; @@ -174,7 +189,8 @@ const transformers = { { config, auth, info } ) => { if (Object.keys(value) > 1 || Object.keys(value) === 0) - throw new Error( + throw new Parse.Error( + Parse.Error.INVALID_POINTER, `You need to provide link OR createLink on the pointer mutation of field ${field}` ); @@ -199,10 +215,15 @@ const transformers = { }; } if (value.link) { + let objectId = value.link; + const globalIdObject = fromGlobalId(objectId); + if (globalIdObject.type === targetClass) { + objectId = globalIdObject.id; + } return { __type: 'Pointer', className: targetClass, - objectId: value.link, + objectId, }; } }, diff --git a/src/GraphQL/transformers/outputType.js b/src/GraphQL/transformers/outputType.js index 85b28de3f6..e56f233832 100644 --- a/src/GraphQL/transformers/outputType.js +++ b/src/GraphQL/transformers/outputType.js @@ -45,7 +45,7 @@ const transformOutputTypeToGraphQL = ( parseClassTypes[targetClass].classGraphQLFindResultType ); } else { - return new GraphQLNonNull(defaultGraphQLTypes.FIND_RESULT); + return new GraphQLNonNull(defaultGraphQLTypes.OBJECT); } case 'File': return defaultGraphQLTypes.FILE_INFO; diff --git a/src/GraphQL/transformers/query.js b/src/GraphQL/transformers/query.js index 3766b4db1b..a417ef4c5d 100644 --- a/src/GraphQL/transformers/query.js +++ b/src/GraphQL/transformers/query.js @@ -1,3 +1,5 @@ +import { fromGlobalId } from 'graphql-relay'; + const parseQueryMap = { id: 'objectId', OR: '$or', @@ -102,10 +104,15 @@ const transformQueryConstraintInputToParse = ( typeof fieldValue === 'string' ) { const { targetClass } = fields[parentFieldName]; + let objectId = fieldValue; + const globalIdObject = fromGlobalId(objectId); + if (globalIdObject.type === targetClass) { + objectId = globalIdObject.id; + } constraints[fieldName] = { __type: 'Pointer', className: targetClass, - objectId: fieldValue, + objectId, }; } } @@ -176,7 +183,7 @@ const transformQueryConstraintInputToParse = ( }); }; -const transformQueryInputToParse = (constraints, fields) => { +const transformQueryInputToParse = (constraints, fields, className) => { if (!constraints || typeof constraints !== 'object') { return; } @@ -191,9 +198,30 @@ const transformQueryInputToParse = (constraints, fields) => { if (fieldName !== 'objectId') { fieldValue.forEach(fieldValueItem => { - transformQueryInputToParse(fieldValueItem, fields); + transformQueryInputToParse(fieldValueItem, fields, className); }); return; + } else if (className) { + Object.keys(fieldValue).forEach(constraintName => { + const constraintValue = fieldValue[constraintName]; + if (typeof constraintValue === 'string') { + const globalIdObject = fromGlobalId(constraintValue); + + if (globalIdObject.type === className) { + fieldValue[constraintName] = globalIdObject.id; + } + } else if (Array.isArray(constraintValue)) { + fieldValue[constraintName] = constraintValue.map(value => { + const globalIdObject = fromGlobalId(value); + + if (globalIdObject.type === className) { + return globalIdObject.id; + } + + return value; + }); + } + }); } }