From afa0ab8c556dc9bd78cb154c68298eaf35e782e2 Mon Sep 17 00:00:00 2001 From: Ben Whitmore <ben.whitmore0@gmail.com> Date: Thu, 4 Mar 2021 05:00:45 +0000 Subject: [PATCH] fix(review): fix accept/decline review request Closes #62 --- .../src/components/sections/EditorItem.js | 35 +++++++++++++++++-- .../src/components/sections/ReviewerItem.js | 19 +++++++++- .../src/{Manuscript.jsx => Manuscript.js} | 23 ++++++++---- .../components/decision/DecisionReviews.js | 23 +++++++++++- .../src/components/metadata/ReviewMetadata.js | 17 ++++++++- docker-compose.yml | 1 + server/model-manuscript/src/graphql.js | 9 +++-- server/subscriptions.js | 14 ++++++-- 8 files changed, 124 insertions(+), 17 deletions(-) rename app/components/component-manuscripts/src/{Manuscript.jsx => Manuscript.js} (78%) diff --git a/app/components/component-dashboard/src/components/sections/EditorItem.js b/app/components/component-dashboard/src/components/sections/EditorItem.js index 8dfbb1e03f..54237a55cd 100644 --- a/app/components/component-dashboard/src/components/sections/EditorItem.js +++ b/app/components/component-dashboard/src/components/sections/EditorItem.js @@ -45,7 +45,14 @@ const EditorItemLinks = ({ version }) => ( ) EditorItemLinks.propTypes = { - version: PropTypes.element.isRequired, + version: PropTypes.shape({ + id: PropTypes.string.isRequired, + parentId: PropTypes.string, + decision: PropTypes.shape({ + status: PropTypes.string.isRequired, + recommendation: PropTypes.string.isRequired, + }), + }).isRequired, } const getDeclarationsObject = (version, value) => { @@ -102,7 +109,31 @@ const EditorItem = ({ version }) => ( ) EditorItem.propTypes = { - version: PropTypes.element.isRequired, + version: PropTypes.shape({ + id: PropTypes.string.isRequired, + parentId: PropTypes.string, + decision: PropTypes.shape({ + status: PropTypes.string.isRequired, + recommendation: PropTypes.string.isRequired, + }), + meta: PropTypes.shape({ + title: PropTypes.string.isRequired, + }).isRequired, + // eslint-disable-next-line react/forbid-prop-types + published: PropTypes.any, // TODO require boolean rather than any truthy or falsey value + status: PropTypes.string.isRequired, + teams: PropTypes.arrayOf( + PropTypes.shape({ + role: PropTypes.string.isRequired, + members: PropTypes.arrayOf( + PropTypes.shape({ + status: PropTypes.string, + }).isRequired, + ).isRequired, + }).isRequired, + ).isRequired, + _currentRoles: PropTypes.arrayOf(PropTypes.string.isRequired).isRequired, + }).isRequired, } export default EditorItem diff --git a/app/components/component-dashboard/src/components/sections/ReviewerItem.js b/app/components/component-dashboard/src/components/sections/ReviewerItem.js index 28b34d9ffa..911a6dba70 100644 --- a/app/components/component-dashboard/src/components/sections/ReviewerItem.js +++ b/app/components/component-dashboard/src/components/sections/ReviewerItem.js @@ -72,7 +72,24 @@ const ReviewerItem = ({ version, currentUser, reviewerRespond }) => { } ReviewerItem.propTypes = { - version: PropTypes.string.isRequired, + version: PropTypes.shape({ + id: PropTypes.string.isRequired, + meta: PropTypes.shape({ + title: PropTypes.string.isRequired, + }).isRequired, + teams: PropTypes.arrayOf( + PropTypes.shape({ + role: PropTypes.string.isRequired, + members: PropTypes.arrayOf( + PropTypes.shape({ + user: PropTypes.shape({ + id: PropTypes.string.isRequired, + }).isRequired, + }).isRequired, + ).isRequired, + }).isRequired, + ).isRequired, + }).isRequired, currentUser: PropTypes.oneOfType([PropTypes.object]).isRequired, reviewerRespond: PropTypes.func.isRequired, } diff --git a/app/components/component-manuscripts/src/Manuscript.jsx b/app/components/component-manuscripts/src/Manuscript.js similarity index 78% rename from app/components/component-manuscripts/src/Manuscript.jsx rename to app/components/component-manuscripts/src/Manuscript.js index 529e267e8c..32fc549d63 100644 --- a/app/components/component-manuscripts/src/Manuscript.jsx +++ b/app/components/component-manuscripts/src/Manuscript.js @@ -1,7 +1,5 @@ -/* eslint-disable react/jsx-filename-extension */ import React from 'react' -import gql from 'graphql-tag' -import { useMutation } from '@apollo/client' +import { gql, useMutation } from '@apollo/client' // import { Action } from '@pubsweet/ui' import config from 'config' import PropTypes from 'prop-types' @@ -83,9 +81,22 @@ const User = ({ manuscriptId, manuscript, submitter }) => { } User.propTypes = { - manuscriptId: PropTypes.number.isRequired, - manuscript: PropTypes.element.isRequired, - submitter: PropTypes.element.isRequired, + manuscriptId: PropTypes.string.isRequired, + manuscript: PropTypes.shape({ + meta: PropTypes.shape({ + title: PropTypes.string.isRequired, + }).isRequired, + created: PropTypes.string.isRequired, + updated: PropTypes.string, + status: PropTypes.string.isRequired, + }).isRequired, + submitter: PropTypes.shape({ + defaultIdentity: PropTypes.shape({ + name: PropTypes.string.isRequired, + }).isRequired, + email: PropTypes.string, + username: PropTypes.string.isRequired, + }).isRequired, } export default User diff --git a/app/components/component-review/src/components/decision/DecisionReviews.js b/app/components/component-review/src/components/decision/DecisionReviews.js index c4a741dc07..9b3b79b9af 100644 --- a/app/components/component-review/src/components/decision/DecisionReviews.js +++ b/app/components/component-review/src/components/decision/DecisionReviews.js @@ -62,7 +62,28 @@ const DecisionReviews = ({ manuscript }) => ( ) DecisionReviews.propTypes = { - manuscript: PropTypes.element.isRequired, + manuscript: PropTypes.shape({ + id: PropTypes.string.isRequired, + reviews: PropTypes.arrayOf( + PropTypes.shape({ + user: PropTypes.shape({ + id: PropTypes.string.isRequired, + }).isRequired, + }).isRequired, + ).isRequired, + teams: PropTypes.arrayOf( + PropTypes.shape({ + role: PropTypes.string.isRequired, + members: PropTypes.arrayOf( + PropTypes.shape({ + user: PropTypes.shape({ + id: PropTypes.string.isRequired, + }).isRequired, + }).isRequired, + ).isRequired, + }).isRequired, + ).isRequired, + }).isRequired, } export default DecisionReviews diff --git a/app/components/component-review/src/components/metadata/ReviewMetadata.js b/app/components/component-review/src/components/metadata/ReviewMetadata.js index 934032b919..b2b05b5585 100644 --- a/app/components/component-review/src/components/metadata/ReviewMetadata.js +++ b/app/components/component-review/src/components/metadata/ReviewMetadata.js @@ -130,7 +130,22 @@ const ReviewMetadata = ({ manuscript: rawManuscript }) => { } ReviewMetadata.propTypes = { - manuscript: PropTypes.node.isRequired, + manuscript: PropTypes.shape({ + meta: PropTypes.shape({ + notes: PropTypes.arrayOf( + PropTypes.shape({ + notesType: PropTypes.string.isRequired, + content: PropTypes.string.isRequired, + }).isRequired, + ).isRequired, + }).isRequired, + files: PropTypes.arrayOf( + PropTypes.shape({ + url: PropTypes.string.isRequired, + filename: PropTypes.string.isRequired, + }).isRequired, + ).isRequired, + }).isRequired, } export default ReviewMetadata diff --git a/docker-compose.yml b/docker-compose.yml index ba141465cf..e0ab7f2611 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -17,6 +17,7 @@ services: - ${CLIENT_PORT:-4000}:${CLIENT_PORT:-4000} environment: - NODE_ENV=development + - CLIENT_PROTOCOL=${CLIENT_PROTOCOL:-http} - CLIENT_HOST=0.0.0.0 - CLIENT_PORT=${CLIENT_PORT:-4000} - SERVER_PROTOCOL=http diff --git a/server/model-manuscript/src/graphql.js b/server/model-manuscript/src/graphql.js index b577a01892..3017b5044b 100644 --- a/server/model-manuscript/src/graphql.js +++ b/server/model-manuscript/src/graphql.js @@ -148,12 +148,15 @@ const resolvers = { return id }, async reviewerResponse(_, { action, teamId }, context) { - // eslint-disable-next-line global-require - const { TeamModel, ReviewModel } = require('@pubsweet/models') // Pubsweet models may initially be undefined, so we require only when resolver runs. + const { + Team: TeamModel, + Review: ReviewModel, + // eslint-disable-next-line global-require + } = require('@pubsweet/models') // Pubsweet models may initially be undefined, so we require only when resolver runs. if (action !== 'accepted' && action !== 'rejected') throw new Error( - `Invalid action (revieweResponse): Must be either "accepted" or "rejected"`, + `Invalid action (reviewerResponse): Must be either "accepted" or "rejected"`, ) const team = await TeamModel.query().findById(teamId).eager('members') diff --git a/server/subscriptions.js b/server/subscriptions.js index f4f2de1fbf..597e484d11 100644 --- a/server/subscriptions.js +++ b/server/subscriptions.js @@ -2,7 +2,8 @@ * subscription (websocket) server for GraphQL */ const { execute, subscribe } = require('graphql') -const { SubscriptionServer } = require('subscriptions-transport-ws') +// eslint-disable-next-line import/no-extraneous-dependencies +const { SubscriptionServer } = require('subscriptions-transport-ws') // TODO: Should we rewrite subscriptions to use Apollo v2? const logger = require('@pubsweet/logger') @@ -11,10 +12,12 @@ const { token } = require('pubsweet-server/src/authentication') // TODO: Fix imp module.exports = { addSubscriptions: server => { + /* eslint-disable global-require */ const models = require('@pubsweet/models') const helpers = require('pubsweet-server/src/helpers/authorization') - const { User } = require('@pubsweet/models') + /* eslint-enable global-require */ + SubscriptionServer.create( { schema: graphqlSchema, @@ -22,8 +25,11 @@ module.exports = { subscribe, onConnect: async (connectionParams, webSocket, context) => { if (!connectionParams.authToken) { - throw new Error('Missing auth token') + logger.info('Missing auth token') + return false + // throw new Error('Missing auth token') } + const addTocontext = await new Promise((resolve, reject) => { token.verify(connectionParams.authToken, (_, id) => { if (!id) { @@ -34,6 +40,7 @@ module.exports = { resolve({ userId: id, models, helpers }) }) }) + // Record a user's online status const user = await User.query().updateAndFetchById( addTocontext.userId, @@ -45,6 +52,7 @@ module.exports = { }, onDisconnect: async (webSocket, context) => { const initialContext = await context.initPromise + // Record that a user is no longer online if (initialContext.user && initialContext.user.id) { await User.query() -- GitLab