From 129a4084c2c77aee8addf73c3b9c94c5ca193fcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20Babi=C4=8D?= Date: Sat, 14 Dec 2019 12:30:06 +0100 Subject: [PATCH] implement FigureResolver --- .gitignore | 1 + package-lock.json | 460 +++++++----------- package.json | 5 + src/integration/figure_upload.spec.ts | 65 +++ .../refresh_token.spec.ts} | 22 +- src/server.ts | 4 +- src/server/FigureResolver.spec.ts | 109 +++++ src/server/FigureResolver.ts | 29 ++ src/server/FigureResolver/Upload.ts | 8 + src/server/UserResolver.spec.ts | 21 +- src/server/UserResolver.ts | 5 +- src/server/connection.ts | 2 +- src/server/schema.ts | 35 +- src/server/testing.ts | 80 ++- 14 files changed, 491 insertions(+), 355 deletions(-) create mode 100644 src/integration/figure_upload.spec.ts rename src/{server.spec.ts => integration/refresh_token.spec.ts} (89%) create mode 100644 src/server/FigureResolver.spec.ts create mode 100644 src/server/FigureResolver.ts create mode 100644 src/server/FigureResolver/Upload.ts diff --git a/.gitignore b/.gitignore index 007ca3c..2a6fe52 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .vscode/ +uploads/ # Created by https://www.gitignore.io/api/node # Edit at https://www.gitignore.io/?templates=node diff --git a/package-lock.json b/package-lock.json index ddf59f1..e1b98a6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -533,9 +533,9 @@ "dev": true }, "@types/cookies": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/@types/cookies/-/cookies-0.7.2.tgz", - "integrity": "sha512-jnihWgshWystcJKrz8C9hV+Ot9lqOUyAh2RF+o3BEo6K6AS2l4zYCb9GYaBuZ3C6Il59uIGqpE3HvCun4KKeJA==", + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@types/cookies/-/cookies-0.7.4.tgz", + "integrity": "sha512-oTGtMzZZAVuEjTwCjIh8T8FrC8n/uwy+PG0yTvQcdZ7etoel7C7/3MSd7qrukENTgQtotG7gvBlBojuVs7X5rw==", "requires": { "@types/connect": "*", "@types/express": "*", @@ -583,6 +583,15 @@ "@types/node": "*" } }, + "@types/fs-extra": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.0.1.tgz", + "integrity": "sha512-J00cVDALmi/hJOYsunyT52Hva5TnJeKP5yd1r+mH/ZU0mbYZflR0Z5kw5kITtKTRYMhm1JMClOFYdHnQszEvqw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/glob": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz", @@ -673,9 +682,9 @@ "integrity": "sha1-/1QEYtL7TQqIRBzq8n0oewHD2Hg=" }, "@types/koa": { - "version": "2.0.49", - "resolved": "https://registry.npmjs.org/@types/koa/-/koa-2.0.49.tgz", - "integrity": "sha512-WQWpCH8O4Dslk8IcXfazff40aM1jXX7BQRbADIj/fKozVPu76P/wQE4sRe2SCWMn8yNkOcare2MkDrnZqLMkPQ==", + "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": "*", @@ -686,9 +695,9 @@ } }, "@types/koa-compose": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@types/koa-compose/-/koa-compose-3.2.4.tgz", - "integrity": "sha512-ioou0rxkuWL+yBQYsHUQAzRTfVxAg8Y2VfMftU+Y3RA03/MzuFL0x/M2sXXj3PkfnENbHsjeHR1aMdezLYpTeA==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/@types/koa-compose/-/koa-compose-3.2.5.tgz", + "integrity": "sha512-B8nG/OoE1ORZqCkBVsup/AKcvjdgoHnfi4pZMn5UwAPCbhk/96xyv284eBYW8JlQbQ7zDmnpFr68I/40mFoIBQ==", "requires": { "@types/koa": "*" } @@ -727,6 +736,15 @@ "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz", "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==" }, + "@types/readable-stream": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-2.3.5.tgz", + "integrity": "sha512-Mq2eLkGYamlcolW603FY2ROBvcl90jPF+3jLkjpBV6qS+2aVeJqlgRG0TVAa1oWbmPdb5yOWlOPVvQle76nUNw==", + "requires": { + "@types/node": "*", + "safe-buffer": "*" + } + }, "@types/semver": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-6.0.2.tgz", @@ -964,6 +982,52 @@ "lru-cache": "^5.0.0" } }, + "apollo-server-core": { + "version": "2.9.4", + "resolved": "https://registry.npmjs.org/apollo-server-core/-/apollo-server-core-2.9.4.tgz", + "integrity": "sha512-6mzipnn9woJxgo/JQFWTlY13svS7HCr0ZsN035eRmKOsXzROfB9ugXcTuc6MP94ICM7TlB/DtJOP+bLX53mijw==", + "requires": { + "@apollographql/apollo-tools": "^0.4.0", + "@apollographql/graphql-playground-html": "1.6.24", + "@types/graphql-upload": "^8.0.0", + "@types/ws": "^6.0.0", + "apollo-cache-control": "^0.8.4", + "apollo-datasource": "^0.6.3", + "apollo-engine-reporting": "^1.4.6", + "apollo-server-caching": "^0.5.0", + "apollo-server-env": "^2.4.3", + "apollo-server-errors": "^2.3.3", + "apollo-server-plugin-base": "^0.6.4", + "apollo-server-types": "^0.2.4", + "apollo-tracing": "^0.8.4", + "fast-json-stable-stringify": "^2.0.0", + "graphql-extensions": "^0.10.3", + "graphql-tag": "^2.9.2", + "graphql-tools": "^4.0.0", + "graphql-upload": "^8.0.2", + "sha.js": "^2.4.11", + "subscriptions-transport-ws": "^0.9.11", + "ws": "^6.0.0" + }, + "dependencies": { + "fs-capacitor": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/fs-capacitor/-/fs-capacitor-2.0.4.tgz", + "integrity": "sha512-8S4f4WsCryNw2mJJchi46YgB6CR5Ze+4L1h8ewl9tEpL4SJ3ZO+c/bS4BWhB8bK+O3TMqhuZarTitd0S0eh2pA==" + }, + "graphql-upload": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/graphql-upload/-/graphql-upload-8.1.0.tgz", + "integrity": "sha512-U2OiDI5VxYmzRKw0Z2dmfk0zkqMRaecH9Smh1U277gVgVe9Qn+18xqf4skwr4YJszGIh7iQDZ57+5ygOK9sM/Q==", + "requires": { + "busboy": "^0.3.1", + "fs-capacitor": "^2.0.4", + "http-errors": "^1.7.3", + "object-path": "^0.11.4" + } + } + } + }, "apollo-server-env": { "version": "2.4.3", "resolved": "https://registry.npmjs.org/apollo-server-env/-/apollo-server-env-2.4.3.tgz", @@ -1001,33 +1065,10 @@ "type-is": "^1.6.16" }, "dependencies": { - "apollo-server-core": { - "version": "2.9.4", - "resolved": "https://registry.npmjs.org/apollo-server-core/-/apollo-server-core-2.9.4.tgz", - "integrity": "sha512-6mzipnn9woJxgo/JQFWTlY13svS7HCr0ZsN035eRmKOsXzROfB9ugXcTuc6MP94ICM7TlB/DtJOP+bLX53mijw==", - "requires": { - "@apollographql/apollo-tools": "^0.4.0", - "@apollographql/graphql-playground-html": "1.6.24", - "@types/graphql-upload": "^8.0.0", - "@types/ws": "^6.0.0", - "apollo-cache-control": "^0.8.4", - "apollo-datasource": "^0.6.3", - "apollo-engine-reporting": "^1.4.6", - "apollo-server-caching": "^0.5.0", - "apollo-server-env": "^2.4.3", - "apollo-server-errors": "^2.3.3", - "apollo-server-plugin-base": "^0.6.4", - "apollo-server-types": "^0.2.4", - "apollo-tracing": "^0.8.4", - "fast-json-stable-stringify": "^2.0.0", - "graphql-extensions": "^0.10.3", - "graphql-tag": "^2.9.2", - "graphql-tools": "^4.0.0", - "graphql-upload": "^8.0.2", - "sha.js": "^2.4.11", - "subscriptions-transport-ws": "^0.9.11", - "ws": "^6.0.0" - } + "fs-capacitor": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/fs-capacitor/-/fs-capacitor-2.0.4.tgz", + "integrity": "sha512-8S4f4WsCryNw2mJJchi46YgB6CR5Ze+4L1h8ewl9tEpL4SJ3ZO+c/bS4BWhB8bK+O3TMqhuZarTitd0S0eh2pA==" } } }, @@ -1511,8 +1552,7 @@ "camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" }, "camelcase-keys": { "version": "2.1.0", @@ -1606,90 +1646,12 @@ "mz": "^2.4.0", "parse5": "^4.0.0", "yargs": "^13.0.0" - }, - "dependencies": { - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" - }, - "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - } - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "requires": { - "locate-path": "^3.0.0" - } - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - } - }, - "yargs": { - "version": "13.2.4", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.4.tgz", - "integrity": "sha512-HG/DWAJa1PAnHT9JAhNa8AbAv3FPaiLzioSjCcmuXXhP8MlpHO5vwls4g4j6n30Z74GVQj8Xa62dWVx1QCGklg==", - "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "os-locale": "^3.1.0", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.0" - } - }, - "yargs-parser": { - "version": "13.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", - "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - } } }, "cliui": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "dev": true, "requires": { "string-width": "^3.1.0", "strip-ansi": "^5.2.0", @@ -1858,19 +1820,13 @@ "version": "6.0.5", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, "requires": { "nice-try": "^1.0.4", "path-key": "^2.0.1", "semver": "^5.5.0", "shebang-command": "^1.2.0", "which": "^1.2.9" - }, - "dependencies": { - "semver": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==" - } } }, "cssom": { @@ -2136,6 +2092,7 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "dev": true, "requires": { "once": "^1.4.0" } @@ -2237,6 +2194,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, "requires": { "cross-spawn": "^6.0.0", "get-stream": "^4.0.0", @@ -2525,7 +2483,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, "requires": { "locate-path": "^3.0.0" } @@ -2543,13 +2500,13 @@ "dev": true }, "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.0.tgz", + "integrity": "sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg==", "dev": true, "requires": { "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", + "combined-stream": "^1.0.8", "mime-types": "^2.1.12" } }, @@ -2573,9 +2530,24 @@ "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" }, "fs-capacitor": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/fs-capacitor/-/fs-capacitor-2.0.4.tgz", - "integrity": "sha512-8S4f4WsCryNw2mJJchi46YgB6CR5Ze+4L1h8ewl9tEpL4SJ3ZO+c/bS4BWhB8bK+O3TMqhuZarTitd0S0eh2pA==" + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/fs-capacitor/-/fs-capacitor-4.0.1.tgz", + "integrity": "sha512-e0qFoKQMFe52F54dMvZLD+I1M/Gs6xB2gnZVQB5FYT/8ioP6qTb3U/tzp55O0IuPOMvSM8j4ta0bVafIFjJzxQ==", + "requires": { + "@types/readable-stream": "^2.3.5", + "readable-stream": "^3.4.0" + } + }, + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } }, "fs.realpath": { "version": "1.0.0", @@ -3138,8 +3110,7 @@ "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" }, "get-stdin": { "version": "4.0.1", @@ -3151,6 +3122,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, "requires": { "pump": "^3.0.0" } @@ -3261,13 +3233,13 @@ } }, "graphql-upload": { - "version": "8.0.7", - "resolved": "https://registry.npmjs.org/graphql-upload/-/graphql-upload-8.0.7.tgz", - "integrity": "sha512-gi2yygbDPXbHPC7H0PNPqP++VKSoNoJO4UrXWq4T0Bi4IhyUd3Ycop/FSxhx2svWIK3jdXR/i0vi91yR1aAF0g==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/graphql-upload/-/graphql-upload-9.0.0.tgz", + "integrity": "sha512-YR2o9GoDa5On3q3lYLkLo3gHfa8crCHvMY1QbT7Zqja6BUqiihqaGjbWbvSPko/gbDSmZE+zLcX46Ef+/SmRyA==", "requires": { "busboy": "^0.3.1", - "fs-capacitor": "^2.0.4", - "http-errors": "^1.7.2", + "fs-capacitor": "^4.0.1", + "http-errors": "^1.7.3", "object-path": "^0.11.4" } }, @@ -3481,11 +3453,6 @@ "loose-envify": "^1.0.0" } }, - "invert-kv": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", - "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==" - }, "ipaddr.js": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", @@ -3647,7 +3614,8 @@ "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true }, "is-symbol": { "version": "1.0.2", @@ -3690,7 +3658,8 @@ "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true }, "isobject": { "version": "3.0.1", @@ -4337,6 +4306,15 @@ "minimist": "^1.2.0" } }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, "jsonwebtoken": { "version": "8.5.1", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", @@ -4404,14 +4382,6 @@ "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", "dev": true }, - "lcid": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", - "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", - "requires": { - "invert-kv": "^2.0.0" - } - }, "left-pad": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz", @@ -4584,14 +4554,6 @@ "tmpl": "1.0.x" } }, - "map-age-cleaner": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", - "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", - "requires": { - "p-defer": "^1.0.0" - } - }, "map-cache": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", @@ -4618,16 +4580,6 @@ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" }, - "mem": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", - "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", - "requires": { - "map-age-cleaner": "^0.1.1", - "mimic-fn": "^2.0.0", - "p-is-promise": "^2.0.0" - } - }, "meow": { "version": "3.7.0", "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", @@ -4782,11 +4734,6 @@ "mime-db": "1.40.0" } }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" - }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -4898,7 +4845,8 @@ "nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true }, "node-addon-api": { "version": "1.7.1", @@ -4965,6 +4913,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, "requires": { "path-key": "^2.0.0" } @@ -5121,21 +5070,6 @@ } } }, - "os-locale": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", - "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", - "requires": { - "execa": "^1.0.0", - "lcid": "^2.0.0", - "mem": "^4.0.0" - } - }, - "p-defer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", - "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=" - }, "p-each-series": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-1.0.0.tgz", @@ -5148,12 +5082,8 @@ "p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" - }, - "p-is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", - "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==" + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true }, "p-limit": { "version": "2.2.0", @@ -5235,7 +5165,8 @@ "path-key": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true }, "path-parse": { "version": "1.0.6", @@ -5467,6 +5398,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, "requires": { "end-of-stream": "^1.1.0", "once": "^1.3.1" @@ -5540,6 +5472,16 @@ "read-pkg": "^3.0.0" } }, + "readable-stream": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", + "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, "realpath-native": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-1.1.0.tgz", @@ -5629,6 +5571,17 @@ "uuid": "^3.3.2" }, "dependencies": { + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, "punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", @@ -5869,6 +5822,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, "requires": { "shebang-regex": "^1.0.0" } @@ -5876,7 +5830,8 @@ "shebang-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true }, "shellwords": { "version": "0.1.1", @@ -5893,7 +5848,8 @@ "signal-exit": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true }, "sisteransi": { "version": "1.0.3", @@ -6206,7 +6162,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, "requires": { "emoji-regex": "^7.0.1", "is-fullwidth-code-point": "^2.0.0", @@ -6231,6 +6186,14 @@ "function-bind": "^1.1.1" } }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + } + }, "strip-ansi": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", @@ -6248,7 +6211,8 @@ "strip-eof": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true }, "strip-indent": { "version": "1.0.1", @@ -6616,21 +6580,6 @@ "yargs": "^13.2.1" }, "dependencies": { - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" - }, - "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - } - }, "debug": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", @@ -6644,70 +6593,10 @@ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-6.2.0.tgz", "integrity": "sha512-HygQCKUBSFl8wKQZBSemMywRWcEDNidvNbjGVyZu3nbZ8qq9ubiPoGLMdRDpfSrpkkm9BXYFkpKxxFX38o/76w==" }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "requires": { - "locate-path": "^3.0.0" - } - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" - }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - } - }, - "yargs": { - "version": "13.2.4", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.4.tgz", - "integrity": "sha512-HG/DWAJa1PAnHT9JAhNa8AbAv3FPaiLzioSjCcmuXXhP8MlpHO5vwls4g4j6n30Z74GVQj8Xa62dWVx1QCGklg==", - "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "os-locale": "^3.1.0", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.0" - } - }, - "yargs-parser": { - "version": "13.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", - "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } } } }, @@ -6749,6 +6638,12 @@ "set-value": "^2.0.1" } }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true + }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -6815,6 +6710,11 @@ "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", "dev": true }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, "util.promisify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", @@ -6925,6 +6825,7 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, "requires": { "isexe": "^2.0.0" } @@ -6944,7 +6845,6 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "dev": true, "requires": { "ansi-styles": "^3.2.0", "string-width": "^3.0.0", @@ -7061,7 +6961,6 @@ "version": "13.3.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz", "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==", - "dev": true, "requires": { "cliui": "^5.0.0", "find-up": "^3.0.0", @@ -7079,7 +6978,6 @@ "version": "13.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", - "dev": true, "requires": { "camelcase": "^5.0.0", "decamelize": "^1.2.0" diff --git a/package.json b/package.json index 8d76b1a..cb921e8 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "dotenv": "^8.1.0", "express": "^4.17.1", "graphql": "^14.5.4", + "graphql-upload": "^9.0.0", "jsonwebtoken": "^8.5.1", "pg": "^7.12.1", "reflect-metadata": "^0.1.13", @@ -28,11 +29,15 @@ "devDependencies": { "@types/cookie": "^0.3.3", "@types/express": "^4.17.1", + "@types/fs-extra": "^8.0.1", "@types/graphql": "^14.5.0", + "@types/graphql-upload": "^8.0.3", "@types/jest": "^24.0.18", "@types/jsonwebtoken": "^8.3.3", "@types/node": "^12.7.5", "@types/node-fetch": "^2.5.2", + "form-data": "^3.0.0", + "fs-extra": "^8.1.0", "graphql-request": "^1.8.2", "jest": "^24.9.0", "node-fetch": "^2.6.0", diff --git a/src/integration/figure_upload.spec.ts b/src/integration/figure_upload.spec.ts new file mode 100644 index 0000000..ff3f368 --- /dev/null +++ b/src/integration/figure_upload.spec.ts @@ -0,0 +1,65 @@ +import fetch from "node-fetch" +import { gqlUri, uploadDir } from "../server/testing" +import FormData = require("form-data") +import fs = require("fs-extra") + +describe("figure upload integration should", () => { + it("upload a file", async () => { + const filename = "ok.png" + const path = uploadDir + filename + + const body = bodyFrom(filename) + const reponse = await fetchWith(body) + const uploadedFile = await fs.access(path, fs.constants.F_OK) + + expect(uploadedFile).toBeUndefined() + expect(reponse.errors).toBeUndefined() + expect(reponse.data).toMatchObject({ uploadFigure: true }) + }) + + it("revoke a non-picture file", async () => { + const filename = "file.txt" + const path = uploadDir + filename + + const body = bodyFrom(filename) + const response = await fetchWith(body) + + await expect(fs.access(path, fs.constants.F_OK)).rejects.toThrow() + expect(response.errors).toBeDefined() + expect(response.data).toBeNull() + }) +}) + +const fetchWith = async (body: FormData): Promise => { + const reponse = await fetch(gqlUri, { + method: "POST", + body, + }) + return reponse.json() +} + +const bodyFrom = (filename: string) => { + const body = new FormData() + body.append( + "operations", + JSON.stringify({ + query: /* GraphQL */ ` + mutation($file: Upload!) { + uploadFigure(file: $file) + } + `, + variables: { + file: null, + }, + }) + ) + body.append("map", JSON.stringify({ 0: ["variables.file"] })) + body.append("0", "", { filename }) + + return body +} + +interface FetchResponse { + data: any + errors?: any +} diff --git a/src/server.spec.ts b/src/integration/refresh_token.spec.ts similarity index 89% rename from src/server.spec.ts rename to src/integration/refresh_token.spec.ts index a8782be..089cf36 100644 --- a/src/server.spec.ts +++ b/src/integration/refresh_token.spec.ts @@ -1,18 +1,22 @@ -// TODO: convert to import import cookie = require("cookie") import { gql } from "apollo-server-express" import { GraphQLClient, rawRequest } from "graphql-request" import fetch from "node-fetch" import { createConnection } from "typeorm" -import { createServer } from "./server" -import { gqlToStr } from "./server/schema" -import { testingConnectionOptions } from "./server/testing" +import { createServer } from "../server" +import { gqlToStr } from "../server/schema" +import { + gqlUri, + refreshTokenUri, + testingConnectionOptions, + testingPort, +} from "../server/testing" import { rtCookieOptions, signRefreshToken, verifiedRefreshTokenPayload, -} from "./server/userResolver/auth" -import { User } from "./server/userResolver/User" +} from "../server/UserResolver/auth" +import { User } from "../server/UserResolver/User" let user: User @@ -107,7 +111,7 @@ describe("server should", () => { beforeAll(async () => { await createConnection(testingConnectionOptions()) - await createServer(port) + await createServer(testingPort) await User.delete({ email: "auth@server.com" }) user = await User.create({ email: "auth@server.com", password: "password" }).save() @@ -117,10 +121,6 @@ afterAll(async () => { await User.delete({ email: "auth@server.com" }) }) -const port = 4001 -const gqlUri = `http://localhost:${port}/graphql` -const refreshTokenUri = `http://localhost:${port}/refresh_token` - const accessTokenMutation = gql` mutation { accessToken(email: "auth@server.com", password: "password") diff --git a/src/server.ts b/src/server.ts index 4eb6547..1075f31 100644 --- a/src/server.ts +++ b/src/server.ts @@ -5,8 +5,8 @@ import { accessTokenWithRefreshCookie, contextFunction, verifiedRefreshTokenPayload, -} from "./server/userResolver/auth" -import { User } from "./server/userResolver/User" +} from "./server/UserResolver/auth" +import { User } from "./server/UserResolver/User" import cookie = require("cookie") import cors = require("cors") diff --git a/src/server/FigureResolver.spec.ts b/src/server/FigureResolver.spec.ts new file mode 100644 index 0000000..2bc447a --- /dev/null +++ b/src/server/FigureResolver.spec.ts @@ -0,0 +1,109 @@ +import { gql } from "apollo-server-express" +import { Readable, Stream } from "stream" +import { createConnection, getConnection } from "typeorm" +import { Upload } from "./FigureResolver/Upload" +import { callSchema } from "./schema" +import { + context, + initializeRollbackTransactions, + runInRollbackTransaction, + testingConnectionOptions, + uploadDir, +} from "./testing" +import fs = require("fs-extra") + +describe("resolver of figures should", () => { + it( + "return an error when wrong file extension is provided", + runInRollbackTransaction(async () => { + const filename = "wrong_extensions.txt" + const file: Upload = { + filename, + mimetype: "image/text", + encoding: "7bit", + createReadStream: () => new Stream(), + } + const response = await callSchema(uploadFileMutation, context(), { + file, + }) + + expect(response.errors).toBeDefined() + expect(response.data).toBeNull() + await expect(fs.stat(uploadDir + filename)).rejects.toThrow() + }) + ) + + it( + "return an error when error occurs during streaming", + runInRollbackTransaction(async () => { + const filename = "stream_error.png" + const file: Upload = { + filename, + mimetype: "image/png", + encoding: "7bit", + createReadStream: () => { + const stream = new Readable({ + objectMode: true, + read() {}, + }) + stream.push("file contents") + stream.destroy(new Error("readable stream error")) + return stream + }, + } + const response = await callSchema(uploadFileMutation, context(), { + file, + }) + + expect(response.errors).toBeDefined() + expect(response.data).toBeNull() + await expect(fs.stat(uploadDir + filename)).resolves.toBeTruthy() + }) + ) + + it( + "return truthy when ok", + runInRollbackTransaction(async () => { + const filename = "truthy.png" + const file: Upload = { + filename, + mimetype: "image/png", + encoding: "7bit", + createReadStream: () => { + const stream = new Readable({ + objectMode: true, + autoDestroy: true, + read() {}, + }) + + stream.push(null) + return stream + }, + } + const response = await callSchema(uploadFileMutation, context(), { + file, + }) + + expect(response.errors).toBeUndefined() + expect(response.data).toMatchObject({ uploadFigure: true }) + await expect(fs.stat(uploadDir + filename)).resolves.toBeTruthy() + }) + ) +}) + +beforeAll(async () => { + initializeRollbackTransactions() + await createConnection(testingConnectionOptions()) + await fs.emptyDir(uploadDir) +}) + +afterAll(async () => { + await getConnection().close() + // await fs.emptyDir(uploadDir) +}) + +const uploadFileMutation = gql` + mutation($file: Upload!) { + uploadFigure(file: $file) + } +` diff --git a/src/server/FigureResolver.ts b/src/server/FigureResolver.ts new file mode 100644 index 0000000..8e4bac0 --- /dev/null +++ b/src/server/FigureResolver.ts @@ -0,0 +1,29 @@ +import { createWriteStream } from "fs-extra" +import { GraphQLUpload } from "graphql-upload" +import { pipeline } from "stream" +import { Arg, Mutation } from "type-graphql" +import { Upload } from "./FigureResolver/Upload" +import path = require("path") + +export class FigureResolver { + @Mutation(() => Boolean) + async uploadFigure( + @Arg("file", () => GraphQLUpload) + { createReadStream, filename }: Upload + ) { + const allowedExt = [".jpg", ".png"] + const ext = path.extname(filename) + + if (!allowedExt.includes(ext)) { + throw new Error("wrong extension") + } + + return new Promise((resolve, reject) => + pipeline( + createReadStream() as any, + createWriteStream(__dirname + "/../../uploads/" + filename), + error => (error ? reject(error) : resolve(true)) + ) + ) + } +} diff --git a/src/server/FigureResolver/Upload.ts b/src/server/FigureResolver/Upload.ts new file mode 100644 index 0000000..826a103 --- /dev/null +++ b/src/server/FigureResolver/Upload.ts @@ -0,0 +1,8 @@ +import { Stream } from "stream" + +export interface Upload { + filename: string + mimetype: string + encoding: string + createReadStream: () => Stream +} diff --git a/src/server/UserResolver.spec.ts b/src/server/UserResolver.spec.ts index 54d0e1a..55c1688 100644 --- a/src/server/UserResolver.spec.ts +++ b/src/server/UserResolver.spec.ts @@ -1,14 +1,15 @@ import { gql } from "apollo-server-express" -import { Request, Response } from "express" import { createConnection, getConnection } from "typeorm" import { callSchema } from "./schema" import { + contextWithAuthHeader, + contextWithCookie, initializeRollbackTransactions, runInRollbackTransaction, testingConnectionOptions, } from "./testing" -import { Context, signAccessToken, verifiedAccessTokenPayload } from "./userResolver/auth" -import { User } from "./userResolver/User" +import { signAccessToken, verifiedAccessTokenPayload } from "./UserResolver/auth" +import { User } from "./UserResolver/User" describe("resolver of user", () => { describe("createUser mutation should", () => { @@ -161,17 +162,3 @@ const signOutMutation = gql` signOut } ` - -const contextWithAuthHeader = (header: string): Context => ({ - req: { - headers: { - authorization: header, - }, - } as Request, - res: {} as Response, -}) - -const contextWithCookie = (): Context => ({ - req: {} as Request, - res: ({ cookie: () => undefined } as unknown) as Response, -}) diff --git a/src/server/UserResolver.ts b/src/server/UserResolver.ts index 8ca43ab..06b9b12 100644 --- a/src/server/UserResolver.ts +++ b/src/server/UserResolver.ts @@ -5,10 +5,11 @@ import { comparePasswords, Context, createRtCookie, -} from "./userResolver/auth" -import { User } from "./userResolver/User" +} from "./UserResolver/auth" +import { User } from "./UserResolver/User" export class UserResolver { + // TODO: remove when other query gets itroduced @Query(() => String) async query() { return "" diff --git a/src/server/connection.ts b/src/server/connection.ts index 201931f..10c6221 100644 --- a/src/server/connection.ts +++ b/src/server/connection.ts @@ -1,5 +1,5 @@ import { ConnectionOptions } from "typeorm" -import { User } from "./userResolver/User" +import { User } from "./UserResolver/User" export const connectionOptions = (): ConnectionOptions => ({ type: "postgres", diff --git a/src/server/schema.ts b/src/server/schema.ts index 04c748e..86d7b4a 100644 --- a/src/server/schema.ts +++ b/src/server/schema.ts @@ -1,27 +1,34 @@ require("dotenv").config() import { DocumentNode, graphql, GraphQLSchema } from "graphql" import { buildSchema } from "type-graphql" +import { FigureResolver } from "./FigureResolver" import { UserResolver } from "./UserResolver" -import { Context, customAuthChecker } from "./userResolver/auth" +import { Context, customAuthChecker } from "./UserResolver/auth" let schema: GraphQLSchema -export const callSchema = async (document: DocumentNode, context?: Context) => { - if (!schema) { - schema = await createSchema() - } +export const callSchema = async ( + document: DocumentNode, + context?: Context, + variables?: any +) => { + if (!schema) { + schema = await createSchema() + } - return graphql({ - schema, - source: gqlToStr(document), - contextValue: context, - }) + return graphql({ + schema, + source: gqlToStr(document), + contextValue: context, + variableValues: variables, + }) } export const createSchema = () => - buildSchema({ - resolvers: [UserResolver], - authChecker: customAuthChecker, - }) + buildSchema({ + resolvers: [UserResolver, FigureResolver], + authChecker: customAuthChecker, + validate: false, + }) export const gqlToStr = (document: DocumentNode) => document.loc!.source.body as string diff --git a/src/server/testing.ts b/src/server/testing.ts index ba05d99..50324f6 100644 --- a/src/server/testing.ts +++ b/src/server/testing.ts @@ -1,50 +1,76 @@ +import { Request, Response } from "express" import { ConnectionOptions } from "typeorm" import { - initializeTransactionalContext, - patchTypeORMRepositoryWithBaseRepository, - Propagation, - Transactional, + initializeTransactionalContext, + patchTypeORMRepositoryWithBaseRepository, + Propagation, + Transactional, } from "typeorm-transactional-cls-hooked" import { connectionOptions } from "./connection" +import { Context } from "./UserResolver/auth" + +export const testingPort = 4001 +export const gqlUri = `http://localhost:${testingPort}/graphql` +export const refreshTokenUri = `http://localhost:${testingPort}/refresh_token` +export const uploadDir = __dirname + "/../../uploads/" export const testingConnectionOptions = () => { - const database = process.env.DB_NAME_TESING as string + const database = process.env.DB_NAME_TESING as string - return { ...connectionOptions(), database } as ConnectionOptions + return { ...connectionOptions(), database } as ConnectionOptions } +export const context = (): Context => ({ + req: {} as Request, + res: {} as Response, +}) + +export const contextWithAuthHeader = (header: string): Context => ({ + req: { + headers: { + authorization: header, + }, + } as Request, + res: {} as Response, +}) + +export const contextWithCookie = (): Context => ({ + req: {} as Request, + res: ({ cookie: () => undefined } as unknown) as Response, +}) + export const initializeRollbackTransactions = () => { - initializeTransactionalContext() - patchTypeORMRepositoryWithBaseRepository() + initializeTransactionalContext() + patchTypeORMRepositoryWithBaseRepository() } type RunFunction = () => Promise | void class RollbackError extends Error { - constructor(message: string) { - super(message) + constructor(message: string) { + super(message) - this.name = this.constructor.name - } + this.name = this.constructor.name + } } class TransactionCreator { - @Transactional({ propagation: Propagation.REQUIRED }) - static async run(func: RunFunction) { - await func() - throw new RollbackError(`This is thrown to cause a rollback on the transaction.`) - } + @Transactional({ propagation: Propagation.REQUIRED }) + static async run(func: RunFunction) { + await func() + throw new RollbackError(`This is thrown to cause a rollback on the transaction.`) + } } export function runInRollbackTransaction(func: RunFunction) { - return async () => { - try { - await TransactionCreator.run(func) - } catch (e) { - /* istanbul ignore next */ - if (!(e instanceof RollbackError)) { - throw e - } - } - } + return async () => { + try { + await TransactionCreator.run(func) + } catch (e) { + /* istanbul ignore next */ + if (!(e instanceof RollbackError)) { + throw e + } + } + } }