From 5dffd0fbd65753181b4ac171cac40dd1b10df234 Mon Sep 17 00:00:00 2001
From: Yannis Barlas <yannisbarlas@gmail.com>
Date: Sat, 28 Mar 2020 23:23:07 +0200
Subject: [PATCH] fix(server): resolve circular dependencies & make passport
 auth work

---
 package.json      |  3 +++
 src/app.js        | 34 ++++++++++++-----------
 src/graphqlApi.js | 69 +++++++++++++++++++++++++++++++++++++++++++++++
 yarn.lock         |  2 +-
 4 files changed, 91 insertions(+), 17 deletions(-)
 create mode 100644 src/graphqlApi.js

diff --git a/package.json b/package.json
index 45655a0..06f6624 100644
--- a/package.json
+++ b/package.json
@@ -23,14 +23,17 @@
     "access": "public"
   },
   "dependencies": {
+    "@pubsweet/errors": "^2.0.32",
     "@pubsweet/logger": "^0.2.42",
     "@pubsweet/models": "^0.3.7",
+    "apollo-server-express": "^2.11.0",
     "body-parser": "^1.19.0",
     "config": "^3.3.1",
     "cookie-parser": "^1.4.5",
     "express": "^4.17.1",
     "helmet": "^3.22.0",
     "http-status-codes": "^1.4.0",
+    "lodash": "^4.17.15",
     "morgan": "^1.10.0",
     "passport": "^0.4.1",
     "pubsweet-server": "^13.9.4",
diff --git a/src/app.js b/src/app.js
index c1f8458..f7e12aa 100644
--- a/src/app.js
+++ b/src/app.js
@@ -1,4 +1,4 @@
-/* eslint-disable no-param-reassign */
+/* eslint-disable global-require, no-param-reassign */
 
 const path = require('path')
 
@@ -13,20 +13,15 @@ const passport = require('passport')
 const wait = require('waait')
 
 const logger = require('@pubsweet/logger')
-const models = require('@pubsweet/models')
 
-const gqlApi = require('pubsweet-server/src/graphql/api')
-const index = require('pubsweet-server/src/routes/index')
-const api = require('pubsweet-server/src/routes/api')
 const registerComponents = require('pubsweet-server/src/register-components')
-const authsome = require('pubsweet-server/src/helpers/authsome')
-const authentication = require('pubsweet-server/src/authentication')
-const { startJobQueue, stopJobQueue } = require('pubsweet-server/src/jobs')
-const {
-  addSubscriptions,
-} = require('pubsweet-server/src/graphql/subscriptions')
+const api = require('pubsweet-server/src/routes/api')
+const index = require('pubsweet-server/src/routes/index')
 
 const configureApp = app => {
+  const models = require('@pubsweet/models')
+  const authsome = require('pubsweet-server/src/helpers/authsome')
+
   app.locals.models = models
 
   app.use(bodyParser.json({ limit: '50mb' }))
@@ -62,6 +57,8 @@ const configureApp = app => {
 
   // Register passport authentication strategies
   app.use(passport.initialize())
+  const authentication = require('pubsweet-server/src/authentication')
+
   passport.use('bearer', authentication.strategies.bearer)
   passport.use('anonymous', authentication.strategies.anonymous)
   passport.use('local', authentication.strategies.local)
@@ -72,12 +69,14 @@ const configureApp = app => {
   registerComponents(app)
 
   app.use('/api', api) // REST API
+
+  const gqlApi = require('./graphqlApi')
   gqlApi(app) // GraphQL API
 
   app.use('/', index) // Serve the index page for front end
 
   app.use((err, req, res, next) => {
-    // development error handler, will print stacktrace
+    // Development error handler, will print stacktrace
     if (app.get('env') === 'development' || app.get('env') === 'test') {
       logger.error(err)
       logger.error(err.stack)
@@ -106,15 +105,18 @@ const configureApp = app => {
 
   // Actions to perform when the HTTP server starts listening
   app.onListen = async server => {
-    // Add GraphQL subscriptions
-    addSubscriptions(server)
+    const {
+      addSubscriptions,
+    } = require('pubsweet-server/src/graphql/subscriptions')
+    addSubscriptions(server) // Add GraphQL subscriptions
 
-    // Manage job queue
-    await startJobQueue()
+    const { startJobQueue } = require('pubsweet-server/src/jobs')
+    await startJobQueue() // Manage job queue
   }
 
   // Actions to perform when the server closes
   app.onClose = async () => {
+    const { stopJobQueue } = require('pubsweet-server/src/jobs')
     await stopJobQueue()
     return wait(500)
   }
diff --git a/src/graphqlApi.js b/src/graphqlApi.js
new file mode 100644
index 0000000..f932dae
--- /dev/null
+++ b/src/graphqlApi.js
@@ -0,0 +1,69 @@
+const { ApolloServer } = require('apollo-server-express')
+const isEmpty = require('lodash/isEmpty')
+const logger = require('@pubsweet/logger')
+const errors = require('@pubsweet/errors')
+
+const config = require('config')
+
+const schema = require('pubsweet-server/src/graphql/schema')
+const connectors = require('pubsweet-server/src/connectors')
+const loaders = require('pubsweet-server/src/graphql/loaders')
+const helpers = require('pubsweet-server/src/helpers/authorization')
+
+const hostname = config.has('pubsweet-server.hostname')
+  ? config.get('pubsweet-server.hostname')
+  : 'localhost'
+
+const extraApolloConfig = config.has('pubsweet-server.apollo')
+  ? config.get('pubsweet-server.apollo')
+  : {}
+
+const api = app => {
+  app.use(
+    '/graphql',
+    app.locals.passport.authenticate(['bearer', 'anonymous'], {
+      session: false,
+    }),
+  )
+
+  const server = new ApolloServer({
+    schema,
+    context: ({ req, res }) => ({
+      helpers,
+      connectors,
+      user: req.user,
+      loaders: loaders(),
+    }),
+    formatError: err => {
+      const error = isEmpty(err.originalError) ? err : err.originalError
+
+      logger.error(error.message, { error })
+
+      const isPubsweetDefinedError = Object.values(errors).some(
+        pubsweetError => error instanceof pubsweetError,
+      )
+
+      // err is always a GraphQLError which should be passed to the client
+      if (!isEmpty(err.originalError) && !isPubsweetDefinedError)
+        return {
+          name: 'Server Error',
+          message: 'Something went wrong! Please contact your administrator',
+        }
+
+      return {
+        name: error.name || 'GraphQLError',
+        message: error.message,
+        extensions: {
+          code: err.extensions.code,
+        },
+      }
+    },
+    playground: {
+      subscriptionEndpoint: `ws://${hostname}:3000/subscriptions`,
+    },
+    ...extraApolloConfig,
+  })
+  server.applyMiddleware({ app })
+}
+
+module.exports = api
diff --git a/yarn.lock b/yarn.lock
index 1579c95..e90d5f8 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -845,7 +845,7 @@ apollo-server-errors@^2.4.0:
   resolved "https://registry.yarnpkg.com/apollo-server-errors/-/apollo-server-errors-2.4.0.tgz#3096db02b6ae8d434a6b2678f74eddaad8b98452"
   integrity sha512-ZouZfr2sGavvI18rgdRcyY2ausRAlVtWNOax9zca8ZG2io86dM59jXBmUVSNlVZSmBsIh45YxYC0eRvr2vmRdg==
 
-apollo-server-express@^2.4.8:
+apollo-server-express@^2.11.0, apollo-server-express@^2.4.8:
   version "2.11.0"
   resolved "https://registry.yarnpkg.com/apollo-server-express/-/apollo-server-express-2.11.0.tgz#ef59a15f6f9ba8a8fb90cfa8b7c4c436be6e84c0"
   integrity sha512-9bbiD+zFAx+xyurc9lxYmNa9y79k/gsA1vEyPFVcv7jxzCFC5wc0tcbV7NPX2qi1Nn7K76fxo2fPNYbPFX/y0g==
-- 
GitLab