From dbddb536395499b95826573a25324f487045c57a Mon Sep 17 00:00:00 2001 From: Rahul Rajan Date: Sun, 20 Dec 2020 05:45:04 -0500 Subject: [PATCH 1/3] major refactor --- client/src/components/EditProfile.js | 2 +- client/src/components/Members.js | 22 +- client/src/components/MembersBlank.js | 3 +- client/src/components/TeamPage.js | 1 + package-lock.json | 6 +- package.json | 1 + server/package-lock.json | 387 +++++--- server/package.json | 6 +- server/src/app.ts | 935 +++--------------- server/src/app2.ts | 818 +++++++++++++++ server/src/auth.ts | 7 + server/src/directives/auth.ts | 20 + server/src/directives/index.ts | 5 + server/src/models/index.ts | 6 + server/src/models/notification.ts | 32 + server/src/models/team.ts | 43 + server/src/models/user.ts | 55 ++ server/src/resolvers/index.ts | 5 + server/src/resolvers/source/index.ts | 15 + server/src/resolvers/team/index.ts | 14 + server/src/resolvers/team/mutations/index.ts | 1 + .../resolvers/team/mutations/updateTeam.ts | 21 + server/src/resolvers/team/queries/getTeam.ts | 21 + server/src/resolvers/team/queries/getTeams.ts | 44 + server/src/resolvers/team/queries/index.ts | 3 + server/src/resolvers/user/index.ts | 15 + server/src/resolvers/user/mutations/index.ts | 1 + .../resolvers/user/mutations/updateUser.ts | 18 + server/src/resolvers/user/queries/getUser.ts | 19 + .../resolvers/user/queries/getUserProfile.ts | 14 + server/src/resolvers/user/queries/getUsers.ts | 67 ++ server/src/resolvers/user/queries/index.ts | 5 + server/src/routes/strategies.ts | 10 +- server/src/routes/user.ts | 3 +- server/src/typeDefs/index.ts | 6 + server/src/typeDefs/notification.ts | 29 + server/src/typeDefs/root.ts | 12 + server/src/typeDefs/team.ts | 25 + server/src/typeDefs/user.ts | 30 + server/src/types/index.ts | 7 + server/src/types/mongoose.ts | 9 + server/src/types/notification.ts | 20 + server/src/types/team.ts | 17 + server/src/types/user.ts | 25 + server/tsconfig.json | 3 +- workspace.code-workspace | 7 + 46 files changed, 1826 insertions(+), 989 deletions(-) create mode 100644 server/src/app2.ts create mode 100644 server/src/auth.ts create mode 100644 server/src/directives/auth.ts create mode 100644 server/src/directives/index.ts create mode 100644 server/src/models/index.ts create mode 100644 server/src/models/notification.ts create mode 100644 server/src/models/team.ts create mode 100644 server/src/models/user.ts create mode 100644 server/src/resolvers/index.ts create mode 100644 server/src/resolvers/source/index.ts create mode 100644 server/src/resolvers/team/index.ts create mode 100644 server/src/resolvers/team/mutations/index.ts create mode 100644 server/src/resolvers/team/mutations/updateTeam.ts create mode 100644 server/src/resolvers/team/queries/getTeam.ts create mode 100644 server/src/resolvers/team/queries/getTeams.ts create mode 100644 server/src/resolvers/team/queries/index.ts create mode 100644 server/src/resolvers/user/index.ts create mode 100644 server/src/resolvers/user/mutations/index.ts create mode 100644 server/src/resolvers/user/mutations/updateUser.ts create mode 100644 server/src/resolvers/user/queries/getUser.ts create mode 100644 server/src/resolvers/user/queries/getUserProfile.ts create mode 100644 server/src/resolvers/user/queries/getUsers.ts create mode 100644 server/src/resolvers/user/queries/index.ts create mode 100644 server/src/typeDefs/index.ts create mode 100644 server/src/typeDefs/notification.ts create mode 100644 server/src/typeDefs/root.ts create mode 100644 server/src/typeDefs/team.ts create mode 100644 server/src/typeDefs/user.ts create mode 100644 server/src/types/index.ts create mode 100644 server/src/types/mongoose.ts create mode 100644 server/src/types/notification.ts create mode 100644 server/src/types/team.ts create mode 100644 server/src/types/user.ts create mode 100644 workspace.code-workspace diff --git a/client/src/components/EditProfile.js b/client/src/components/EditProfile.js index 0d762df5..f04fe93c 100644 --- a/client/src/components/EditProfile.js +++ b/client/src/components/EditProfile.js @@ -220,7 +220,7 @@ class EditProfile extends Component { checkProfanity = () => { var profanityExists = false; const profanities = Object.keys(this.state).map((key) => { - if (typeof(this.state[key]) === "string") { + if (typeof(this.state[key]) === "string" && key != "name") { const k = `${key}_profane` const isProfane = this.profanityFilter.isProfane(this.state[key]) this.setState({[k]: isProfane}) diff --git a/client/src/components/Members.js b/client/src/components/Members.js index 12c8b1b5..7ea557ee 100644 --- a/client/src/components/Members.js +++ b/client/src/components/Members.js @@ -8,31 +8,12 @@ import "./css/Members.css"; import environment from "./Environment"; import { Link } from "react-router-dom"; -// edit this query to pull on the team members -const getUsersQuery = graphql` - query MembersQuery { - user_profile { - team { - members { - name - school - grad_year - contact - skills - experience - } - } - } - } -`; - class Members extends Component { render() { let memberCards = []; // sample users array let users = []; - console.log("Team: " + this.props.members); if (this.props.members) { users = this.props.members; } @@ -47,11 +28,12 @@ class Members extends Component { name={user.name} grad_year={user.grad_year} school={user.school} - contact={user.contact} + contact={user.email} skills={user.skills.filter(function(el) { return Boolean(el); })} experience={user.experience} + slackid={user.slackid} /> ); } diff --git a/client/src/components/MembersBlank.js b/client/src/components/MembersBlank.js index 90a73cf7..a763b997 100644 --- a/client/src/components/MembersBlank.js +++ b/client/src/components/MembersBlank.js @@ -46,11 +46,12 @@ class Members extends Component { name={user.name} grad_year={user.grad_year} school={user.school} - contact={user.contact} + contact={user.email} skills={user.skills.filter(function(el) { return Boolean(el); })} experience={user.experience} + slackid={user.slackid} /> ); } diff --git a/client/src/components/TeamPage.js b/client/src/components/TeamPage.js index b90f12c0..76395fae 100644 --- a/client/src/components/TeamPage.js +++ b/client/src/components/TeamPage.js @@ -21,6 +21,7 @@ const getTeamQuery = graphql ` picture members { name + email school grad_year contact diff --git a/package-lock.json b/package-lock.json index 57955a50..623e1e94 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1044,9 +1044,9 @@ } }, "@types/express-serve-static-core": { - "version": "4.17.8", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.8.tgz", - "integrity": "sha512-1SJZ+R3Q/7mLkOD9ewCBDYD2k0WyZQtWYqF/2VvoNN2/uhI49J9CDN4OAm+wGMA0DbArA4ef27xl4+JwMtGggw==", + "version": "4.17.17", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.17.tgz", + "integrity": "sha512-YYlVaCni5dnHc+bLZfY908IG1+x5xuibKZMGv8srKkvtul3wUuanYvpIj9GXXoWkQbaAdR+kgX46IETKUALWNQ==", "requires": { "@types/node": "*", "@types/qs": "*", diff --git a/package.json b/package.json index 20b0bdfa..8ed502e0 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "build": "gulp build" }, "dependencies": { + "@types/express-serve-static-core": "^4.17.17", "apollo-server": "^2.16.0", "apollo-server-express": "^1.4.0", "bad-words": "^3.0.2", diff --git a/server/package-lock.json b/server/package-lock.json index 8a317955..02e96a28 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -5,9 +5,9 @@ "requires": true, "dependencies": { "@apollo/protobufjs": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@apollo/protobufjs/-/protobufjs-1.0.4.tgz", - "integrity": "sha512-EE3zx+/D/wur/JiLp6VCiw1iYdyy1lCJMf8CGPkLeDt5QJrN4N8tKFx33Ah4V30AUQzMk7Uz4IXKZ1LOj124gA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@apollo/protobufjs/-/protobufjs-1.0.5.tgz", + "integrity": "sha512-ZtyaBH1icCgqwIGb3zrtopV2D5Q8yxibkJzlaViM08eOhTQc7rACdYu0pfORFfhllvdMZ3aq69vifYHszY4gNA==", "requires": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", @@ -25,9 +25,9 @@ }, "dependencies": { "@types/node": { - "version": "10.17.28", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.28.tgz", - "integrity": "sha512-dzjES1Egb4c1a89C7lKwQh8pwjYmlOAG9dW1pBgxEk57tMrLnssOfEthz8kdkNaBd7lIqQx7APm5+mZ619IiCQ==" + "version": "10.17.49", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.49.tgz", + "integrity": "sha512-PGaJNs5IZz5XgzwJvL/1zRfZB7iaJ5BydZ8/Picm+lUNYoNO9iVTQkVy5eUh0dZDrx3rBOIs3GCbCRmMuYyqwg==" } } }, @@ -1469,9 +1469,9 @@ } }, "@types/connect": { - "version": "3.4.33", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.33.tgz", - "integrity": "sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A==", + "version": "3.4.34", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.34.tgz", + "integrity": "sha512-ePPA/JuI+X0vb+gSWlPKOY0NdNAie/rPUqX2GUPpbZwiKTkSPhjXWuee47E4MtE54QVzGCQMQkAL6JhV2E1+cQ==", "requires": { "@types/node": "*" } @@ -1497,9 +1497,9 @@ "dev": true }, "@types/cookies": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/@types/cookies/-/cookies-0.7.4.tgz", - "integrity": "sha512-oTGtMzZZAVuEjTwCjIh8T8FrC8n/uwy+PG0yTvQcdZ7etoel7C7/3MSd7qrukENTgQtotG7gvBlBojuVs7X5rw==", + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@types/cookies/-/cookies-0.7.5.tgz", + "integrity": "sha512-3+TAFSm78O7/bAeYdB8FoYGntuT87vVP9JKuQRL8sRhv9313LP2SpHHL50VeFtnyjIcb3UELddMk5Yt0eOSOkg==", "requires": { "@types/connect": "*", "@types/express": "*", @@ -1508,9 +1508,9 @@ } }, "@types/cors": { - "version": "2.8.7", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.7.tgz", - "integrity": "sha512-sOdDRU3oRS7LBNTIqwDkPJyq0lpHYcbMTt0TrjzsXbk/e37hcLTH6eZX7CdbDeN0yJJvzw9hFBZkbtCSbk/jAQ==", + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.8.tgz", + "integrity": "sha512-fO3gf3DxU2Trcbr75O7obVndW/X5k8rJNZkLXlQWStTHhP71PkRqjwPIEI0yMnJdg9R9OasjU+Bsr+Hr1xy/0w==", "requires": { "@types/express": "*" } @@ -1584,14 +1584,21 @@ } }, "@types/graphql-upload": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/@types/graphql-upload/-/graphql-upload-8.0.3.tgz", - "integrity": "sha512-hmLg9pCU/GmxBscg8GCr1vmSoEmbItNNxdD5YH2TJkXm//8atjwuprB+xJBK714JG1dkxbbhp5RHX+Pz1KsCMA==", + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/@types/graphql-upload/-/graphql-upload-8.0.4.tgz", + "integrity": "sha512-0TRyJD2o8vbkmJF8InppFcPVcXKk+Rvlg/xvpHBIndSJYpmDWfmtx/ZAtl4f3jR2vfarpTqYgj8MZuJssSoU7Q==", "requires": { "@types/express": "*", "@types/fs-capacitor": "*", "@types/koa": "*", - "graphql": "^14.5.3" + "graphql": "^15.3.0" + }, + "dependencies": { + "graphql": { + "version": "15.4.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-15.4.0.tgz", + "integrity": "sha512-EB3zgGchcabbsU9cFe1j+yxdzKQKAbGUWRb13DsrsMN1yyfmmIq+2+L5MqVWcDCE4V89R5AyUOi7sMOGxdsYtA==" + } } }, "@types/http-assert": { @@ -1599,20 +1606,26 @@ "resolved": "https://registry.npmjs.org/@types/http-assert/-/http-assert-1.5.1.tgz", "integrity": "sha512-PGAK759pxyfXE78NbKxyfRcWYA/KwW17X290cNev/qAsn9eQIxkH4shoNBafH37wewhDG/0p1cHPbK6+SzZjWQ==" }, + "@types/http-errors": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-1.8.0.tgz", + "integrity": "sha512-2aoSC4UUbHDj2uCsCxcG/vRMXey/m17bC7UwitVm5hn22nI8O8Y9iDpA76Orc+DWkQ4zZrOKEshCqR/jSuXAHA==" + }, "@types/keygrip": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@types/keygrip/-/keygrip-1.0.2.tgz", "integrity": "sha512-GJhpTepz2udxGexqos8wgaBx4I/zWIDPh/KOGEwAqtuGDkOUJu5eFvwmdBX4AmB8Odsr+9pHCQqiAqDL/yKMKw==" }, "@types/koa": { - "version": "2.11.3", - "resolved": "https://registry.npmjs.org/@types/koa/-/koa-2.11.3.tgz", - "integrity": "sha512-ABxVkrNWa4O/Jp24EYI/hRNqEVRlhB9g09p48neQp4m3xL1TJtdWk2NyNQSMCU45ejeELMQZBYyfstyVvO2H3Q==", + "version": "2.11.6", + "resolved": "https://registry.npmjs.org/@types/koa/-/koa-2.11.6.tgz", + "integrity": "sha512-BhyrMj06eQkk04C97fovEDQMpLpd2IxCB4ecitaXwOKGq78Wi2tooaDOWOFGajPk8IkQOAtMppApgSVkYe1F/A==", "requires": { "@types/accepts": "*", "@types/content-disposition": "*", "@types/cookies": "*", "@types/http-assert": "*", + "@types/http-errors": "*", "@types/keygrip": "*", "@types/koa-compose": "*", "@types/node": "*" @@ -1742,9 +1755,9 @@ } }, "@types/qs": { - "version": "6.9.4", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.4.tgz", - "integrity": "sha512-+wYo+L6ZF6BMoEjtf8zB2esQsqdV6WsjRK/GP9WOgLPrq87PbNWgIxS76dS5uvl/QXtHGakZmwTznIfcPXcKlQ==" + "version": "6.9.5", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.5.tgz", + "integrity": "sha512-/JHkVHtx/REVG0VVToGRGH2+23hsYLHdyG+GrvoUGlGAd0ErauXDyvHtRI/7H7mzLm+tBCKA7pfcpkQ1lf58iQ==" }, "@types/qwest": { "version": "1.7.28", @@ -1812,9 +1825,9 @@ } }, "@types/ws": { - "version": "7.2.6", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.2.6.tgz", - "integrity": "sha512-Q07IrQUSNpr+cXU4E4LtkSIBPie5GLZyyMC1QtQYRLWz701+XcoVygGUZgvLqElq1nU4ICldMYPnexlBsg3dqQ==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.4.0.tgz", + "integrity": "sha512-Y29uQ3Uy+58bZrFLhX36hcI3Np37nqWE7ky5tjiDoy1GDZnIwVxS0CgF+s+1bXMzjKBFy+fqaRfb708iNzdinw==", "requires": { "@types/node": "*" } @@ -2144,12 +2157,12 @@ } }, "apollo-cache-control": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/apollo-cache-control/-/apollo-cache-control-0.11.1.tgz", - "integrity": "sha512-6iHa8TkcKt4rx5SKRzDNjUIpCQX+7/FlZwD7vRh9JDnM4VH8SWhpj8fUR3CiEY8Kuc4ChXnOY8bCcMju5KPnIQ==", + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/apollo-cache-control/-/apollo-cache-control-0.11.4.tgz", + "integrity": "sha512-FUKE8ASr8GxVq5rmky/tY8bsf++cleGT591lfLiqnPsP1fo3kAfgRfWA2QRHTCKFNlQxzUhVOEDv+PaysqiOjw==", "requires": { "apollo-server-env": "^2.4.5", - "apollo-server-plugin-base": "^0.9.1" + "apollo-server-plugin-base": "^0.10.2" } }, "apollo-datasource": { @@ -2161,37 +2174,6 @@ "apollo-server-env": "^2.4.5" } }, - "apollo-engine-reporting": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/apollo-engine-reporting/-/apollo-engine-reporting-2.3.0.tgz", - "integrity": "sha512-SbcPLFuUZcRqDEZ6mSs8uHM9Ftr8yyt2IEu0JA8c3LNBmYXSLM7MHqFe80SVcosYSTBgtMz8mLJO8orhYoSYZw==", - "requires": { - "apollo-engine-reporting-protobuf": "^0.5.2", - "apollo-graphql": "^0.5.0", - "apollo-server-caching": "^0.5.2", - "apollo-server-env": "^2.4.5", - "apollo-server-errors": "^2.4.2", - "apollo-server-plugin-base": "^0.9.1", - "apollo-server-types": "^0.5.1", - "async-retry": "^1.2.1", - "uuid": "^8.0.0" - }, - "dependencies": { - "uuid": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.0.tgz", - "integrity": "sha512-fX6Z5o4m6XsXBdli9g7DtWgAx+osMsRRZFKma1mIUsLCz6vRvv+pz5VNbyu9UEDzpMWulZfvpgb/cmDXVulYFQ==" - } - } - }, - "apollo-engine-reporting-protobuf": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/apollo-engine-reporting-protobuf/-/apollo-engine-reporting-protobuf-0.5.2.tgz", - "integrity": "sha512-4wm9FR3B7UvJxcK/69rOiS5CAJPEYKufeRWb257ZLfX7NGFTMqvbc1hu4q8Ch7swB26rTpkzfsftLED9DqH9qg==", - "requires": { - "@apollo/protobufjs": "^1.0.3" - } - }, "apollo-env": { "version": "0.6.5", "resolved": "https://registry.npmjs.org/apollo-env/-/apollo-env-0.6.5.tgz", @@ -2204,16 +2186,16 @@ }, "dependencies": { "core-js": { - "version": "3.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz", - "integrity": "sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA==" + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.8.1.tgz", + "integrity": "sha512-9Id2xHY1W7m8hCl8NkhQn5CufmF/WuR30BTRewvCXc1aZd3kMECwNZ69ndLbekKfakw9Rf2Xyc+QR6E7Gg+obg==" } } }, "apollo-graphql": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/apollo-graphql/-/apollo-graphql-0.5.0.tgz", - "integrity": "sha512-YSdF/BKPbsnQpxWpmCE53pBJX44aaoif31Y22I/qKpB6ZSGzYijV5YBoCL5Q15H2oA/v/02Oazh9lbp4ek3eig==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/apollo-graphql/-/apollo-graphql-0.6.0.tgz", + "integrity": "sha512-BxTf5LOQe649e9BNTPdyCGItVv4Ll8wZ2BKnmiYpRAocYEXAVrQPWuSr3dO4iipqAU8X0gvle/Xu9mSqg5b7Qg==", "requires": { "apollo-env": "^0.6.5", "lodash.sortby": "^4.7.0" @@ -2240,6 +2222,14 @@ "tslib": "^1.9.3" } }, + "apollo-reporting-protobuf": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/apollo-reporting-protobuf/-/apollo-reporting-protobuf-0.6.1.tgz", + "integrity": "sha512-qr4DheFP154PGZsd93SSIS9RkqHnR5b6vT+eCloWjy3UIpY+yZ3cVLlttlIjYvOG4xTJ25XEwcHiAExatQo/7g==", + "requires": { + "@apollo/protobufjs": "^1.0.3" + } + }, "apollo-server-caching": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/apollo-server-caching/-/apollo-server-caching-0.5.2.tgz", @@ -2264,31 +2254,35 @@ } }, "apollo-server-core": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/apollo-server-core/-/apollo-server-core-2.16.1.tgz", - "integrity": "sha512-nuwn5ZBbmzPwDetb3FgiFFJlNK7ZBFg8kis/raymrjd3eBGdNcOyMTJDl6J9673X9Xqp+dXQmFYDW/G3G8S1YA==", + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/apollo-server-core/-/apollo-server-core-2.19.0.tgz", + "integrity": "sha512-2aMKUVPyNbomJQaG2tkpfqvp1Tfgxgkdr7nX5zHudYNSzsPrHw+CcYlCbIVFFI/mTZsjoK9czNq1qerFRxZbJw==", "requires": { "@apollographql/apollo-tools": "^0.4.3", "@apollographql/graphql-playground-html": "1.6.26", "@types/graphql-upload": "^8.0.0", "@types/ws": "^7.0.0", - "apollo-cache-control": "^0.11.1", + "apollo-cache-control": "^0.11.4", "apollo-datasource": "^0.7.2", - "apollo-engine-reporting": "^2.3.0", + "apollo-graphql": "^0.6.0", + "apollo-reporting-protobuf": "^0.6.1", "apollo-server-caching": "^0.5.2", "apollo-server-env": "^2.4.5", "apollo-server-errors": "^2.4.2", - "apollo-server-plugin-base": "^0.9.1", - "apollo-server-types": "^0.5.1", - "apollo-tracing": "^0.11.1", + "apollo-server-plugin-base": "^0.10.2", + "apollo-server-types": "^0.6.1", + "apollo-tracing": "^0.12.0", + "async-retry": "^1.2.1", "fast-json-stable-stringify": "^2.0.0", - "graphql-extensions": "^0.12.4", + "graphql-extensions": "^0.12.6", "graphql-tag": "^2.9.2", "graphql-tools": "^4.0.0", "graphql-upload": "^8.0.2", "loglevel": "^1.6.7", + "lru-cache": "^5.0.0", "sha.js": "^2.4.11", "subscriptions-transport-ws": "^0.9.11", + "uuid": "^8.0.0", "ws": "^6.0.0" }, "dependencies": { @@ -2302,8 +2296,28 @@ "deprecated-decorator": "^0.1.6", "iterall": "^1.1.3", "uuid": "^3.1.0" + }, + "dependencies": { + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + } + } + }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "requires": { + "yallist": "^3.0.2" } }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + }, "ws": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", @@ -2311,6 +2325,11 @@ "requires": { "async-limiter": "~1.0.0" } + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" } } }, @@ -2329,18 +2348,19 @@ "integrity": "sha512-FeGxW3Batn6sUtX3OVVUm7o56EgjxDlmgpTLNyWcLb0j6P8mw9oLNyAm3B+deHA4KNdNHO5BmHS2g1SJYjqPCQ==" }, "apollo-server-express": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/apollo-server-express/-/apollo-server-express-2.16.1.tgz", - "integrity": "sha512-Oq5YNcaMYnRk6jDmA9LWf8oSd2KHDVe7jQ4wtooAvG9FVUD+FaFBgSkytXHMvtifQh2wdF07Ri8uDLMz6IQjTw==", + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/apollo-server-express/-/apollo-server-express-2.19.0.tgz", + "integrity": "sha512-3rgSrTme1SlLoecAYtSa8ThH6vYvz29QecgZCigq5Vdc6bFP2SZrCk0ls6BAdD8OZbVKUtizzRxd0yd/uREPAw==", "requires": { "@apollographql/graphql-playground-html": "1.6.26", "@types/accepts": "^1.3.5", "@types/body-parser": "1.19.0", - "@types/cors": "^2.8.4", + "@types/cors": "2.8.8", "@types/express": "4.17.7", + "@types/express-serve-static-core": "4.17.13", "accepts": "^1.3.5", - "apollo-server-core": "^2.16.1", - "apollo-server-types": "^0.5.1", + "apollo-server-core": "^2.19.0", + "apollo-server-types": "^0.6.1", "body-parser": "^1.18.3", "cors": "^2.8.4", "express": "^4.17.1", @@ -2371,6 +2391,16 @@ "@types/serve-static": "*" } }, + "@types/express-serve-static-core": { + "version": "4.17.13", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.13.tgz", + "integrity": "sha512-RgDi5a4nuzam073lRGKTUIaL3eF2+H7LJvJ8eUnCI0wA6SNjXc44DCmWNiTLs/AZ7QlsFWZiw/gTG3nSQGL0fA==", + "requires": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, "bytes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", @@ -2635,30 +2665,30 @@ } }, "apollo-server-plugin-base": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/apollo-server-plugin-base/-/apollo-server-plugin-base-0.9.1.tgz", - "integrity": "sha512-kvrX4Z3FdpjrZdHkyl5iY2A1Wvp4b6KQp00DeZqss7GyyKNUBKr80/7RQgBLEw7EWM7WB19j459xM/TjvW0FKQ==", + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/apollo-server-plugin-base/-/apollo-server-plugin-base-0.10.2.tgz", + "integrity": "sha512-uM5uL1lOxbXdgvt/aEIbgs40fV9xA45Y3Mmh0VtQ/ddqq0MXR5aG92nnf8rM+URarBCUfxKJKaYzJJ/CXAnEdA==", "requires": { - "apollo-server-types": "^0.5.1" + "apollo-server-types": "^0.6.1" } }, "apollo-server-types": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/apollo-server-types/-/apollo-server-types-0.5.1.tgz", - "integrity": "sha512-my2cPw+DAb2qVnIuBcsRKGyS28uIc2vjFxa1NpRoJZe9gK0BWUBk7wzXnIzWy3HZ5Er11e/40MPTUesNfMYNVA==", + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/apollo-server-types/-/apollo-server-types-0.6.1.tgz", + "integrity": "sha512-IEQ37aYvMLiTUzsySVLOSuvvhxuyYdhI05f3cnH6u2aN1HgGp7vX6bg+U3Ue8wbHfdcifcGIk5UEU+Q+QO6InA==", "requires": { - "apollo-engine-reporting-protobuf": "^0.5.2", + "apollo-reporting-protobuf": "^0.6.1", "apollo-server-caching": "^0.5.2", "apollo-server-env": "^2.4.5" } }, "apollo-tracing": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/apollo-tracing/-/apollo-tracing-0.11.1.tgz", - "integrity": "sha512-l7g+uILw7v32GA46IRXIx5XXbZhFI96BhSqrGK9yyvfq+NMcvVZrj3kIhRImPGhAjMdV+5biA/jztabElAbDjg==", + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/apollo-tracing/-/apollo-tracing-0.12.0.tgz", + "integrity": "sha512-cMUYGE6mOEwb9HDqhf4fiPEo2JMhjPIqEprAQEC57El76avRpRig5NM0bnqMZcYJZR5QmLlNcttNccOwf9WrNg==", "requires": { "apollo-server-env": "^2.4.5", - "apollo-server-plugin-base": "^0.9.1" + "apollo-server-plugin-base": "^0.10.2" } }, "apollo-upload-client": { @@ -3494,6 +3524,15 @@ "unset-value": "^1.0.0" } }, + "call-bind": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.0.tgz", + "integrity": "sha512-AEXsYIyyDY3MCzbwdhzG3Jx1R0J2wetQyUynn6dYHAO+bg8l1k7jwZtRv4ryryFs7EP+NDlikJlVe59jr0cM2w==", + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.0" + } + }, "camel-case": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.1.tgz", @@ -4361,19 +4400,19 @@ } }, "es-abstract": { - "version": "1.17.6", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", - "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", + "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", "requires": { "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", "has": "^1.0.3", "has-symbols": "^1.0.1", - "is-callable": "^1.2.0", - "is-regex": "^1.1.0", - "object-inspect": "^1.7.0", + "is-callable": "^1.2.2", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", "object-keys": "^1.1.1", - "object.assign": "^4.1.0", + "object.assign": "^4.1.1", "string.prototype.trimend": "^1.0.1", "string.prototype.trimstart": "^1.0.1" }, @@ -4387,6 +4426,17 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + }, + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } } } }, @@ -5596,6 +5646,23 @@ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" }, + "get-intrinsic": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.0.2.tgz", + "integrity": "sha512-aeX0vrFm21ILl3+JpFFRNe9aUvp6VFZb2/CTbgLb8j75kOhvoNYjt9d8KA/tJG4gSo8nzEDedRl0h7vDmBYRVg==", + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + }, + "dependencies": { + "has-symbols": { + "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==" + } + } + }, "get-stream": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", @@ -5774,13 +5841,13 @@ } }, "graphql-extensions": { - "version": "0.12.4", - "resolved": "https://registry.npmjs.org/graphql-extensions/-/graphql-extensions-0.12.4.tgz", - "integrity": "sha512-GnR4LiWk3s2bGOqIh6V1JgnSXw2RCH4NOgbCFEWvB6JqWHXTlXnLZ8bRSkCiD4pltv7RHUPWqN/sGh8R6Ae/ag==", + "version": "0.12.6", + "resolved": "https://registry.npmjs.org/graphql-extensions/-/graphql-extensions-0.12.6.tgz", + "integrity": "sha512-EUNw+OIRXYTPxToSoJjhJvS5aGa94KkdkZnL1I9DCZT64/+rzQNeLeGj+goj2RYuYvoQe1Bmcx0CNZ1GqwBhng==", "requires": { "@apollographql/apollo-tools": "^0.4.3", "apollo-server-env": "^2.4.5", - "apollo-server-types": "^0.5.1" + "apollo-server-types": "^0.6.1" } }, "graphql-subscriptions": { @@ -6348,9 +6415,9 @@ "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" }, "is-callable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", - "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==" + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", + "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==" }, "is-ci": { "version": "1.2.1", @@ -6440,6 +6507,11 @@ "resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz", "integrity": "sha1-aRC8pdqMleeEtXUbl2z1oQ/uNtI=" }, + "is-negative-zero": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", + "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==" + }, "is-npm": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz", @@ -6837,9 +6909,9 @@ "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=" }, "loglevel": { - "version": "1.6.8", - "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.8.tgz", - "integrity": "sha512-bsU7+gc9AJ2SqpzxwU3+1fedl8zAntbtC5XYlt3s2j1hJcn2PsXSmgN8TaLG/J1/2mod4+cE/3vNL70/c1RNCA==" + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.7.1.tgz", + "integrity": "sha512-Hesni4s5UkWkwCGJMQGAh71PaLUmKFM60dHvq0zi/vDhhrzuk+4GgNbTXJ12YYQJn6ZKBDNIjYcuQGKudvqrIw==" }, "long": { "version": "4.0.0", @@ -7608,9 +7680,9 @@ } }, "object-inspect": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", - "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==" + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz", + "integrity": "sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==" }, "object-keys": { "version": "1.1.0", @@ -7618,9 +7690,9 @@ "integrity": "sha512-6OO5X1+2tYkNyNEx6TsCxEqFfRWaqx6EtMiSbGrw8Ob8v9Ne+Hl8rBAgLBZn5wjEz3s/s6U1WXFUFOcxxAwUpg==" }, "object-path": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/object-path/-/object-path-0.11.4.tgz", - "integrity": "sha1-NwrnUvvzfePqcKhhwju6iRVpGUk=" + "version": "0.11.5", + "resolved": "https://registry.npmjs.org/object-path/-/object-path-0.11.5.tgz", + "integrity": "sha512-jgSbThcoR/s+XumvGMTMf81QVBmah+/Q7K7YduKeKVWL7N111unR2d6pZZarSk6kY/caeNxUDyxOvMWyzoU2eg==" }, "object-visit": { "version": "1.0.1", @@ -7653,12 +7725,55 @@ } }, "object.getownpropertydescriptors": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz", - "integrity": "sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.1.tgz", + "integrity": "sha512-6DtXgZ/lIZ9hqx4GtZETobXLR/ZLaa0aqV0kzbn80Rf8Z2e/XFnhA0I7p07N2wH8bBBltr2xQPi6sbKWAY2Eng==", "requires": { + "call-bind": "^1.0.0", "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1" + "es-abstract": "^1.18.0-next.1" + }, + "dependencies": { + "es-abstract": { + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-negative-zero": "^2.0.0", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + }, + "has-symbols": { + "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==" + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + }, + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + } } }, "object.map": { @@ -9233,21 +9348,21 @@ } }, "string.prototype.trimend": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", - "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.3.tgz", + "integrity": "sha512-ayH0pB+uf0U28CtjlLvL7NaohvR1amUvVZk+y3DYb0Ey2PUV5zPkkKy9+U1ndVEIXO8hNg18eIv9Jntbii+dKw==", "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" } }, "string.prototype.trimstart": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", - "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.3.tgz", + "integrity": "sha512-oBIBUy5lea5tt0ovtOFiEQaBkoBBkyJhZXzJYrSmDo5IUUqbOPvVezuRs/agBIdZ2p2Eo1FD6bD9USyBLfl3xg==", "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" } }, "string_decoder": { @@ -9643,9 +9758,9 @@ } }, "typescript": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.4.1.tgz", - "integrity": "sha512-3NSMb2VzDQm8oBTLH6Nj55VVtUEpe/rgkIzMir0qVoLyjDZlnMBva0U6vDiV3IH+sl/Yu6oP5QwsAQtHPmDd2Q==" + "version": "3.9.7", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz", + "integrity": "sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==" }, "ua-parser-js": { "version": "0.7.21", diff --git a/server/package.json b/server/package.json index 7e59fc38..488c463c 100644 --- a/server/package.json +++ b/server/package.json @@ -16,7 +16,7 @@ "@types/mongodb": "^3.1.22", "@types/oauth": "^0.9.1", "ajv": "^4.11.2", - "apollo-server-express": "^2.16.1", + "apollo-server-express": "^2.9.12", "compression": "^1.6.2", "connect-flash": "^0.1.1", "cors": "^2.8.5", @@ -25,7 +25,7 @@ "express-graphql": "^0.7.1", "express-session": "^1.15.6", "git-rev-sync": "^1.8.0", - "graphql": "^14.7.0", + "graphql": "^14.5.8", "graphql-tools": "^6.0.14", "gulp-copy": "^4.0.1", "gulp-tslint": "^8.1.4", @@ -44,7 +44,7 @@ "request": "^2.88.0", "serve-static": "^1.11.1", "tslint": "^5.15.0", - "typescript": "^3.3.3333", + "typescript": "^3.7.2", "uuid": "^3.3.2", "webpack": "^4.29.6" }, diff --git a/server/src/app.ts b/server/src/app.ts index 004830d1..478ef728 100644 --- a/server/src/app.ts +++ b/server/src/app.ts @@ -1,821 +1,114 @@ -import fs from "fs"; -import path from "path"; -import express from "express"; -import compression from "compression"; -import morgan from "morgan"; -import passport from "passport"; -import session from "express-session"; -import mongoose from "mongoose"; - -// import express_graphql from "express-graphql" -import cors from "cors"; -import dotenv from "dotenv"; -const { ApolloServer, gql } = require("apollo-server-express"); -// import { buildSchema } from "graphql" -import { GroundTruthStrategy } from "./routes/strategies"; -import { IUser, User, Notification, Team } from "./schema"; -import { userRoutes } from "./routes/user"; -import sendSlackMessage from "./sendSlackMessage"; - -dotenv.config(); - -const PORT = 3000; -const typeDefs = gql` - ${fs.readFileSync(path.resolve(__dirname, "../api.graphql"), "utf8")} -`; -const VERSION_NUMBER = JSON.parse( - fs.readFileSync(path.resolve(__dirname, "../package.json"), "utf8") -).version; -//const VERSION_HASH = require("git-rev-sync").short(); - -export let app = express(); - -if (process.env.ISPRODUCTION === "true") { - app.enable("trust proxy"); -} else { - console.warn("OAuth callback(s) running in development mode"); -} - -app.use(morgan("dev")); -app.use(compression()); -app.use(cors()); -const session_secret = process.env["SECRET"]; -if (!session_secret) { - throw new Error("Secret not specified"); -} -app.use( - session({ - secret: session_secret, - saveUninitialized: false, - resave: true, - }) -); - -app.use(passport.initialize()); -app.use(passport.session()); - -export function loggedInErr(req, res, next) { - if (req.user) { - res.status(200).json({ - success: true, - }); - next(); - } else { - res.status(401).json({ error: "User not logged in", success: false }); - return; - } -} - -const gturl = String(process.env.GROUNDTRUTHURL || "login.hack.gt"); -const groundTruthStrategy = new GroundTruthStrategy(gturl); -passport.use(groundTruthStrategy); -passport.serializeUser((user, done) => { - done(null, user.uuid); -}); -passport.deserializeUser((id, done) => { - User.findOne({ uuid: id }, (err, user) => { - done(err, user!); - }); -}); - -let getTeam = async function (parent, args, context, info, req) { - if (!context._id) { - throw new Error("User is not logged in!"); - } - let team = await Team.findById(args.team_id).populate("members"); - if (!team) { - throw new Error("Team not found!"); - } - return team; -}; -let getTeams = async function (parent, args, context, info, req) { - console.log("getting teams.."); - let teams; - let search; - let interests; - let interestSearch = [] as {}[]; - - if (args.search) { - search = '"' + args.search.split(" ").join('" "') + '"'; - } - - if (args.interests) { - interests = args.interests.match(/\w+/g); - for (let i = 0; i < interests.length; i++) { - interestSearch.push({ - interests: { - $elemMatch: { $regex: ".*" + interests[i] + ".*", $options: "i" }, - }, - }); - } - } - - if (search && interests) { - console.log("search and interest filters applied"); - teams = await Team.find({ - $and: [ - { public: true }, - { $text: { $search: search } }, - { $and: interestSearch }, - ], - }).populate("members"); - } else if (search) { - console.log(`searching for ${search}..`); - teams = await Team.find({ - $and: [{ public: true }, { $text: { $search: search } }], - }).populate("members"); - } else if (interests) { - console.log(`interest filter(s) applied: ${interestSearch}`); - teams = await Team.find({ - $and: [{ public: true }, { $and: interestSearch }], - }).populate("members"); - } else { - console.log("no filter applied"); - teams = await Team.find({ - public: true, - }).populate("members"); - } - - if (!teams) { - return null; - } - - teams.sort(function (a, b) { - return a.name.toLowerCase() - b.name.toLowerCase(); - }); - return teams; -}; - -let getUser = async function (parent, args, context, info, req) { - if (!context._id) { - throw new Error("User not logged in"); - } - let user = await User.findById(args.user_id); - if (!user) { - throw new Error("User not found"); - } - return user; -}; - -let getUsers = async function (parent, args, context, info, req) { - if (!context._id) { - throw new Error("User not logged in"); - } - console.log("getting users.."); - let users; - let search; - let skills; - let grad_years; - let schools; - let skillSearch = [] as {}[]; - let yearSearch = [] as {}[]; - let schoolSearch = [] as {}[]; - - if (args.search) { - search = '"' + args.search.split(" ").join('" "') + '"'; - } - if (args.skill) { - skills = args.skill.match(/\w+/g); - for (let i = 0; i < skills.length; i++) { - skillSearch.push({ - skills: { - $elemMatch: { $regex: ".*" + skills[i] + ".*", $options: "i" }, - }, - }); - } - } - if (args.grad_year) { - grad_years = args.grad_year.match(/\w+/g); - for (let i = 0; i < grad_years.length; i++) { - yearSearch.push({ grad_year: grad_years[i] }); - } - } - if (args.school) { - schools = args.school.split(","); - for (let i = 0; i < schools.length; i++) { - schoolSearch.push({ school: schools[i] }); - } - } - - if (search && skills && grad_years && schools) { - console.log("all filters applied..."); - users = await User.find({ - $and: [ - { $text: { $search: search } }, - { $and: skillSearch }, - { $or: yearSearch }, - { $or: schoolSearch }, - ], - visible: 1 - }); - } else if (search && skills && grad_years) { - console.log("search, skills, and grad year filters applied..."); - users = await User.find({ - $and: [ - { $text: { $search: search } }, - { $and: skillSearch }, - { $or: yearSearch }, - ], - visible: 1 - }); - } else if (search && skills && schools) { - console.log("search, skills, and schools filters applied..."); - users = await User.find({ - $and: [ - { $text: { $search: search } }, - { $and: skillSearch }, - { $or: schoolSearch }, - ], - visible: 1 - }); - } else if (search && grad_years && schools) { - console.log("searc, grad year, and schools filters applied..."); - users = await User.find({ - $and: [ - { $text: { $search: search } }, - { $or: yearSearch }, - { $or: schoolSearch }, - ], - visible: 1 - }); - } else if (skills && grad_years && schools) { - console.log("skills, grad year, and schools filter applied..."); - users = await User.find({ - $and: [{ $and: skillSearch }, { $or: yearSearch }, { $or: schoolSearch }],visible: 1 - } - ); - } else if (search && skills) { - console.log("search and skills filters applied..."); - users = await User.find({ - $and: [{ $text: { $search: search } }, { $and: skillSearch }], visible: 1 - }); - } else if (search && grad_years) { - console.log("search and grad year filters applied..."); - users = await User.find({ - $and: [{ $text: { $search: search } }, { $or: yearSearch }], visible: 1 - }); - } else if (search && schools) { - console.log("search and schools filters applied..."); - users = await User.find({ - $and: [{ $text: { $search: search } }, { $or: schoolSearch }], visible: 1 - }); - } else if (skills && grad_years) { - console.log("skill and year filters applied"); - users = await User.find({ - $and: [{ $and: skillSearch }, { $or: yearSearch }], visible: 1 - }); - } else if (skills && schools) { - console.log("only skill and school filters applied"); - users = await User.find({ - $and: [{ $and: skillSearch }, { $or: schoolSearch }],visible: 1 - }); - } else if (grad_years && schools) { - console.log("only year and school filters applied"); - users = await User.find({ - $and: [{ $or: yearSearch }, { $or: schoolSearch }], visible: 1 - }); - } else if (search) { - console.log(`searching for ${search}..`); - users = await User.find({ $text: { $search: search }, visible: 1 }); - console.log(users); - } else if (skills) { - console.log("only skill filter(s) applied"); - users = await User.find({ - $and: skillSearch, visible: 1 - }); - } else if (grad_years) { - console.log("only year filter(s) applied"); - users = await User.find({ - $or: yearSearch, visible: 1 - }); - } else if (schools) { - console.log("only school filter(s) applied"); - users = await User.find({ - $or: schoolSearch, visible: 1 - }); - } else { - console.log("no filters applied"); - users = await User.find({visible: 1}); - } - - if (!users) { - return null; - } - - users = users - .sort(function (a, b) { - return a.name.toLowerCase() - b.name.toLowerCase(); - }) - .filter((item) => { - return item.uuid != context.uuid && !item.team && item.visible == 1; - }); - return users; -}; - -let updateUser = async function (parent, args, context, info, req) { - console.log(context._id); - if (!context._id) { - throw new Error("User not logged in"); - } - return await User.findByIdAndUpdate( - context._id, - { $set: args }, - { new: true } - ); -}; - -let updateTeam = async function (parent, args, context, info, req) { - console.log(context._id); - if (!context._id) { - throw new Error("User not logged in"); - } - let user = await User.findById(context._id).populate("team"); - if (!user) { - throw new Error("User not found"); - } - if (!user.team) { - throw new Error("User not on team"); - } - return await Team.findByIdAndUpdate(user.team, { $set: args }, { new: true }); -}; - -let leaveTeam = async function (parent, args, context, info, req) { - console.log("leaveing team"); - - if (!context._id) { - throw new Error("User not logged in"); - } - if (!context.team) { - throw new Error("User not on team"); - } - - await Team.findByIdAndUpdate( - context.team, - { - $pull: { - members: context._id, - }, - }, - async (err, team) => { - if (err || !team) { - console.log(err); - throw new Error("Issue updating team"); - } - console.log("TTTEAM", team); - if (team.members.length <= 1) { - await Team.findByIdAndDelete(context.team); - } - } - ); - - return await User.findByIdAndUpdate(context._id, { - team: null, - }); -}; - -let getSentTeamNotifications = async function ( - parent, - args, - context, - info, - req -) { - if (!context._id) { - throw new Error("User not logged in"); - } - let user = await User.findById(context._id); - if (!user) { - throw new Error("User not found"); - } - if (!user.team) { - throw new Error("User not on teams"); - } - return await Notification.find({ - sender: user.team, - resolved: false, - }).populate("receiver"); -}; - -let getUserProfile = async function (parent, args, context, info, req) { - return await User.findById(context._id).populate("team"); -}; - -/* - Accepting a user sending request to team - */ -let acceptTeamRequest = async function (parent, args, context, info, req) { - if (!context._id) { - throw new Error("User not logged in"); - } - let user = await User.findById(context._id).populate("team"); - if (!user) { - throw new Error("User not found"); - } - if (!user.team) { - throw new Error("User not on team"); - } - if (user.team.members.length >= 4) { - throw new Error("Team is full"); - } - let notification = await Notification.findById(args.notification_id) - .populate("sender") - .populate("receiver"); - if (!notification) { - throw new Error("Notification does not exist"); - } - if (notification.senderType == "Team") { - throw new Error("Cannot accept request from a team"); - } - if (notification.senderType == "User") { - console.log( - "TEAM", - user.team._id.toString(), - notification.receiver._id.toString() - ); - - if (user.team._id.toString() !== notification.receiver._id.toString()) { - throw new Error("Team does not own notification"); - } - let requestUser = await User.findById(notification.sender._id); - if (!requestUser) { - throw new Error("Notificaton sender not found"); - } - if (requestUser.team) { - throw new Error("Sender already on team"); - } - console.log("here2"); - - let requestedUserId = requestUser._id; - console.log(requestedUserId); - console.log(user.team._id); - console.log(mongoose.Types.ObjectId.isValid(requestedUserId)); - console.log(mongoose.Types.ObjectId.isValid(user.team._id)); - - await Team.findByIdAndUpdate( - user.team._id, - { - $push: { - members: requestedUserId, - }, - }, - function (err, user) { - console.log("here"); - console.log(err); - } - ); - await User.findByIdAndUpdate(notification.sender._id, { - team: user.team._id, - }); - await Notification.updateMany({ sender: notification.sender._id, receiver: notification.receiver._id }, { - resolved: true, - }); - - sendSlackMessage( - `${ - user!.team!.name - } has accepted your request. View your new team here: https://teamformation.hack.gt/team/${user.team._id}`, - requestUser.slackid - ); - console.log("slack message sent"); - } else { - throw new Error("Notification invalid"); - } -}; - -let acceptUserRequest = async function (parent, args, context, info, req) { - if (!context._id) { - throw new Error("User not logged in"); - } - let user = await User.findById(context._id); - if (!user) { - throw new Error("User not found"); - } - - let notification = await Notification.findById(args.notification_id) - .populate("sender") - .populate("receiver"); - if (!notification) { - throw new Error("Notification not found"); - } - console.log("ACCEPT USER REQUEST"); - console.log(notification.receiver._id); - console.log(user._id); - - if (notification.receiver._id.toString() != user._id.toString()) { - throw new Error("Notification not valid"); - } - await Notification.findByIdAndUpdate(notification._id, { - resolved: true, - }); - if (notification.senderType == "Team") { - if (user.team) { - throw new Error("User already on team"); - } - if (!notification.sender) { - throw new Error("Team no longer exists"); - } - if (notification.sender.members.length >= 4) { - throw new Error("Team is full"); - } - let team = await Team.findByIdAndUpdate(notification.sender._id, { - $push: { - members: user, - }, - }).populate("members"); - await User.findByIdAndUpdate(context._id, { - team: notification.sender._id, - }); - let teamSlackIDs: any = []; - team!.members.forEach((member) => { - teamSlackIDs.push(member.slackid); - }); - var index = teamSlackIDs.indexOf(user.slackid); - if (index != -1) { - teamSlackIDs.splice(index, 1); - } - teamSlackIDs.forEach((id) => { - sendSlackMessage( - `${context.name} has accepted your request. View your team here: https://teamformation.hack.gt/feed`, - id - ); - }); - console.log("slack message sent"); - return team; - } else if (notification.senderType == "User") { - let user1 = await User.findById(context._id); - let user2 = await User.findById(notification.sender); - console.log(user1 + " " + user2); - if (user1 && user2) { - if (user1.team) { - throw new Error("User already on a team!"); - } - if (user2.team) { - throw new Error("Requesting user already on team"); - } - var team = new Team({ - name: "Team " + user1._id + "" + user2._id, - members: [user1, user2], - public: true, - }); - return await team.save(function (err, team) { - if (err) { - console.log(err); - } - if (!user1) { - throw new Error("User1 not defined"); - } - if (!user2) { - throw new Error("User2 not defined"); - } - user1.team = team; - user2.team = team; - user1.save(function (err) { - if (err) { - console.log(err); - throw new Error(err); - } - }); - user2.save(function (err) { - if (err) { - console.log(err); - throw new Error(err); - } - }); - sendSlackMessage( - `${context.name} has accepted your request. View your team here: https://teamformation.hack.gt/feed`, - user2.slackid - ); - console.log("slack message sent"); - }); - } else { - throw new Error("User not defined"); - } - } else { - throw new Error("Notification invalid"); - } -}; - -let makeUserRequest = async function (parent, args, context, info, req) { - if (!context._id) { - throw new Error("User not logged in"); - } - let notification; - let receiver_id = args.user_id; - let receiver = await User.findById(receiver_id); - let senderName = context.name; - if (!receiver) { - throw new Error("Receiver user not found"); - } - if (receiver.team) { - throw new Error("Requested user already on team"); - } - if (!context.team) { - //if user is not on a team - notification = new Notification({ - bio: args.bio, - idea: args.idea, - senderType: "User", - receiverType: "User", - sender: context._id, - receiver: receiver_id, - resolved: false, - }); - } else { - let team = await Team.findById(context.team); - if (!team) { - throw new Error("Team not found"); - } - notification = new Notification({ - bio: args.bio, - idea: args.idea, - senderType: "Team", - receiverType: "User", - sender: team._id, - receiver: receiver_id, - resolved: false, - }); - senderName = team.name; - } - - return await notification.save((err, notif) => { - if (err) { - throw new Error("Error creating notification: " + notif); - } - User.findByIdAndUpdate(receiver_id, { - $push: { - notifications: notif, - }, - }); - sendSlackMessage( - `You have received a request from ${senderName}. Accept or deny the request here: https://teamformation.hack.gt/feed`, - receiver!.slackid - ); - if (!context.team) { - sendSlackMessage( - `You have sent a request to ${receiver!.name}`, - context.slackid - ); - } - console.log("slack message sent"); - }); -}; - -let makeTeamRequest = async function (parent, args, context, info, req) { - if (!context._id) { - throw new Error("User not logged in"); - } - let user = await User.findById(context._id); - let team_id = args.team_id; - - let team = await Team.findById(team_id).populate('members'); - - console.log("TEAM_ID: ", team_id); - if (!user) { - throw new Error("User not found!"); - } - if (user.team) { - throw new Error("You are already on a team!"); - } - let notification = new Notification({ - bio: args.bio, - idea: args.idea, - senderType: "User", - receiverType: "Team", - sender: user._id, - receiver: team_id, - resolved: false, - }); - - return await notification.save((err, notif) => { - if (err) { - throw new Error("Error creating notification: " + notif); - } - Team.findByIdAndUpdate(team_id, { - $push: { - notifications: notif, - }, - }); - let teamSlackIDs: any = []; - team!.members.forEach((member) => { - console.log(member.slackid) - teamSlackIDs.push(member.slackid); - console.log(member.slackid); - }); - teamSlackIDs.forEach((id) => { - sendSlackMessage( - `You have received a request from ${context.name}. Accept or deny the request here: https://teamformation.hack.gt/team/${team_id}`, - id - ); - }); - sendSlackMessage(`You have sent a request to ${team!.name}`, user!.slackid); - }); -}; - -let getUserNotifications = async function (parent, args, context, info, req) { - if (!context._id) { - throw new Error("User not logged in"); - } - return await Notification.find({ - receiverType: "User", - receiver: context._id, - resolved: false, - }).populate("sender"); -}; - -let getTeamNotifications = async function (parent, args, context, info, req) { - if (!context._id) { - throw new Error("User not logged in"); - } - if (!context.team) { - throw new Error("User not on a team"); - } - return await Notification.find({ - receiverType: "Team", - receiver: context.team, - resolved: false, - }) - .populate("sender") - .populate("receiver"); -}; - -// let createTeam = async function(args) { -// -// } - -let toggleVisibility = async function (parent, args, context, info, req) { - return User.findByIdAndUpdate( - context._id, - { $bit: { visible: { xor: 1 } } }, - { new: true } - ); -}; - -let apiRouter = express.Router(); - -const resolvers = { - Source: { - __resolveType(obj, context, info) { - if (context.team) { - return "User"; - } - if (context._id) { - return "Team"; - } - return null; - }, - }, - Query: { - users: getUsers, - user_profile: getUserProfile, - teams: getTeams, - team: getTeam, - notifications: getUserNotifications, - team_notifications: getTeamNotifications, - sent_team_notifications: getSentTeamNotifications, - user: getUser, - }, - Mutation: { - toggle_visibility: toggleVisibility, - update_user: updateUser, - update_team: updateTeam, - accept_user_request: acceptUserRequest, - accept_team_request: acceptTeamRequest, - make_team_request: makeTeamRequest, - make_user_request: makeUserRequest, - leave_team: leaveTeam, - }, -}; - -apiRouter.use("/user", userRoutes); - -app.use("/api", apiRouter); - -const server = new ApolloServer({ - typeDefs, - resolvers, - context: ({ req }) => { - return req.user; - }, - playground: { - settings: { - "editor.theme": "dark", - "request.credentials": "include", - }, - }, -}); - -server.applyMiddleware({ app }); -// app.use('/graphql', bodyParser.json(), graphqlExpress({ schema } as GraphQLServerOptions)); -// -// app.use('/graphiql', graphiqlExpress({ endpointURL: '/graphql' })); - -app.use(express.static(path.join(__dirname, "../../client/build"))); -app.get("*", (request, response) => { - response.sendFile(path.join(__dirname, "../../client/build", "index.html")); -}); - -app.listen(PORT, () => { - console.log( - `Team Formation system v${VERSION_NUMBER} started on port ${PORT}` - ); -}); +import fs from "fs"; +import path from "path"; +import express from "express"; +import compression from "compression"; +import morgan from "morgan"; +import passport from "passport"; +import session from "express-session"; +import mongoose from "mongoose"; +import cors from "cors"; +import dotenv from "dotenv"; +const { ApolloServer } = require("apollo-server-express"); +import { GroundTruthStrategy } from "./routes/strategies"; +import { userRoutes } from "./routes/user"; +import schemaDirectives from "./directives"; +import resolvers from "./resolvers"; +import typeDefs from "./typeDefs"; + +dotenv.config(); + +const PORT = 3000; +const VERSION_NUMBER = JSON.parse( + fs.readFileSync(path.resolve(__dirname, "../package.json"), "utf8") +).version; + +import User from "./models/user"; +import { IUser } from "./types/user"; + +export let app = express(); +app.use(morgan("dev")); +app.use(compression()); +app.use(cors()); +const session_secret = process.env["SECRET"]; +if (!session_secret) { + throw new Error("Secret not specified"); +} +app.use( + session({ + secret: session_secret, + saveUninitialized: false, + resave: true, + }) +); + +app.use(passport.initialize()); +app.use(passport.session()); + +export function loggedInErr(req, res, next) { + if (req.user) { + res.status(200).json({ + success: true, + }); + next(); + } else { + res.status(401).json({ error: "User not logged in", success: false }); + return; + } +} + +const gturl = String(process.env.GROUNDTRUTHURL || "login.hack.gt"); +const groundTruthStrategy = new GroundTruthStrategy(gturl); +passport.use(groundTruthStrategy); +passport.serializeUser((user, done) => { + done(null, user.uuid); +}); +passport.deserializeUser((id, done) => { + User.findOne({ uuid: id }, (err, user) => { + done(err, user!); + }); +}); + +let apiRouter = express.Router(); +apiRouter.use("/user", userRoutes); + +app.use("/api", apiRouter); + +const server = new ApolloServer({ + typeDefs, + resolvers, + schemaDirectives, + context: ({ req }) => { + return req.user; + }, + playground: { + settings: { + "editor.theme": "dark", + "request.credentials": "include", + }, + }, +}); + +server.applyMiddleware({ app }); + +app.use(express.static(path.join(__dirname, "../../client/build"))); +app.get("*", (request, response) => { + response.sendFile(path.join(__dirname, "../../client/build", "index.html")); +}); + +const MONGO_URL = String(process.env.MONGO_URL); + +(async () => { + await mongoose + .connect(MONGO_URL, { + useMongoClient: false, + }) + .catch((err) => { + throw err; + }); +})(); + +app.listen(PORT, () => { + console.log( + `Team Formation system v${VERSION_NUMBER} started on port ${PORT}` + ); +}); diff --git a/server/src/app2.ts b/server/src/app2.ts new file mode 100644 index 00000000..628281f4 --- /dev/null +++ b/server/src/app2.ts @@ -0,0 +1,818 @@ +import fs from "fs"; +import path from "path"; +import express from "express"; +import compression from "compression"; +import morgan from "morgan"; +import passport from "passport"; +import session from "express-session"; +import mongoose from "mongoose"; +import cors from "cors"; +import dotenv from "dotenv"; +const { ApolloServer, gql } = require("apollo-server-express"); +import { GroundTruthStrategy } from "./routes/strategies"; +import { IUser, User, Notification, Team } from "./schema"; +import { userRoutes } from "./routes/user"; +import sendSlackMessage from "./sendSlackMessage"; + +dotenv.config(); + +const PORT = 3000; +const typeDefs = gql` + ${fs.readFileSync(path.resolve(__dirname, "../api.graphql"), "utf8")} +`; +const VERSION_NUMBER = JSON.parse( + fs.readFileSync(path.resolve(__dirname, "../package.json"), "utf8") +).version; +//const VERSION_HASH = require("git-rev-sync").short(); + +export let app = express(); + +if (process.env.ISPRODUCTION === "true") { + app.enable("trust proxy"); +} else { + console.warn("OAuth callback(s) running in development mode"); +} + +app.use(morgan("dev")); +app.use(compression()); +app.use(cors()); +const session_secret = process.env["SECRET"]; +if (!session_secret) { + throw new Error("Secret not specified"); +} +app.use( + session({ + secret: session_secret, + saveUninitialized: false, + resave: true, + }) +); + +app.use(passport.initialize()); +app.use(passport.session()); + +export function loggedInErr(req, res, next) { + if (req.user) { + res.status(200).json({ + success: true, + }); + next(); + } else { + res.status(401).json({ error: "User not logged in", success: false }); + return; + } +} + +const gturl = String(process.env.GROUNDTRUTHURL || "login.hack.gt"); +const groundTruthStrategy = new GroundTruthStrategy(gturl); +passport.use(groundTruthStrategy); +passport.serializeUser((user, done) => { + done(null, user.uuid); +}); +passport.deserializeUser((id, done) => { + User.findOne({ uuid: id }, (err, user) => { + done(err, user!); + }); +}); + +let getTeam = async function (parent, args, context, info, req) { + if (!context._id) { + throw new Error("User is not logged in!"); + } + let team = await Team.findById(args.team_id).populate("members"); + if (!team) { + throw new Error("Team not found!"); + } + return team; +}; +let getTeams = async function (parent, args, context, info, req) { + console.log("getting teams.."); + let teams; + let search; + let interests; + let interestSearch = [] as {}[]; + + if (args.search) { + search = '"' + args.search.split(" ").join('" "') + '"'; + } + + if (args.interests) { + interests = args.interests.match(/\w+/g); + for (let i = 0; i < interests.length; i++) { + interestSearch.push({ + interests: { + $elemMatch: { $regex: ".*" + interests[i] + ".*", $options: "i" }, + }, + }); + } + } + + if (search && interests) { + console.log("search and interest filters applied"); + teams = await Team.find({ + $and: [ + { public: true }, + { $text: { $search: search } }, + { $and: interestSearch }, + ], + }).populate("members"); + } else if (search) { + console.log(`searching for ${search}..`); + teams = await Team.find({ + $and: [{ public: true }, { $text: { $search: search } }], + }).populate("members"); + } else if (interests) { + console.log(`interest filter(s) applied: ${interestSearch}`); + teams = await Team.find({ + $and: [{ public: true }, { $and: interestSearch }], + }).populate("members"); + } else { + console.log("no filter applied"); + teams = await Team.find({ + public: true, + }).populate("members"); + } + + if (!teams) { + return null; + } + + teams.sort(function (a, b) { + return a.name.toLowerCase() - b.name.toLowerCase(); + }); + return teams; +}; + +let getUser = async function (parent, args, context, info, req) { + if (!context._id) { + throw new Error("User not logged in"); + } + let user = await User.findById(args.user_id); + if (!user) { + throw new Error("User not found"); + } + return user; +}; + +let getUsers = async function (parent, args, context, info, req) { + if (!context._id) { + throw new Error("User not logged in"); + } + console.log("getting users.."); + let users; + let search; + let skills; + let grad_years; + let schools; + let skillSearch = [] as {}[]; + let yearSearch = [] as {}[]; + let schoolSearch = [] as {}[]; + + if (args.search) { + search = '"' + args.search.split(" ").join('" "') + '"'; + } + if (args.skill) { + skills = args.skill.match(/\w+/g); + for (let i = 0; i < skills.length; i++) { + skillSearch.push({ + skills: { + $elemMatch: { $regex: ".*" + skills[i] + ".*", $options: "i" }, + }, + }); + } + } + if (args.grad_year) { + grad_years = args.grad_year.match(/\w+/g); + for (let i = 0; i < grad_years.length; i++) { + yearSearch.push({ grad_year: grad_years[i] }); + } + } + if (args.school) { + schools = args.school.split(","); + for (let i = 0; i < schools.length; i++) { + schoolSearch.push({ school: schools[i] }); + } + } + + if (search && skills && grad_years && schools) { + console.log("all filters applied..."); + users = await User.find({ + $and: [ + { $text: { $search: search } }, + { $and: skillSearch }, + { $or: yearSearch }, + { $or: schoolSearch }, + ], + visible: 1 + }); + } else if (search && skills && grad_years) { + console.log("search, skills, and grad year filters applied..."); + users = await User.find({ + $and: [ + { $text: { $search: search } }, + { $and: skillSearch }, + { $or: yearSearch }, + ], + visible: 1 + }); + } else if (search && skills && schools) { + console.log("search, skills, and schools filters applied..."); + users = await User.find({ + $and: [ + { $text: { $search: search } }, + { $and: skillSearch }, + { $or: schoolSearch }, + ], + visible: 1 + }); + } else if (search && grad_years && schools) { + console.log("searc, grad year, and schools filters applied..."); + users = await User.find({ + $and: [ + { $text: { $search: search } }, + { $or: yearSearch }, + { $or: schoolSearch }, + ], + visible: 1 + }); + } else if (skills && grad_years && schools) { + console.log("skills, grad year, and schools filter applied..."); + users = await User.find({ + $and: [{ $and: skillSearch }, { $or: yearSearch }, { $or: schoolSearch }],visible: 1 + } + ); + } else if (search && skills) { + console.log("search and skills filters applied..."); + users = await User.find({ + $and: [{ $text: { $search: search } }, { $and: skillSearch }], visible: 1 + }); + } else if (search && grad_years) { + console.log("search and grad year filters applied..."); + users = await User.find({ + $and: [{ $text: { $search: search } }, { $or: yearSearch }], visible: 1 + }); + } else if (search && schools) { + console.log("search and schools filters applied..."); + users = await User.find({ + $and: [{ $text: { $search: search } }, { $or: schoolSearch }], visible: 1 + }); + } else if (skills && grad_years) { + console.log("skill and year filters applied"); + users = await User.find({ + $and: [{ $and: skillSearch }, { $or: yearSearch }], visible: 1 + }); + } else if (skills && schools) { + console.log("only skill and school filters applied"); + users = await User.find({ + $and: [{ $and: skillSearch }, { $or: schoolSearch }],visible: 1 + }); + } else if (grad_years && schools) { + console.log("only year and school filters applied"); + users = await User.find({ + $and: [{ $or: yearSearch }, { $or: schoolSearch }], visible: 1 + }); + } else if (search) { + console.log(`searching for ${search}..`); + users = await User.find({ $text: { $search: search }, visible: 1 }); + console.log(users); + } else if (skills) { + console.log("only skill filter(s) applied"); + users = await User.find({ + $and: skillSearch, visible: 1 + }); + } else if (grad_years) { + console.log("only year filter(s) applied"); + users = await User.find({ + $or: yearSearch, visible: 1 + }); + } else if (schools) { + console.log("only school filter(s) applied"); + users = await User.find({ + $or: schoolSearch, visible: 1 + }); + } else { + console.log("no filters applied"); + users = await User.find({visible: 1}); + } + + if (!users) { + return null; + } + + users = users + .sort(function (a, b) { + return a.name.toLowerCase() - b.name.toLowerCase(); + }) + .filter((item) => { + return item.uuid != context.uuid && !item.team && item.visible == 1; + }); + return users; +}; + +let updateUser = async function (parent, args, context, info, req) { + console.log(context._id); + if (!context._id) { + throw new Error("User not logged in"); + } + return await User.findByIdAndUpdate( + context._id, + { $set: args }, + { new: true } + ); +}; + +let updateTeam = async function (parent, args, context, info, req) { + console.log(context._id); + if (!context._id) { + throw new Error("User not logged in"); + } + let user = await User.findById(context._id).populate("team"); + if (!user) { + throw new Error("User not found"); + } + if (!user.team) { + throw new Error("User not on team"); + } + return await Team.findByIdAndUpdate(user.team, { $set: args }, { new: true }); +}; + +let leaveTeam = async function (parent, args, context, info, req) { + console.log("leaveing team"); + + if (!context._id) { + throw new Error("User not logged in"); + } + if (!context.team) { + throw new Error("User not on team"); + } + + await Team.findByIdAndUpdate( + context.team, + { + $pull: { + members: context._id, + }, + }, + async (err, team) => { + if (err || !team) { + console.log(err); + throw new Error("Issue updating team"); + } + console.log("TTTEAM", team); + if (team.members.length <= 1) { + await Team.findByIdAndDelete(context.team); + } + } + ); + + return await User.findByIdAndUpdate(context._id, { + team: null, + }); +}; + +let getSentTeamNotifications = async function ( + parent, + args, + context, + info, + req +) { + if (!context._id) { + throw new Error("User not logged in"); + } + let user = await User.findById(context._id); + if (!user) { + throw new Error("User not found"); + } + if (!user.team) { + throw new Error("User not on teams"); + } + return await Notification.find({ + sender: user.team, + resolved: false, + }).populate("receiver"); +}; + +let getUserProfile = async function (parent, args, context, info, req) { + return await User.findById(context._id).populate("team"); +}; + +/* + Accepting a user sending request to team + */ +let acceptTeamRequest = async function (parent, args, context, info, req) { + if (!context._id) { + throw new Error("User not logged in"); + } + let user = await User.findById(context._id).populate("team"); + if (!user) { + throw new Error("User not found"); + } + if (!user.team) { + throw new Error("User not on team"); + } + if (user.team.members.length >= 4) { + throw new Error("Team is full"); + } + let notification = await Notification.findById(args.notification_id) + .populate("sender") + .populate("receiver"); + if (!notification) { + throw new Error("Notification does not exist"); + } + if (notification.senderType == "Team") { + throw new Error("Cannot accept request from a team"); + } + if (notification.senderType == "User") { + console.log( + "TEAM", + user.team._id.toString(), + notification.receiver._id.toString() + ); + + if (user.team._id.toString() !== notification.receiver._id.toString()) { + throw new Error("Team does not own notification"); + } + let requestUser = await User.findById(notification.sender._id); + if (!requestUser) { + throw new Error("Notificaton sender not found"); + } + if (requestUser.team) { + throw new Error("Sender already on team"); + } + console.log("here2"); + + let requestedUserId = requestUser._id; + console.log(requestedUserId); + console.log(user.team._id); + console.log(mongoose.Types.ObjectId.isValid(requestedUserId)); + console.log(mongoose.Types.ObjectId.isValid(user.team._id)); + + await Team.findByIdAndUpdate( + user.team._id, + { + $push: { + members: requestedUserId, + }, + }, + function (err, user) { + console.log("here"); + console.log(err); + } + ); + await User.findByIdAndUpdate(notification.sender._id, { + team: user.team._id, + }); + await Notification.updateMany({ sender: notification.sender._id, receiver: notification.receiver._id }, { + resolved: true, + }); + + sendSlackMessage( + `${ + user!.team!.name + } has accepted your request. View your new team here: https://teamformation.hack.gt/team/${user.team._id}`, + requestUser.slackid + ); + console.log("slack message sent"); + } else { + throw new Error("Notification invalid"); + } +}; + +let acceptUserRequest = async function (parent, args, context, info, req) { + if (!context._id) { + throw new Error("User not logged in"); + } + let user = await User.findById(context._id); + if (!user) { + throw new Error("User not found"); + } + + let notification = await Notification.findById(args.notification_id) + .populate("sender") + .populate("receiver"); + if (!notification) { + throw new Error("Notification not found"); + } + console.log("ACCEPT USER REQUEST"); + console.log(notification.receiver._id); + console.log(user._id); + + if (notification.receiver._id.toString() != user._id.toString()) { + throw new Error("Notification not valid"); + } + await Notification.findByIdAndUpdate(notification._id, { + resolved: true, + }); + if (notification.senderType == "Team") { + if (user.team) { + throw new Error("User already on team"); + } + if (!notification.sender) { + throw new Error("Team no longer exists"); + } + if (notification.sender.members.length >= 4) { + throw new Error("Team is full"); + } + let team = await Team.findByIdAndUpdate(notification.sender._id, { + $push: { + members: user, + }, + }).populate("members"); + await User.findByIdAndUpdate(context._id, { + team: notification.sender._id, + }); + let teamSlackIDs: any = []; + team!.members.forEach((member) => { + teamSlackIDs.push(member.slackid); + }); + var index = teamSlackIDs.indexOf(user.slackid); + if (index != -1) { + teamSlackIDs.splice(index, 1); + } + teamSlackIDs.forEach((id) => { + sendSlackMessage( + `${context.name} has accepted your request. View your team here: https://teamformation.hack.gt/feed`, + id + ); + }); + console.log("slack message sent"); + return team; + } else if (notification.senderType == "User") { + let user1 = await User.findById(context._id); + let user2 = await User.findById(notification.sender); + console.log(user1 + " " + user2); + if (user1 && user2) { + if (user1.team) { + throw new Error("User already on a team!"); + } + if (user2.team) { + throw new Error("Requesting user already on team"); + } + var team = new Team({ + name: "Team " + user1._id + "" + user2._id, + members: [user1, user2], + public: true, + }); + return await team.save(function (err, team) { + if (err) { + console.log(err); + } + if (!user1) { + throw new Error("User1 not defined"); + } + if (!user2) { + throw new Error("User2 not defined"); + } + user1.team = team; + user2.team = team; + user1.save(function (err) { + if (err) { + console.log(err); + throw new Error(err); + } + }); + user2.save(function (err) { + if (err) { + console.log(err); + throw new Error(err); + } + }); + sendSlackMessage( + `${context.name} has accepted your request. View your team here: https://teamformation.hack.gt/feed`, + user2.slackid + ); + console.log("slack message sent"); + }); + } else { + throw new Error("User not defined"); + } + } else { + throw new Error("Notification invalid"); + } +}; + +let makeUserRequest = async function (parent, args, context, info, req) { + if (!context._id) { + throw new Error("User not logged in"); + } + let notification; + let receiver_id = args.user_id; + let receiver = await User.findById(receiver_id); + let senderName = context.name; + if (!receiver) { + throw new Error("Receiver user not found"); + } + if (receiver.team) { + throw new Error("Requested user already on team"); + } + if (!context.team) { + //if user is not on a team + notification = new Notification({ + bio: args.bio, + idea: args.idea, + senderType: "User", + receiverType: "User", + sender: context._id, + receiver: receiver_id, + resolved: false, + }); + } else { + let team = await Team.findById(context.team); + if (!team) { + throw new Error("Team not found"); + } + notification = new Notification({ + bio: args.bio, + idea: args.idea, + senderType: "Team", + receiverType: "User", + sender: team._id, + receiver: receiver_id, + resolved: false, + }); + senderName = team.name; + } + + return await notification.save((err, notif) => { + if (err) { + throw new Error("Error creating notification: " + notif); + } + User.findByIdAndUpdate(receiver_id, { + $push: { + notifications: notif, + }, + }); + sendSlackMessage( + `You have received a request from ${senderName}. Accept or deny the request here: https://teamformation.hack.gt/feed`, + receiver!.slackid + ); + if (!context.team) { + sendSlackMessage( + `You have sent a request to ${receiver!.name}`, + context.slackid + ); + } + console.log("slack message sent"); + }); +}; + +let makeTeamRequest = async function (parent, args, context, info, req) { + if (!context._id) { + throw new Error("User not logged in"); + } + let user = await User.findById(context._id); + let team_id = args.team_id; + + let team = await Team.findById(team_id).populate('members'); + + console.log("TEAM_ID: ", team_id); + if (!user) { + throw new Error("User not found!"); + } + if (user.team) { + throw new Error("You are already on a team!"); + } + let notification = new Notification({ + bio: args.bio, + idea: args.idea, + senderType: "User", + receiverType: "Team", + sender: user._id, + receiver: team_id, + resolved: false, + }); + + return await notification.save((err, notif) => { + if (err) { + throw new Error("Error creating notification: " + notif); + } + Team.findByIdAndUpdate(team_id, { + $push: { + notifications: notif, + }, + }); + let teamSlackIDs: any = []; + team!.members.forEach((member) => { + console.log(member.slackid) + teamSlackIDs.push(member.slackid); + console.log(member.slackid); + }); + teamSlackIDs.forEach((id) => { + sendSlackMessage( + `You have received a request from ${context.name}. Accept or deny the request here: https://teamformation.hack.gt/team/${team_id}`, + id + ); + }); + sendSlackMessage(`You have sent a request to ${team!.name}`, user!.slackid); + }); +}; + +let getUserNotifications = async function (parent, args, context, info, req) { + if (!context._id) { + throw new Error("User not logged in"); + } + return await Notification.find({ + receiverType: "User", + receiver: context._id, + resolved: false, + }).populate("sender"); +}; + +let getTeamNotifications = async function (parent, args, context, info, req) { + if (!context._id) { + throw new Error("User not logged in"); + } + if (!context.team) { + throw new Error("User not on a team"); + } + return await Notification.find({ + receiverType: "Team", + receiver: context.team, + resolved: false, + }) + .populate("sender") + .populate("receiver"); +}; + +// let createTeam = async function(args) { +// +// } + +let toggleVisibility = async function (parent, args, context, info, req) { + return User.findByIdAndUpdate( + context._id, + { $bit: { visible: { xor: 1 } } }, + { new: true } + ); +}; + +let apiRouter = express.Router(); + +const resolvers = { + Source: { + __resolveType(obj, context, info) { + if (context.team) { + return "User"; + } + if (context._id) { + return "Team"; + } + return null; + }, + }, + Query: { + users: getUsers, + user_profile: getUserProfile, + teams: getTeams, + team: getTeam, + notifications: getUserNotifications, + team_notifications: getTeamNotifications, + sent_team_notifications: getSentTeamNotifications, + user: getUser, + }, + Mutation: { + toggle_visibility: toggleVisibility, + update_user: updateUser, + update_team: updateTeam, + accept_user_request: acceptUserRequest, + accept_team_request: acceptTeamRequest, + make_team_request: makeTeamRequest, + make_user_request: makeUserRequest, + leave_team: leaveTeam, + }, +}; + +apiRouter.use("/user", userRoutes); + +app.use("/api", apiRouter); + +const server = new ApolloServer({ + typeDefs, + resolvers, + context: ({ req }) => { + return req.user; + }, + playground: { + settings: { + "editor.theme": "dark", + "request.credentials": "include", + }, + }, +}); + +server.applyMiddleware({ app }); +// app.use('/graphql', bodyParser.json(), graphqlExpress({ schema } as GraphQLServerOptions)); +// +// app.use('/graphiql', graphiqlExpress({ endpointURL: '/graphql' })); + +app.use(express.static(path.join(__dirname, "../../client/build"))); +app.get("*", (request, response) => { + response.sendFile(path.join(__dirname, "../../client/build", "index.html")); +}); + +app.listen(PORT, () => { + console.log( + `Team Formation system v${VERSION_NUMBER} started on port ${PORT}` + ); +}); diff --git a/server/src/auth.ts b/server/src/auth.ts new file mode 100644 index 00000000..d33f0065 --- /dev/null +++ b/server/src/auth.ts @@ -0,0 +1,7 @@ +const { AuthenticationError } = require("apollo-server-express"); + +export const ensureSignedIn = (context): void => { + if (!context._id) { // check if user's id exists + throw new AuthenticationError("You must be signed in."); + } +}; diff --git a/server/src/directives/auth.ts b/server/src/directives/auth.ts new file mode 100644 index 00000000..b5dc2acf --- /dev/null +++ b/server/src/directives/auth.ts @@ -0,0 +1,20 @@ +// import { SchemaDirectiveVisitor } from 'apollo-server-express' +const { SchemaDirectiveVisitor } = require("apollo-server-express"); +import { GraphQLField, defaultFieldResolver } from "graphql"; +import { ensureSignedIn } from "../auth"; + +class AuthDirective extends SchemaDirectiveVisitor { + public visitFieldDefinition(field: GraphQLField) { + const { resolve = defaultFieldResolver } = field; + + field.resolve = function (...args) { + const [, , context] = args; + + ensureSignedIn(context); + + return resolve.apply(this, args); + }; + } +} + +export default AuthDirective; diff --git a/server/src/directives/index.ts b/server/src/directives/index.ts new file mode 100644 index 00000000..0d9e083d --- /dev/null +++ b/server/src/directives/index.ts @@ -0,0 +1,5 @@ +import AuthDirective from './auth'; + +export default { + auth: AuthDirective +} \ No newline at end of file diff --git a/server/src/models/index.ts b/server/src/models/index.ts new file mode 100644 index 00000000..ab36c5f3 --- /dev/null +++ b/server/src/models/index.ts @@ -0,0 +1,6 @@ + +export { default as User } from './user' + +export { default as Team } from './team' + +export { default as Not } from './notification' \ No newline at end of file diff --git a/server/src/models/notification.ts b/server/src/models/notification.ts new file mode 100644 index 00000000..02c0f267 --- /dev/null +++ b/server/src/models/notification.ts @@ -0,0 +1,32 @@ +import { model, Schema } from 'mongoose' +import { INotificationMongoose } from '../types' + +const Notification = model("Notification", new Schema({ + message: String, + bio: String, + idea: String, + senderType: { + type: String, + required: true, + enum: ['Team', 'User'] + }, + receiverType: { + type: String, + required: true, + enum: ['Team', 'User'] + }, + sender: { + type: Schema.Types.ObjectId, + required: true, + refPath: 'senderType' + }, + receiver: { + type: Schema.Types.ObjectId, + required: true, + refPath: 'receiverType' + }, + resolved: Boolean +})); + +export default Notification + diff --git a/server/src/models/team.ts b/server/src/models/team.ts new file mode 100644 index 00000000..8415cc96 --- /dev/null +++ b/server/src/models/team.ts @@ -0,0 +1,43 @@ +import { model, Schema } from 'mongoose' +import { ITeamMongoose } from '../types' + +const TeamSchema = new Schema({ + name: { + required: true, + type: String, + unique: true + }, + picture: String, + members: { + required: true, + type: [{ + type: Schema.Types.ObjectId, + ref: "User" + }] + }, + interests: [String], + description: String, + project_idea: String, + notifications: { + type: [{ + type: Schema.Types.ObjectId, + ref: "Notification" + }] + }, + public: Boolean + }, + { + usePushEach: true + } +); + +TeamSchema.index({ + name: 'text', + members: 'text', + interests: 'text', + description: 'text' +}) + +const Team = model("Team", TeamSchema); + +export default Team \ No newline at end of file diff --git a/server/src/models/user.ts b/server/src/models/user.ts new file mode 100644 index 00000000..50bce7a7 --- /dev/null +++ b/server/src/models/user.ts @@ -0,0 +1,55 @@ +import { model, Schema } from 'mongoose' +import { IUserMongoose } from '../types' + +const UserSchema = new Schema({ + uuid: { + type: String, + required: true, + index: true, + unique: true + }, + email: { + type: String, + required: true, + unique: true + }, + name: { + type: String, + required: false + }, + school: { + type: String, + required: false + }, + token: String, + grad_year: String, + skills: [String], + beginner: { + type: Boolean, + required: false + }, + experience: String, + image: String, + auth_keys: [String], + admin: Boolean, + contact: String, + contact_method: String, + visible: Number, + team: { + type: Schema.Types.ObjectId, + ref: "Team" + }, + slackid: String +}); + +UserSchema.index({ + name: 'text', + school: 'text', + grad_year: 'text', + skills: 'text', + experience: 'text' +}); + +const User = model("User", UserSchema); + +export default User \ No newline at end of file diff --git a/server/src/resolvers/index.ts b/server/src/resolvers/index.ts new file mode 100644 index 00000000..b500193c --- /dev/null +++ b/server/src/resolvers/index.ts @@ -0,0 +1,5 @@ +import user from './user'; +import team from './team'; +import source from './source' + +export default [source, user, team] \ No newline at end of file diff --git a/server/src/resolvers/source/index.ts b/server/src/resolvers/source/index.ts new file mode 100644 index 00000000..978cb8ee --- /dev/null +++ b/server/src/resolvers/source/index.ts @@ -0,0 +1,15 @@ +const resolvers = { + Source: { + __resolveType(obj, context, info) { + if (context.team) { + return "User"; + } + if (context._id) { + return "Team"; + } + return null; + }, + }, +}; + +export default resolvers; diff --git a/server/src/resolvers/team/index.ts b/server/src/resolvers/team/index.ts new file mode 100644 index 00000000..d3e20037 --- /dev/null +++ b/server/src/resolvers/team/index.ts @@ -0,0 +1,14 @@ +import { updateTeam } from './mutations' +import { getTeam, getTeams } from './queries'; + +const resolvers = { + Query: { + teams: getTeams, + team: getTeam + }, + Mutation: { + update_team: updateTeam + } +} + +export default resolvers \ No newline at end of file diff --git a/server/src/resolvers/team/mutations/index.ts b/server/src/resolvers/team/mutations/index.ts new file mode 100644 index 00000000..9d9df5db --- /dev/null +++ b/server/src/resolvers/team/mutations/index.ts @@ -0,0 +1 @@ +export { default as updateTeam } from './updateTeam' diff --git a/server/src/resolvers/team/mutations/updateTeam.ts b/server/src/resolvers/team/mutations/updateTeam.ts new file mode 100644 index 00000000..df44ee8f --- /dev/null +++ b/server/src/resolvers/team/mutations/updateTeam.ts @@ -0,0 +1,21 @@ +import { Team, User } from "../../../models"; +import { IUserMongoose, ITeamMongoose } from "../../../types"; + +const updateTeam = async function ( + parent, + args, + context, + info, + req +): Promise { + let user: IUserMongoose | null = await User.findById(context._id).populate("team"); + if(!user) { + throw new Error("User not found"); + } + if (!user.team) { + throw new Error("User not on team"); + } + return await Team.findByIdAndUpdate(user.team, { $set: args }, { new: true }); +}; // consider taking in team-id instead of automatically using logged in user's team id + +export default updateTeam; diff --git a/server/src/resolvers/team/queries/getTeam.ts b/server/src/resolvers/team/queries/getTeam.ts new file mode 100644 index 00000000..6b6d5731 --- /dev/null +++ b/server/src/resolvers/team/queries/getTeam.ts @@ -0,0 +1,21 @@ +import { Team } from "../../../models"; +import { GraphQLError } from "graphql"; +import { ITeamMongoose } from "../../../types"; + +const getTeam = async function ( + parent, + args, + context, + info, + req +): Promise { + let team: ITeamMongoose | null = await Team.findById(args.team_id).populate( + "members" + ); + if (!team) { + throw new GraphQLError("Team not found!"); + } + return team; +}; + +export default getTeam; diff --git a/server/src/resolvers/team/queries/getTeams.ts b/server/src/resolvers/team/queries/getTeams.ts new file mode 100644 index 00000000..adea64bf --- /dev/null +++ b/server/src/resolvers/team/queries/getTeams.ts @@ -0,0 +1,44 @@ +import { Team } from "../../../models"; +import { ITeamMongoose } from "../../../types"; + +const getTeams = async function ( + parent, + args, + context, + info, + req +): Promise { + let teams: ITeamMongoose[]; + let search: String; + let interests: String[]; + let interestSearch = [] as {}[]; + let andQuery = [] as {}[]; + if (args.search) { + search = '"' + args.search.split(" ").join('" "') + '"'; + andQuery.push({ $text: { $search: search } }); + } + + if (args.interests) { + interests = args.interests.match(/\w+/g); + interestSearch = interests.map((interest) => { + return { + interests: { + $elemMatch: { $regex: ".*" + interest + ".*", $options: "i" }, + }, + }; + }); + andQuery.push({ $and: interestSearch }); + } + + teams = await Team.find({ + $and: [{ public: true }, ...andQuery], + }).populate("members"); + + teams.sort(function (a, b) { + return a.name.toLowerCase() < b.name.toLowerCase() ? 1 : -1; + }); + + return teams; +}; + +export default getTeams; diff --git a/server/src/resolvers/team/queries/index.ts b/server/src/resolvers/team/queries/index.ts new file mode 100644 index 00000000..bdfd7dde --- /dev/null +++ b/server/src/resolvers/team/queries/index.ts @@ -0,0 +1,3 @@ +export { default as getTeam } from './getTeam' + +export { default as getTeams } from './getTeams' diff --git a/server/src/resolvers/user/index.ts b/server/src/resolvers/user/index.ts new file mode 100644 index 00000000..275a0170 --- /dev/null +++ b/server/src/resolvers/user/index.ts @@ -0,0 +1,15 @@ +import { updateUser } from './mutations' +import { getUser, getUserProfile, getUsers } from './queries'; + +const resolvers = { + Query: { + users: getUsers, + user_profile: getUserProfile, + user: getUser, + }, + Mutation: { + update_user: updateUser, + } +} + +export default resolvers \ No newline at end of file diff --git a/server/src/resolvers/user/mutations/index.ts b/server/src/resolvers/user/mutations/index.ts new file mode 100644 index 00000000..de532ea1 --- /dev/null +++ b/server/src/resolvers/user/mutations/index.ts @@ -0,0 +1 @@ +export { default as updateUser } from './updateUser' diff --git a/server/src/resolvers/user/mutations/updateUser.ts b/server/src/resolvers/user/mutations/updateUser.ts new file mode 100644 index 00000000..a749cfed --- /dev/null +++ b/server/src/resolvers/user/mutations/updateUser.ts @@ -0,0 +1,18 @@ +import { User } from "../../../models"; +import { IUserMongoose } from "../../../types"; + +const updateUser = async function ( + parent, + args, + context, + info, + req +): Promise { + return await User.findByIdAndUpdate( + context._id, + { $set: args }, + { new: true } + ); +}; + +export default updateUser; diff --git a/server/src/resolvers/user/queries/getUser.ts b/server/src/resolvers/user/queries/getUser.ts new file mode 100644 index 00000000..14bbf1c0 --- /dev/null +++ b/server/src/resolvers/user/queries/getUser.ts @@ -0,0 +1,19 @@ +import { User } from "../../../models"; +import { IUserMongoose } from "../../../types"; +import { GraphQLError } from 'graphql'; + +const getUser = async function ( + parent, + args, + context, + info, + req +): Promise { + let user = await User.findById(args.user_id); + if (!user) { + throw new GraphQLError("User not found"); + } + return user; +}; + +export default getUser; diff --git a/server/src/resolvers/user/queries/getUserProfile.ts b/server/src/resolvers/user/queries/getUserProfile.ts new file mode 100644 index 00000000..49dc2756 --- /dev/null +++ b/server/src/resolvers/user/queries/getUserProfile.ts @@ -0,0 +1,14 @@ +import { User } from "../../../models"; +import { IUserMongoose } from "../../../types"; + +const getUserProfile = async function ( + parent, + args, + context, + info, + req +): Promise { + return await User.findById(context._id).populate("team"); +}; + +export default getUserProfile; diff --git a/server/src/resolvers/user/queries/getUsers.ts b/server/src/resolvers/user/queries/getUsers.ts new file mode 100644 index 00000000..a563cff4 --- /dev/null +++ b/server/src/resolvers/user/queries/getUsers.ts @@ -0,0 +1,67 @@ +import { IUserMongoose } from "../../../types"; +import { User } from "../../../models" + +const getUsers = async function ( + parent, + args, + context, + info, + req +): Promise { + let users: IUserMongoose[]; + let search: String; + let skills: String[]; + let grad_years: String[]; + let schools: String[]; + let skillSearch = [] as {}[]; + let yearSearch = [] as {}[]; + let schoolSearch = [] as {}[]; + + let andQuery = [] as any; + + if (args.search) { + search = '"' + args.search.split(" ").join('" "') + '"'; + andQuery.push({ $text: { $search: search } }); + } + if (args.skill) { + skills = args.skill.match(/\w+/g); + for (let i = 0; i < skills.length; i++) { + skillSearch.push({ + skills: { + $elemMatch: { $regex: ".*" + skills[i] + ".*", $options: "i" }, + }, + }); + } + andQuery.push({ $and: skillSearch }); + } + if (args.grad_year) { + grad_years = args.grad_year.match(/\w+/g); + for (let i = 0; i < grad_years.length; i++) { + yearSearch.push({ grad_year: grad_years[i] }); + } + andQuery.push({ $or: yearSearch }); + } + if (args.school) { + schools = args.school.split(","); + for (let i = 0; i < schools.length; i++) { + schoolSearch.push({ school: schools[i] }); + } + andQuery.push({ $or: schoolSearch }); + } + + users = await User.find({ + $and: andQuery, + visible: 1, + }); + users = users + .sort(function (a, b) { + return a.name.toLowerCase() < b.name.toLowerCase() ? 1 : -1; + }) + .filter((item) => { + return item.uuid != context.uuid && !item.team && item.visible == 1; + }); + + return users; +}; + +export default getUsers; diff --git a/server/src/resolvers/user/queries/index.ts b/server/src/resolvers/user/queries/index.ts new file mode 100644 index 00000000..a0052d2f --- /dev/null +++ b/server/src/resolvers/user/queries/index.ts @@ -0,0 +1,5 @@ +export { default as getUser } from './getUser' + +export { default as getUsers } from './getUsers' + +export { default as getUserProfile } from './getUserProfile' diff --git a/server/src/routes/strategies.ts b/server/src/routes/strategies.ts index f0582fff..ecc1527d 100644 --- a/server/src/routes/strategies.ts +++ b/server/src/routes/strategies.ts @@ -4,7 +4,9 @@ import { Strategy as OAuthStrategy } from "passport-oauth2"; import dotenv from "dotenv" import request from "request" import { Request } from "express"; -import { createNew, IUser, User } from "../schema"; +import { User } from "../models" +import { createNew, IUser } from "../types"; +import mongoose from 'mongoose'; dotenv.config() @@ -74,11 +76,14 @@ export class GroundTruthStrategy extends OAuthStrategy { } protected static async passportCallback(req: Request, accessToken: string, refreshToken: string, profile: IProfile, done: PassportDone) { + console.log(mongoose.connection.readyState); + console.log(profile) + console.log("hi") let user = await User.findOne({ uuid: profile.uuid }); const GRAPHQLURL = process.env.GRAPHQLURL || 'https://registration.hack.gt/graphql' - + console.log("82 here") if (!user) { let confirmed = false; const query = ` @@ -110,6 +115,7 @@ export class GroundTruthStrategy extends OAuthStrategy { if (JSON.parse(body).data.search_user.users.length > 0) { confirmed = JSON.parse(body).data.search_user.users[0].confirmed; } + console.log("requesting...") if (!process.env.ISPRODUCTION || confirmed) { console.log("here") user = createNew(User, { diff --git a/server/src/routes/user.ts b/server/src/routes/user.ts index aee326a7..498cef2f 100644 --- a/server/src/routes/user.ts +++ b/server/src/routes/user.ts @@ -2,7 +2,8 @@ import express from "express"; import request from "request" import passport from "passport"; import { createLink, AuthenticateOptions } from "./strategies" -import { IUser, User } from "../schema"; +import { User } from "../models"; +import { IUser } from "../types"; export let userRoutes = express.Router(); userRoutes.route("/login").get((req, response, next) => { diff --git a/server/src/typeDefs/index.ts b/server/src/typeDefs/index.ts new file mode 100644 index 00000000..ec711a49 --- /dev/null +++ b/server/src/typeDefs/index.ts @@ -0,0 +1,6 @@ +import user from './user' +import team from './team' +import notification from './notification' +import root from './root' + +export default [root, user, team, notification] \ No newline at end of file diff --git a/server/src/typeDefs/notification.ts b/server/src/typeDefs/notification.ts new file mode 100644 index 00000000..9f741a02 --- /dev/null +++ b/server/src/typeDefs/notification.ts @@ -0,0 +1,29 @@ +const { gql } = require("apollo-server-express"); + +export default gql` + extend type Query { + notifications(receiver_id: String): [Notification!]! @auth + team_notifications: [Notification]! @auth + sent_team_notifications: [Notification]! @auth + } + + extend type Mutation { + accept_team_request(notification_id: String): Team @auth + accept_user_request(notification_id: String): Team @auth + make_team_request(team_id: String, user_id: String, bio: String, idea: String): Notification @auth + make_user_request(user_id: String, bio: String, idea: String): Notification @auth + } + + union Source = User | Team + + type Notification { + id: ID + message: String + bio: String + idea: String + sender: Source + senderType: String + receiver: Source + resolved: Boolean + } +` \ No newline at end of file diff --git a/server/src/typeDefs/root.ts b/server/src/typeDefs/root.ts new file mode 100644 index 00000000..0d3d8b0a --- /dev/null +++ b/server/src/typeDefs/root.ts @@ -0,0 +1,12 @@ +import { gql } from 'apollo-server-express' + +export default gql` + directive @auth on FIELD_DEFINITION + + type Query { + _: String + } + type Mutation { + _: String + } +` \ No newline at end of file diff --git a/server/src/typeDefs/team.ts b/server/src/typeDefs/team.ts new file mode 100644 index 00000000..7ec4500e --- /dev/null +++ b/server/src/typeDefs/team.ts @@ -0,0 +1,25 @@ +const { gql } = require("apollo-server-express"); + +export default gql` + extend type Query { + teams(search: String, name: String, picture: String, team_id: String, interests: String, description: String, public: Boolean): [Team!]! @auth + team(team_id: String): Team! @auth + } + + extend type Mutation { + update_team(name: String, picture: String, interests: [String], description: String, public: Boolean, project_idea: String): Team @auth + leave_team: User @auth + } + + type Team { + id: ID! + name: String! + picture: String + members: [User!] + interests: [String!] + description: String + project_idea: String + notifications: [Notification!]! + public: Boolean + } +` \ No newline at end of file diff --git a/server/src/typeDefs/user.ts b/server/src/typeDefs/user.ts new file mode 100644 index 00000000..3ac27ad3 --- /dev/null +++ b/server/src/typeDefs/user.ts @@ -0,0 +1,30 @@ +const { gql } = require("apollo-server-express"); + +export default gql` + extend type Query { + users(search: String, skill: String, name: String, email: String, grad_year: String, school: String, visible: Int): [User!]! @auth + user(user_id: String): User! @auth + user_profile: User! @auth + } + + extend type Mutation { + update_user(name: String email: String, grad_year: String, school: String, contact: String, skills: [String], experience: String, contact_method: String, visible: Int): User! @auth + } + + type User { + id: ID! + uuid: String + name: String! + email: String! + school: String + grad_year: String + beginner: Boolean + skills: [String!] + experience: String + contact: String + contact_method: String + visible: Int + team: Team + slackid: String + } +` \ No newline at end of file diff --git a/server/src/types/index.ts b/server/src/types/index.ts new file mode 100644 index 00000000..95fdde9d --- /dev/null +++ b/server/src/types/index.ts @@ -0,0 +1,7 @@ +export * from './notification' + +export * from './mongoose' + +export * from './team' + +export * from './user' \ No newline at end of file diff --git a/server/src/types/mongoose.ts b/server/src/types/mongoose.ts new file mode 100644 index 00000000..ab1d4f97 --- /dev/null +++ b/server/src/types/mongoose.ts @@ -0,0 +1,9 @@ +import { Model, Document, Types } from 'mongoose' + +type Omit = Pick>; +export interface RootDocument { + _id: Types.ObjectId; +} +export function createNew(model: Model, doc: Omit) { + return new model(doc); +} \ No newline at end of file diff --git a/server/src/types/notification.ts b/server/src/types/notification.ts new file mode 100644 index 00000000..7117c457 --- /dev/null +++ b/server/src/types/notification.ts @@ -0,0 +1,20 @@ +import { RootDocument } from './mongoose' +import { Document } from 'mongoose' +import { IUser } from './user' +import { ITeam } from './team' + +export interface INotification extends RootDocument { + message: string; + bio: string; + idea: string; + notificationType: string; + sender: IUser & ITeam; + senderType: string; + receiverType: string; + receiver: IUser & ITeam; + resolved: boolean; + +} + +export type INotificationMongoose = INotification & Document; + diff --git a/server/src/types/team.ts b/server/src/types/team.ts new file mode 100644 index 00000000..8d03420f --- /dev/null +++ b/server/src/types/team.ts @@ -0,0 +1,17 @@ +import { RootDocument } from './mongoose' +import { Document } from 'mongoose' +import { IUser } from './user' + +export interface ITeam extends RootDocument{ + uuid: string; + creator: string; + name: string; + picture?: string; + members: IUser[]; + interests?: string[]; + description?: string; + project_idea?: string; + public: boolean; +} + +export type ITeamMongoose = ITeam & Document; diff --git a/server/src/types/user.ts b/server/src/types/user.ts new file mode 100644 index 00000000..595999d3 --- /dev/null +++ b/server/src/types/user.ts @@ -0,0 +1,25 @@ +import { RootDocument } from './mongoose' +import { Document } from 'mongoose' +import { ITeam } from './team' + +export interface IUser extends RootDocument { + uuid: string; + email: string; + name: string; + token: string | null; + admin?: boolean; + school?: string; + grad_year?: string; + skills?: string[]; + beginner?: boolean; + experience?: string; + contact?: string; + image?: string; + contact_method?: string; + visible?: number; + team?: ITeam; + slackid: string; +} + +export type IUserMongoose = IUser & Document; + diff --git a/server/tsconfig.json b/server/tsconfig.json index c8eb0cb7..8638fa0a 100644 --- a/server/tsconfig.json +++ b/server/tsconfig.json @@ -20,7 +20,8 @@ "noUnusedParameters": false, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, - "esModuleInterop": true + "esModuleInterop": true, + "skipLibCheck": true }, "include": [ "src/**/*" diff --git a/workspace.code-workspace b/workspace.code-workspace new file mode 100644 index 00000000..362d7c25 --- /dev/null +++ b/workspace.code-workspace @@ -0,0 +1,7 @@ +{ + "folders": [ + { + "path": "." + } + ] +} \ No newline at end of file From a5b77258686fc1a3cfd88bbd395f118408941117 Mon Sep 17 00:00:00 2001 From: Rahul Rajan Date: Sun, 20 Dec 2020 21:12:01 -0500 Subject: [PATCH 2/3] add notification resolvers --- client/src/components/TeamCard.js | 167 ++++++++------ client/src/components/TeamInformation.js | 78 +++---- client/src/components/TeamNotifications.js | 174 +++++++------- client/src/components/TeamRequestsSent.js | 168 +++++++------- .../__generated__/MembersQuery.graphql.js | 214 ------------------ .../TeamNotificationsQuery.graphql.js | 100 ++++---- .../__generated__/TeamPageQuery.graphql.js | 15 +- .../TeamRequestsSentQuery.graphql.js | 106 +++++---- server/api.graphql | 3 +- server/src/app2.ts | 2 +- server/src/models/index.ts | 2 +- server/src/resolvers/index.ts | 3 +- server/src/resolvers/notification/index.ts | 23 ++ .../mutations/acceptTeamRequest.ts | 87 +++++++ .../mutations/acceptUserRequest.ts | 131 +++++++++++ .../resolvers/notification/mutations/index.ts | 7 + .../notification/mutations/makeTeamRequest.ts | 69 ++++++ .../notification/mutations/makeUserRequest.ts | 81 +++++++ .../queries/getTeamNotifications.ts | 30 +++ .../queries/getUserNotifications.ts | 18 ++ .../resolvers/notification/queries/index.ts | 3 + .../resolvers/team/mutations/updateTeam.ts | 6 +- server/src/resolvers/user/queries/getUsers.ts | 17 +- server/src/typeDefs/notification.ts | 3 +- server/src/types/notification.ts | 2 +- server/src/types/team.ts | 2 +- server/src/types/user.ts | 2 +- server/src/utils/index.ts | 1 + .../{sendSlackMessage.ts => utils/slack.ts} | 0 29 files changed, 925 insertions(+), 589 deletions(-) delete mode 100644 client/src/components/__generated__/MembersQuery.graphql.js create mode 100644 server/src/resolvers/notification/index.ts create mode 100644 server/src/resolvers/notification/mutations/acceptTeamRequest.ts create mode 100644 server/src/resolvers/notification/mutations/acceptUserRequest.ts create mode 100644 server/src/resolvers/notification/mutations/index.ts create mode 100644 server/src/resolvers/notification/mutations/makeTeamRequest.ts create mode 100644 server/src/resolvers/notification/mutations/makeUserRequest.ts create mode 100644 server/src/resolvers/notification/queries/getTeamNotifications.ts create mode 100644 server/src/resolvers/notification/queries/getUserNotifications.ts create mode 100644 server/src/resolvers/notification/queries/index.ts create mode 100644 server/src/utils/index.ts rename server/src/{sendSlackMessage.ts => utils/slack.ts} (100%) diff --git a/client/src/components/TeamCard.js b/client/src/components/TeamCard.js index 8361dbb4..6919c16f 100644 --- a/client/src/components/TeamCard.js +++ b/client/src/components/TeamCard.js @@ -1,79 +1,100 @@ -import React, { Component } from 'react'; -import { Button, Card, Popup, Container, Label, TextArea, Placeholder } from 'semantic-ui-react'; -import JoinTeam from './ui_subcomponents/JoinTeam'; -import './css/TeamCard.css'; +import React, { Component } from "react"; +import { + Button, + Card, + Popup, + Container, + Label, + TextArea, + Placeholder, +} from "semantic-ui-react"; +import JoinTeam from "./ui_subcomponents/JoinTeam"; +import "./css/TeamCard.css"; import { Link, useParams } from "react-router-dom"; class TeamCard extends Component { - constructor(props) { - super(props) - this.state = { - showModal: false - } - } - closeModal = () => { - console.log("close individual") - this.setState({ showModal: false }); + constructor(props) { + super(props); + this.state = { + showModal: false, }; - render() { - // let contact; - // contact = } - // content="Team has been joined!" - // on='click' - // hideOnScroll - // /> - var colors = [ - 'violet', - 'yellow', - 'orange' - ] - var count = 0; - var skill; - // var viewskill; - let link = "/team/" + this.props.id; - var colors = ["#A0CCC9", "#EBABCA"]; - var count = 0; - var viewskill = this.props.interests.map((skill) => ( - - )); - var button = this.props.team ? :