diff --git a/packages/xpub-faraday-server/.gitignore b/packages/component-manuscript-manager/.gitignore similarity index 65% rename from packages/xpub-faraday-server/.gitignore rename to packages/component-manuscript-manager/.gitignore index 3614a810088d89d9ccaa28d82401545634874a18..59ff5cd1f687b2ad8339cb159a605672cbc76340 100644 --- a/packages/xpub-faraday-server/.gitignore +++ b/packages/component-manuscript-manager/.gitignore @@ -5,4 +5,5 @@ node_modules/ uploads/ .env.* .env -config/local*.* \ No newline at end of file +config/local*.* +public/apidoc \ No newline at end of file diff --git a/packages/component-manuscript-manager/apiDoc.json b/packages/component-manuscript-manager/apiDoc.json new file mode 100644 index 0000000000000000000000000000000000000000..3e59bded0b586134166a5715ce20f7ce568322bf --- /dev/null +++ b/packages/component-manuscript-manager/apiDoc.json @@ -0,0 +1,8 @@ +{ + "name": "Component Manuscript Manager API documentation", + "version": "0.0.1", + "description": "A list of APIs for the Manuscript Manager Component", + "template": { + "forceLanguage": "en" + } +} diff --git a/packages/component-manuscript-manager/index.js b/packages/component-manuscript-manager/index.js new file mode 100644 index 0000000000000000000000000000000000000000..0c04f744119898af8a4ae1dc44a6eabbc5c2b24b --- /dev/null +++ b/packages/component-manuscript-manager/index.js @@ -0,0 +1,5 @@ +module.exports = { + backend: () => app => { + require('./src/FragmentsRecommendations')(app) + }, +} diff --git a/packages/component-manuscript-manager/package.json b/packages/component-manuscript-manager/package.json new file mode 100644 index 0000000000000000000000000000000000000000..9d40f80a6ae8f8706e7e0fc235e9940a444c8b6b --- /dev/null +++ b/packages/component-manuscript-manager/package.json @@ -0,0 +1,42 @@ +{ + "name": "pubsweet-component-manuscript-manager", + "version": "0.0.1", + "description": "manuscript manager component for pubsweet", + "license": "MIT", + "author": "Collaborative Knowledge Foundation", + "files": [ + "src" + ], + "main": "index.js", + "scripts": { + "test": "jest", + "docs": "./node_modules/.bin/apidoc -e \"(node_modules|public)\" -o public/apidoc", + "open-docs": "open public/apidoc/index.html" + }, + "repository": { + "type": "git", + "url": "https://gitlab.coko.foundation/xpub/xpub", + "path": "component-manuscript-manager" + }, + "dependencies": { + "body-parser": "^1.17.2", + "chance": "^1.0.13" + }, + "peerDependencies": { + "@pubsweet/logger": "^0.0.1", + "pubsweet-component-mail-service": "0.0.1", + "pubsweet-server": "^1.0.1" + }, + "devDependencies": { + "apidoc": "^0.17.6", + "jest": "^22.1.1", + "supertest": "^3.0.0" + }, + "jest": { + "verbose": true, + "testRegex": "/src/.*.test.js$" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/packages/component-manuscript-manager/src/FragmentsRecommendations.js b/packages/component-manuscript-manager/src/FragmentsRecommendations.js new file mode 100644 index 0000000000000000000000000000000000000000..df3608c1ace03452ba6124e92e283873edec2666 --- /dev/null +++ b/packages/component-manuscript-manager/src/FragmentsRecommendations.js @@ -0,0 +1,73 @@ +const bodyParser = require('body-parser') + +const FragmentsRecommendations = app => { + app.use(bodyParser.json()) + const basePath = + '/api/collections/:collectionId/fragments/:fragmentId/recommendations' + const routePath = './routes/fragmentsRecommendations' + const authBearer = app.locals.passport.authenticate('bearer', { + session: false, + }) + /** + * @api {post} /api/collections/:collectionId/fragments/:fragmentId/recommendations Create a recommendation on a fragment + * @apiGroup FragmentsRecommendations + * @apiParam {collectionId} collectionId Collection id + * @apiParam {fragmentId} fragmentId Fragment id + * @apiParamExample {json} Body + * { + * "recommendation": "accept", [acceptedValues: accept, revise, etc.], + * "comments": + * [ + * { + * "content": "A very nice manuscript", + * "public": true + * "files": + * [ + * { + * "id": "111-22-333", + * "name": "file.pdf", + * "size": 104232 + * } + * ] + * } + * ], + * "type": "review" [acceptedValues: review, editorRecommendation] + * } + * @apiSuccessExample {json} Success + * HTTP/1.1 200 OK + * { + * "id": "7b2431af-210c-49f9-a69a-e19271066ebd", + * "userId": "4c3f8ee1-785b-4adb-87b4-407a27f652c6", + * "submittedOn": 1525428890167, + * "recommendation": "accept", [acceptedValues: accept, revise, etc.], + * "comments": + * [ + * { + * "content": "A very nice manuscript", + * "public": true + * "files": + * [ + * { + * "id": "111-22-333", + * "name": "file.pdf", + * "size": 104232 + * } + * ] + * } + * ], + * "type": "review" [acceptedValues: review, editorRecommendation] + * } + * @apiErrorExample {json} Invite user errors + * HTTP/1.1 403 Forbidden + * HTTP/1.1 400 Bad Request + * HTTP/1.1 404 Not Found + * HTTP/1.1 500 Internal Server Error + */ + app.post( + basePath, + authBearer, + require(`${routePath}/post`)(app.locals.models), + ) +} + +module.exports = FragmentsRecommendations diff --git a/packages/component-manuscript-manager/src/helpers/helpers.js b/packages/component-manuscript-manager/src/helpers/helpers.js new file mode 100644 index 0000000000000000000000000000000000000000..a37f7504aaa1efd3fbacdc94734fc0807cfa5734 --- /dev/null +++ b/packages/component-manuscript-manager/src/helpers/helpers.js @@ -0,0 +1,83 @@ +const logger = require('@pubsweet/logger') + +const checkForUndefinedParams = (...params) => { + if (params.includes(undefined)) { + return false + } + + return true +} + +const validateEmailAndToken = async (email, token, userModel) => { + try { + const user = await userModel.findByEmail(email) + if (user) { + if (token !== user.passwordResetToken) { + logger.error( + `invite pw reset tokens do not match: REQ ${token} vs. DB ${ + user.passwordResetToken + }`, + ) + return { + success: false, + status: 400, + message: 'invalid request', + } + } + return { success: true, user } + } + } catch (e) { + if (e.name === 'NotFoundError') { + logger.error('invite pw reset on non-existing user') + return { + success: false, + status: 404, + message: 'user not found', + } + } else if (e.name === 'ValidationError') { + logger.error('invite pw reset validation error') + return { + success: false, + status: 400, + message: e.details[0].message, + } + } + logger.error('internal server error') + return { + success: false, + status: 500, + message: e.details[0].message, + } + } + return { + success: false, + status: 500, + message: 'something went wrong', + } +} + +const handleNotFoundError = async (error, item) => { + const response = { + success: false, + status: 500, + message: 'Something went wrong', + } + if (error.name === 'NotFoundError') { + logger.error(`invalid ${item} id`) + response.status = 404 + response.message = `${item} not found` + return response + } + + logger.error(error) + return response +} + +const getBaseUrl = req => `${req.protocol}://${req.get('host')}` + +module.exports = { + checkForUndefinedParams, + validateEmailAndToken, + handleNotFoundError, + getBaseUrl, +} diff --git a/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/post.js b/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/post.js new file mode 100644 index 0000000000000000000000000000000000000000..3c18f9869315eda64df1d08a6576ae51c442699f --- /dev/null +++ b/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/post.js @@ -0,0 +1,41 @@ +const helpers = require('../../helpers/helpers') +const uuid = require('uuid') + +module.exports = models => async (req, res) => { + const { recommendation, comments, type } = req.body + + if (!helpers.checkForUndefinedParams(recommendation, comments, type)) + return res.status(400).json({ error: 'Parameters are missing.' }) + + const reqUser = await models.User.find(req.user) + const { collectionId, fragmentId } = req.params + let collection, fragment + try { + collection = await models.Collection.find(collectionId) + if (!collection.fragments.includes(fragmentId)) + return res.status(400).json({ + error: `collection ${ + collection.id + } does not contain fragment ${fragmentId}.`, + }) + + fragment = await models.Fragment.find(fragmentId) + } catch (e) { + const notFoundError = await helpers.handleNotFoundError(e, 'item') + return res.status(notFoundError.status).json({ + error: notFoundError.message, + }) + } + fragment.recommendations = fragment.recommendations || [] + const newRecommendation = { + id: uuid.v4(), + userId: reqUser.id, + submittedOn: new Date(), + type, + recommendation, + comments, + } + fragment.recommendations.push(newRecommendation) + await fragment.save() + return res.status(200).json(newRecommendation) +} diff --git a/packages/component-manuscript-manager/src/tests/fixtures/collections.js b/packages/component-manuscript-manager/src/tests/fixtures/collections.js new file mode 100644 index 0000000000000000000000000000000000000000..bef6132199981a79b87ea86440146eaab5891883 --- /dev/null +++ b/packages/component-manuscript-manager/src/tests/fixtures/collections.js @@ -0,0 +1,68 @@ +const Chance = require('chance') +const { + user, + handlingEditor, + author, + reviewer, + answerReviewer, +} = require('./userData') +const { fragment } = require('./fragments') + +const chance = new Chance() +const collections = { + collection: { + id: chance.guid(), + title: chance.sentence(), + type: 'collection', + fragments: [fragment.id], + owners: [user.id], + save: jest.fn(), + authors: [ + { + userId: author.id, + isSubmitting: true, + isCorresponding: false, + }, + ], + invitations: [ + { + id: chance.guid(), + role: 'handlingEditor', + hasAnswer: false, + isAccepted: false, + userId: handlingEditor.id, + invitedOn: chance.timestamp(), + respondedOn: null, + }, + { + id: chance.guid(), + role: 'reviewer', + hasAnswer: false, + isAccepted: false, + userId: reviewer.id, + invitedOn: chance.timestamp(), + respondedOn: null, + }, + { + id: chance.guid(), + role: 'reviewer', + hasAnswer: true, + isAccepted: false, + userId: answerReviewer.id, + invitedOn: chance.timestamp(), + respondedOn: chance.timestamp(), + }, + ], + handlingEditor: { + id: handlingEditor.id, + hasAnswer: false, + isAccepted: false, + email: handlingEditor.email, + invitedOn: chance.timestamp(), + respondedOn: null, + name: `${handlingEditor.firstName} ${handlingEditor.lastName}`, + }, + }, +} + +module.exports = collections diff --git a/packages/xpub-faraday-server/src/fixtures/fixtures.js b/packages/component-manuscript-manager/src/tests/fixtures/fixtures.js similarity index 78% rename from packages/xpub-faraday-server/src/fixtures/fixtures.js rename to packages/component-manuscript-manager/src/tests/fixtures/fixtures.js index 18c1d710ce21d4d2d3cf57477b96b1f3cc18c1e1..4ca540342b93acc22bf7903e6f5f8251481bc6ca 100644 --- a/packages/xpub-faraday-server/src/fixtures/fixtures.js +++ b/packages/component-manuscript-manager/src/tests/fixtures/fixtures.js @@ -1,11 +1,9 @@ -const authors = require('./authors') -const fragments = require('./fragments') const users = require('./users') const collections = require('./collections') +const fragments = require('./fragments') module.exports = { - authors, users, - fragments, collections, + fragments, } diff --git a/packages/component-manuscript-manager/src/tests/fixtures/fragments.js b/packages/component-manuscript-manager/src/tests/fixtures/fragments.js new file mode 100644 index 0000000000000000000000000000000000000000..c64478c3390cb63db38602cced003cf97087a32f --- /dev/null +++ b/packages/component-manuscript-manager/src/tests/fixtures/fragments.js @@ -0,0 +1,15 @@ +const Chance = require('chance') + +const chance = new Chance() +const fragments = { + fragment: { + id: chance.guid(), + metadata: { + title: chance.sentence(), + abstract: chance.paragraph(), + }, + save: jest.fn(), + }, +} + +module.exports = fragments diff --git a/packages/component-manuscript-manager/src/tests/fixtures/userData.js b/packages/component-manuscript-manager/src/tests/fixtures/userData.js new file mode 100644 index 0000000000000000000000000000000000000000..546e2d867998c6de1cab8e10a4df20fb74d5288d --- /dev/null +++ b/packages/component-manuscript-manager/src/tests/fixtures/userData.js @@ -0,0 +1,18 @@ +const Chance = require('chance') + +const chance = new Chance() +const generateUserData = () => ({ + id: chance.guid(), + email: chance.email(), + firstName: chance.first(), + lastName: chance.last(), +}) + +module.exports = { + handlingEditor: generateUserData(), + user: generateUserData(), + admin: generateUserData(), + author: generateUserData(), + reviewer: generateUserData(), + answerReviewer: generateUserData(), +} diff --git a/packages/component-manuscript-manager/src/tests/fixtures/users.js b/packages/component-manuscript-manager/src/tests/fixtures/users.js new file mode 100644 index 0000000000000000000000000000000000000000..f3ede318fb34ae24db90440959bd3822f186fbf6 --- /dev/null +++ b/packages/component-manuscript-manager/src/tests/fixtures/users.js @@ -0,0 +1,23 @@ +const { reviewer } = require('./userData') +const Chance = require('chance') + +const chance = new Chance() +const users = { + reviewer: { + type: 'user', + username: chance.word(), + email: reviewer.email, + password: 'password', + admin: false, + id: reviewer.id, + firstName: reviewer.firstName, + lastName: reviewer.lastName, + affiliation: chance.company(), + title: 'Mr', + save: jest.fn(() => users.reviewer), + isConfirmed: true, + invitationToken: 'inv-token-123', + }, +} + +module.exports = users diff --git a/packages/component-manuscript-manager/src/tests/fragmentsRecommendations/post.test.js b/packages/component-manuscript-manager/src/tests/fragmentsRecommendations/post.test.js new file mode 100644 index 0000000000000000000000000000000000000000..a63b4e4685cf4ca3a6ae6bf91a1067f12d07eb90 --- /dev/null +++ b/packages/component-manuscript-manager/src/tests/fragmentsRecommendations/post.test.js @@ -0,0 +1,76 @@ +process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0' +process.env.SUPPRESS_NO_CONFIG_WARNING = true + +const fixtures = require('./../fixtures/fixtures') +const Chance = require('chance') +const Model = require('./../helpers/Model') +const cloneDeep = require('lodash/cloneDeep') +const requests = require('./../helpers/requests') + +const chance = new Chance() +const reqBody = { + recommendation: 'accept', + comments: [ + { + content: chance.paragraph(), + public: chance.bool(), + files: [ + { + id: chance.guid(), + name: 'file.pdf', + size: chance.natural(), + }, + ], + }, + ], + type: 'review', +} +// const route = { +// path: '/api/collections/:collectionId/invitations', +// } + +const path = '../../routes/fragmentsRecommendations/post' +describe('Post collections invitations route handler', () => { + let testFixtures = {} + let body = {} + let models + beforeEach(() => { + testFixtures = cloneDeep(fixtures) + body = cloneDeep(reqBody) + models = Model.build(testFixtures) + }) + it('should return an error params are missing', async () => { + const { reviewer } = testFixtures.users + delete body.comments + const res = await requests.sendRequest({ + body, + userId: reviewer.id, + models, + path, + }) + + expect(res.statusCode).toBe(400) + const data = JSON.parse(res._getData()) + expect(data.error).toEqual('Parameters are missing.') + }) + it('should return success when the parameters are correct', async () => { + const { reviewer } = testFixtures.users + const { collection } = testFixtures.collections + const { fragment } = testFixtures.fragments + + const res = await requests.sendRequest({ + body, + userId: reviewer.id, + models, + path, + params: { + collectionId: collection.id, + fragmentId: fragment.id, + }, + }) + + expect(res.statusCode).toBe(200) + const data = JSON.parse(res._getData()) + expect(data.userId).toEqual(reviewer.id) + }) +}) diff --git a/packages/component-manuscript-manager/src/tests/helpers/Model.js b/packages/component-manuscript-manager/src/tests/helpers/Model.js new file mode 100644 index 0000000000000000000000000000000000000000..ce46f395b170438c22d8e7c6be2a8e0fd7fabba7 --- /dev/null +++ b/packages/component-manuscript-manager/src/tests/helpers/Model.js @@ -0,0 +1,31 @@ +// const fixtures = require('../fixtures/fixtures') + +const UserMock = require('../mocks/User') + +const notFoundError = new Error() +notFoundError.name = 'NotFoundError' +notFoundError.status = 404 + +const build = fixtures => { + const models = { + User: {}, + Collection: { + find: jest.fn(id => findMock(id, 'collections', fixtures)), + }, + Fragment: { + find: jest.fn(id => findMock(id, 'fragments', fixtures)), + }, + } + UserMock.find = jest.fn(id => findMock(id, 'users', fixtures)) + models.User = UserMock + return models +} + +const findMock = (id, type, fixtures) => { + const foundObj = Object.values(fixtures[type]).find( + fixtureObj => fixtureObj.id === id, + ) + if (foundObj === undefined) return Promise.reject(notFoundError) + return Promise.resolve(foundObj) +} +module.exports = { build } diff --git a/packages/component-manuscript-manager/src/tests/helpers/requests.js b/packages/component-manuscript-manager/src/tests/helpers/requests.js new file mode 100644 index 0000000000000000000000000000000000000000..6696fc7c91db5dff36caf192aae5ac5068fab506 --- /dev/null +++ b/packages/component-manuscript-manager/src/tests/helpers/requests.js @@ -0,0 +1,24 @@ +const httpMocks = require('node-mocks-http') + +const sendRequest = async ({ + body = {}, + userId, + route, + models, + path, + params = {}, + query = {}, +}) => { + const req = httpMocks.createRequest({ + body, + }) + req.user = userId + req.route = route + req.params = params + req.query = query + const res = httpMocks.createResponse() + await require(path)(models)(req, res) + return res +} + +module.exports = { sendRequest } diff --git a/packages/xpub-faraday-server/src/mocks/User.js b/packages/component-manuscript-manager/src/tests/mocks/User.js similarity index 53% rename from packages/xpub-faraday-server/src/mocks/User.js rename to packages/component-manuscript-manager/src/tests/mocks/User.js index ae155c2ca5926d7cf683e95ea884f069fce05f5e..b337c5f31ce5d71eaadd61ccb95fb1f83eef7d83 100644 --- a/packages/xpub-faraday-server/src/mocks/User.js +++ b/packages/component-manuscript-manager/src/tests/mocks/User.js @@ -1,14 +1,21 @@ /* eslint-disable func-names-any */ +const uuid = require('uuid') function User(properties) { this.type = 'user' this.email = properties.email this.username = properties.username this.password = properties.password + this.roles = properties.roles + this.title = properties.title + this.affiliation = properties.affiliation + this.firstName = properties.firstName + this.lastName = properties.lastName + this.admin = properties.admin } User.prototype.save = jest.fn(function saveUser() { - this.id = '111222' + this.id = uuid.v4() return Promise.resolve(this) }) diff --git a/packages/xpub-faraday-server/README.md b/packages/xpub-faraday-server/README.md deleted file mode 100644 index 79c103c5a7c0ace162991862d328954d706018a3..0000000000000000000000000000000000000000 --- a/packages/xpub-faraday-server/README.md +++ /dev/null @@ -1,3 +0,0 @@ -## xPub-faraday-server - -A server package that adds extra features needed by `xpub-faraday` on top of `pubsweet` \ No newline at end of file diff --git a/packages/xpub-faraday-server/index.js b/packages/xpub-faraday-server/index.js deleted file mode 100644 index e0310e3e92600af029a5869f91ff4b3d48a0f8bd..0000000000000000000000000000000000000000 --- a/packages/xpub-faraday-server/index.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - backend: () => app => require('./src/AuthorBackend')(app), -} diff --git a/packages/xpub-faraday-server/package.json b/packages/xpub-faraday-server/package.json deleted file mode 100644 index be17dc271762b45ad8a60361ca7487a252a17fb7..0000000000000000000000000000000000000000 --- a/packages/xpub-faraday-server/package.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "name": "xpub-faraday-server", - "version": "0.0.1", - "description": "xpub configured for faraday", - "license": "MIT", - "files": [ - "src" - ], - "scripts": { - "test": "jest" - }, - "repository": { - "type": "git", - "url": "https://gitlab.coko.foundation/xpub/xpub" - }, - "dependencies": { - "body-parser": "^1.17.2", - "config": "^1.26.1", - "moment": "^2.18.1", - "nodemailer": "^4.0.1", - "uuid": "^3.2.1" - }, - "peerDependencies": { - "@pubsweet/logger": "^0.0.1", - "pubsweet": "^1.1.1", - "pubsweet-client": "^1.1.1", - "pubsweet-server": "^1.0.1" - }, - "devDependencies": { - "jest": "^22.1.1", - "supertest": "^3.0.0" - } -} diff --git a/packages/xpub-faraday-server/src/AuthorBackend.js b/packages/xpub-faraday-server/src/AuthorBackend.js deleted file mode 100644 index d05bb8349c8ae0e5dcc051102e710c9c2196871c..0000000000000000000000000000000000000000 --- a/packages/xpub-faraday-server/src/AuthorBackend.js +++ /dev/null @@ -1,138 +0,0 @@ -const bodyParser = require('body-parser') -const uuid = require('uuid') - -const AuthorBackend = app => { - const authBearer = app.locals.passport.authenticate('bearer', { - session: false, - }) - app.post( - '/api/collections/:collectionId/fragments/:fragmentId/authors', - authBearer, - bodyParser.json(), - async (req, res, next) => { - try { - let fragment = await app.locals.models.Fragment.find( - req.params.fragmentId, - ) - const collection = await app.locals.models.Collection.find( - req.params.collectionId, - ) - - fragment.authors = fragment.authors ? fragment.authors : [] - if (fragment.authors.length > 0) { - const emailAuthors = fragment.authors.filter( - author => author.email === req.body.email, - ) - - if (emailAuthors.length > 0) { - res - .status(400) - .json({ error: 'Author with the same email already exists' }) - return - } - - const submittingAuthors = fragment.authors.filter( - author => - author.isSubmitting === true && - author.isSubmitting === req.body.isSubmitting, - ) - - if (submittingAuthors.length > 0) { - res - .status(400) - .json({ error: 'There can only be one submitting author' }) - return - } - } - req.body.id = uuid.v4() - fragment.authors.push(req.body) - const reqUser = await app.locals.models.User.find(req.user) - if (reqUser.admin === true && req.body.isSubmitting === true) { - try { - // check if author has corresponding user - const user = await app.locals.models.User.findByEmail( - req.body.email, - ) - fragment.owners.push(user.id) - collection.owners.push(user.id) - } catch (e) { - if (e.name === 'NotFoundError') { - // create a new User account - const userBody = { - username: `${req.body.firstName}${ - req.body.lastName - }${Math.floor(Math.random() * 1000)}`, - email: req.body.email, - password: uuid.v4(), - } - let newUser = new app.locals.models.User(userBody) - newUser = await newUser.save() - fragment.owners.push(newUser.id) - collection.owners.push(newUser.id) - } - } - } - fragment = await fragment.save() - await collection.save() - res.status(200).json(fragment.authors[fragment.authors.length - 1]) - } catch (e) { - if (e.name === 'NotFoundError') { - res.status(e.status).json({ error: 'Object not found' }) - return - } - - if (e.name === 'ValidationError') { - res.status(404).json({ error: e.details[0].message }) - return - } - res.status(400).json({ error: 'Something went wrong' }) - } - }, - ) - app.delete( - '/api/fragments/:fragmentId/authors/:authorEmail', - authBearer, - async (req, res, next) => { - const { fragmentId, authorEmail } = req.params - try { - let fragment = await app.locals.models.Fragment.find(fragmentId) - if (fragment.authors === 'undefined') { - res.status(404).json({ error: 'Fragment does not have any authors' }) - return - } - - if (fragment.authors.length === 0) { - res.status(404).json({ error: 'Fragment does not have any authors' }) - return - } - - const newAuthors = fragment.authors.filter( - author => author.email !== authorEmail, - ) - - if (newAuthors.length === fragment.authors.length) { - res.status(404).json({ error: 'Author not found' }) - return - } - - fragment.authors = newAuthors - fragment = await fragment.save() - res.status(204).json({}) - return - } catch (e) { - if (e.name === 'NotFoundError') { - res.status(e.status).json({ error: 'Fragment not found' }) - return - } - - if (e.name === 'ValidationError') { - res.status(404).json({ error: e.details[0].message }) - return - } - res.status(400).json({ error: 'Something went wrong' }) - } - }, - ) -} - -module.exports = AuthorBackend diff --git a/packages/xpub-faraday-server/src/AuthorBackend.test.js b/packages/xpub-faraday-server/src/AuthorBackend.test.js deleted file mode 100644 index ab4ee126dd3f531778bcfb68f9fc19ee43e2cd35..0000000000000000000000000000000000000000 --- a/packages/xpub-faraday-server/src/AuthorBackend.test.js +++ /dev/null @@ -1,210 +0,0 @@ -process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0' -process.env.SUPPRESS_NO_CONFIG_WARNING = true - -const bodyParser = require('body-parser') -const supertest = require('supertest') -const component = require('..') -const express = require('express') -const fixtures = require('./fixtures/fixtures') -const passport = require('passport') -const BearerStrategy = require('passport-http-bearer').Strategy -const cloneDeep = require('lodash/cloneDeep') -const UserMock = require('./mocks/User') -const Fragment = require('./mocks/Fragment') - -function makeApp(collection, fragment, standardUser, existingUser) { - const app = express() - app.use(bodyParser.json()) - - app.use(passport.initialize()) - passport.use( - 'bearer', - new BearerStrategy((token, done) => - done(null, fixtures.users.standardUser, { scope: 'all' }), - ), - ) - app.locals.passport = passport - app.locals.models = { - Fragment: { - find: jest.fn( - () => - fragment instanceof Error - ? Promise.reject(fragment) - : Promise.resolve(fragment), - ), - }, - User: {}, - Collection: { - find: jest.fn( - () => - collection instanceof Error - ? Promise.reject(collection) - : Promise.resolve(collection), - ), - }, - } - - UserMock.find = jest.fn( - () => - standardUser instanceof Error - ? Promise.reject(standardUser) - : Promise.resolve(standardUser), - ) - UserMock.findByEmail = jest.fn( - () => - existingUser instanceof Error - ? Promise.reject(existingUser) - : Promise.resolve(existingUser), - ) - - app.locals.models.User = UserMock - - component.backend()(app) - return supertest(app) -} - -const createAuthorUrl = '/api/collections/123/fragments/123/authors' -const getNewFragment = authors => { - const fragProps = { - type: 'fragment', - fragmentType: 'blogpost', - title: 'Just your regular blogpost', - source: '<blog></blog>', - presentation: '<p></p>', - authors, - owners: [], - } - return new Fragment(fragProps) -} -describe('Author Backend API', () => { - let testFixtures = {} - beforeEach(() => (testFixtures = cloneDeep(fixtures))) - - it('should return an error if fragment is not found', () => { - const error = new Error() - error.name = 'NotFoundError' - error.status = 404 - return makeApp(testFixtures.collections.standardCollection, error) - .post(createAuthorUrl) - .set('Authorization', 'Bearer 123') - .send(testFixtures.authors.standardAuthor) - .expect(404, '{"error":"Object not found"}') - }) - - it('should return an error if collection is not found', () => { - const error = new Error() - error.name = 'NotFoundError' - error.status = 404 - return makeApp(error) - .post(createAuthorUrl) - .set('Authorization', 'Bearer 123') - .send(testFixtures.authors.standardAuthor) - .expect(404, '{"error":"Object not found"}') - }) - - it('should return an error if an author field is invalid', () => { - const error = new Error() - error.name = 'ValidationError' - error.status = 404 - error.details = [] - error.details.push({ message: 'firstName is required' }) - return makeApp(testFixtures.collections.standardCollection, error) - .post(createAuthorUrl) - .set('Authorization', 'Bearer 123') - .send(testFixtures.authors.invalidAuthor) - .expect(404, '{"error":"firstName is required"}') - }) - - it('should return an error if an author already exists with the same email', () => { - const fragment = getNewFragment([testFixtures.authors.standardAuthor]) - return makeApp(testFixtures.collections.standardCollection, fragment) - .post(createAuthorUrl) - .set('Authorization', 'Bearer 123') - .send(testFixtures.authors.standardAuthor) - .expect(400, '{"error":"Author with the same email already exists"}') - }) - - it('should return an error if there already is a submitting author', () => { - const fragment = getNewFragment([testFixtures.authors.standardAuthor]) - return makeApp(testFixtures.collections.standardCollection, fragment) - .post(createAuthorUrl) - .set('Authorization', 'Bearer 123') - .send(testFixtures.authors.newSubmittingAuthor) - .expect(400, '{"error":"There can only be one submitting author"}') - }) - - it('should return success when saving a new author', () => { - const fragment = getNewFragment([]) - - return makeApp( - fixtures.collections.standardCollection, - fragment, - testFixtures.users.standardUser, - ) - .post(createAuthorUrl) - .set('Authorization', 'Bearer 123') - .send(testFixtures.authors.newAuthor) - .expect(200) - .then(() => expect(fragment.save).toHaveBeenCalled()) - }) - - it('should return success when the admin adds a submitting author and the author already has a corresponding user account', () => { - const fragment = getNewFragment([]) - - return makeApp( - testFixtures.collections.standardCollection, - fragment, - testFixtures.users.admin, - testFixtures.users.existingUser, - ) - .post(createAuthorUrl) - .set('Authorization', 'Bearer 123') - .send(testFixtures.authors.standardAuthor) - .expect(200) - .then(() => { - expect(fragment.save).toHaveBeenCalled() - expect( - testFixtures.collections.standardCollection.save, - ).toHaveBeenCalled() - expect(fragment.owners.length).toBeGreaterThan(0) - expect( - testFixtures.collections.standardCollection.owners.length, - ).toBeGreaterThan(1) - expect(fragment.owners[0]).toBe('123987') - expect(testFixtures.collections.standardCollection.owners[1]).toBe( - '123987', - ) - }) - }) - - it('should return success when the admin adds a submitting author and creates a corresponding user account', () => { - const error = new Error() - error.name = 'NotFoundError' - error.status = 404 - const fragment = getNewFragment([]) - return makeApp( - testFixtures.collections.standardCollection, - fragment, - testFixtures.users.admin, - error, - ) - .post(createAuthorUrl) - .set('Authorization', 'Bearer 123') - .send(testFixtures.authors.standardAuthor) - .expect(200) - .then(() => { - expect(fragment.save).toHaveBeenCalled() - expect( - testFixtures.collections.standardCollection.save, - ).toHaveBeenCalled() - expect(fragment.owners.length).toBeGreaterThan(0) - expect( - testFixtures.collections.standardCollection.owners.length, - ).toBeGreaterThan(1) - expect(fragment.owners[0]).toBe('111222') - expect(testFixtures.collections.standardCollection.owners[1]).toBe( - '111222', - ) - }) - }) -}) diff --git a/packages/xpub-faraday-server/src/fixtures/authors.js b/packages/xpub-faraday-server/src/fixtures/authors.js deleted file mode 100644 index 16f8d33738f14ad14e18079c86ebe60318d29f4f..0000000000000000000000000000000000000000 --- a/packages/xpub-faraday-server/src/fixtures/authors.js +++ /dev/null @@ -1,48 +0,0 @@ -const authors = { - standardAuthor: { - firstName: 'Andrew', - middleName: '', - lastName: 'Smith', - email: 'email@email.com', - affiliation: 'University', - country: '', - isCorresponding: false, - isSubmitting: true, - id: '123', - }, - newAuthor: { - firstName: 'Robert', - middleName: '', - lastName: 'Smith', - email: 'email_robert@email.com', - affiliation: 'University', - country: '', - isCorresponding: true, - isSubmitting: false, - id: '456', - }, - invalidAuthor: { - firstName: '', - middleName: '', - lastName: 'Jones', - email: 'email2@email.com', - affiliation: 'University', - country: '', - isCorresponding: false, - isSubmitting: false, - id: '768', - }, - newSubmittingAuthor: { - firstName: 'Andrew', - middleName: '', - lastName: 'Smith', - email: 'email3@email.com', - affiliation: 'University', - country: '', - isCorresponding: false, - isSubmitting: true, - id: '879', - }, -} - -module.exports = authors diff --git a/packages/xpub-faraday-server/src/fixtures/collections.js b/packages/xpub-faraday-server/src/fixtures/collections.js deleted file mode 100644 index 933e47e9884cc670b8508ba0e11f1e375b09c3c4..0000000000000000000000000000000000000000 --- a/packages/xpub-faraday-server/src/fixtures/collections.js +++ /dev/null @@ -1,15 +0,0 @@ -const fragments = require('./fragments') -const users = require('./users') - -const collections = { - standardCollection: { - id: '2c4fb766-a798-4c32-b857-c5d21a2ab331', - title: 'Standard Collection', - type: 'collection', - fragments: [fragments.standardFragment, fragments.adminFragment], - owners: [users.admin.id], - save: jest.fn(), - }, -} - -module.exports = collections diff --git a/packages/xpub-faraday-server/src/fixtures/fragments.js b/packages/xpub-faraday-server/src/fixtures/fragments.js deleted file mode 100644 index ea40534ea148f7975a5c2fa4f93fbc66c85b6183..0000000000000000000000000000000000000000 --- a/packages/xpub-faraday-server/src/fixtures/fragments.js +++ /dev/null @@ -1,26 +0,0 @@ -const authors = require('./authors') - -const fragments = { - standardFragment: { - type: 'fragment', - fragmentType: 'blogpost', - title: 'Just your regular blogpost', - source: '<blog></blog>', - presentation: '<p></p>', - authors: [authors.standardAuthor], - save: () => fragments.standardFragment, - }, - - adminFragment: { - type: 'fragment', - fragmentType: 'blogpost', - title: 'Just your admin blogpost', - source: '<blog></blog>', - presentation: '<p></p>', - authors: [], - save: () => fragments.adminFragment, - owners: [], - }, -} - -module.exports = fragments diff --git a/packages/xpub-faraday-server/src/fixtures/users.js b/packages/xpub-faraday-server/src/fixtures/users.js deleted file mode 100644 index 91fae23a91cd08495b49052193bd4fc2c10600c2..0000000000000000000000000000000000000000 --- a/packages/xpub-faraday-server/src/fixtures/users.js +++ /dev/null @@ -1,25 +0,0 @@ -const users = { - standardUser: { - type: 'user', - username: 'testuser', - email: 'test@example.com', - password: 'test', - }, - admin: { - type: 'user', - username: 'admin', - email: 'admin@example.com', - password: 'test', - admin: true, - id: 'admin123', - }, - existingUser: { - type: 'user', - username: 'authoruser', - email: 'email@email.com', - password: 'test', - id: '123987', - }, -} - -module.exports = users diff --git a/packages/xpub-faraday-server/src/mocks/Fragment.js b/packages/xpub-faraday-server/src/mocks/Fragment.js deleted file mode 100644 index cb4ceba637db457f6f8a6d201d8e3b18b0961cd1..0000000000000000000000000000000000000000 --- a/packages/xpub-faraday-server/src/mocks/Fragment.js +++ /dev/null @@ -1,12 +0,0 @@ -function Fragment(properties) { - this.type = properties.type - this.fragmentType = properties.fragmentType - this.title = properties.title - this.source = properties.source - this.presentation = properties.presentation - this.authors = properties.authors - this.owners = properties.owners - this.save = jest.fn(() => Promise.resolve(this)) -} - -module.exports = Fragment diff --git a/packages/xpub-faraday/config/components.json b/packages/xpub-faraday/config/components.json index 5d3e7cc1f02bb95300d2844f2b9f0f0fb3fa5459..64925934b4897ed17dfe6abd56a33260464a41d2 100644 --- a/packages/xpub-faraday/config/components.json +++ b/packages/xpub-faraday/config/components.json @@ -1,7 +1,6 @@ [ "pubsweet-component-login", "pubsweet-component-signup", - "xpub-faraday-server", "pubsweet-component-wizard", "pubsweet-component-modal", "pubsweet-components-faraday", @@ -9,5 +8,6 @@ "pubsweet-component-invite", "pubsweet-component-user-manager", "pubsweet-component-email", - "pubsweet-component-manuscript" + "pubsweet-component-manuscript", + "pubsweet-component-manuscript-manager" ] diff --git a/packages/xpub-faraday/config/validations.js b/packages/xpub-faraday/config/validations.js index 64cbc2aeacf52796fe9431d769af25725c6027b9..984deecf994c418327dfcd845c88198f08434fe4 100644 --- a/packages/xpub-faraday/config/validations.js +++ b/packages/xpub-faraday/config/validations.js @@ -88,6 +88,22 @@ module.exports = { id: Joi.string().uuid(), }), ), + recommendations: Joi.array().items( + Joi.object({ + id: Joi.string().required(), + userId: Joi.string().required(), + type: Joi.string().required(), + submittedOn: Joi.date(), + recommendation: Joi.string().required(), + comments: Joi.array().items( + Joi.object({ + content: Joi.string().required(), + public: Joi.boolean(), + files: Joi.array(), + }), + ), + }), + ), }, ], user: { diff --git a/packages/xpub-faraday/package.json b/packages/xpub-faraday/package.json index cd567a5f8334e1d320f6c82c7285615649765711..ff555499ca38cc5796e800ee9956bdc6679b190b 100644 --- a/packages/xpub-faraday/package.json +++ b/packages/xpub-faraday/package.json @@ -43,7 +43,6 @@ "winston": "^2.4.0", "xpub-connect": "^0.0.3", "xpub-edit": "^0.0.4", - "xpub-faraday-server": "^0.0.1", "xpub-journal": "^0.0.3", "xpub-selectors": "^0.0.3", "xpub-styleguide": "^0.0.3", diff --git a/yarn.lock b/yarn.lock index cb19989c5c6acf4696673203e95be5e1180961bf..064c05a50e2162612bbcf6b43530bb1b9cd65cf0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2254,7 +2254,7 @@ concat-stream@^1.4.10, concat-stream@^1.5.0, concat-stream@^1.6.0: readable-stream "^2.2.2" typedarray "^0.0.6" -config@^1.21.0, config@^1.26.1, config@^1.26.2: +config@^1.21.0, config@^1.26.2: version "1.29.4" resolved "https://registry.yarnpkg.com/config/-/config-1.29.4.tgz#1b42752ed86b363fc4025960569fd74978862a92" dependencies: @@ -6472,7 +6472,7 @@ nodemailer-ses-transport@^1.5.1: dependencies: aws-sdk "^2.2.36" -nodemailer@^4.0.1, nodemailer@^4.4.2: +nodemailer@^4.4.2: version "4.4.2" resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-4.4.2.tgz#f215fb88e8a1052f9f93083909e116d2b79fc8de" @@ -9857,7 +9857,7 @@ uuid@^2.0.1: version "2.0.3" resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a" -uuid@^3.0.0, uuid@^3.0.1, uuid@^3.1.0, uuid@^3.2.1: +uuid@^3.0.0, uuid@^3.0.1, uuid@^3.1.0: version "3.2.1" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.2.1.tgz#12c528bb9d58d0b9265d9a2f6f0fe8be17ff1f14"