diff --git a/api/serverless.yml b/api/serverless.yml
index b321e993e..9d25232f0 100644
--- a/api/serverless.yml
+++ b/api/serverless.yml
@@ -148,13 +148,6 @@ functions:
seedOpenSearch:
handler: src/lib/testUtils.openSearchSeed
# Publications
- getPublications:
- handler: src/components/publication/routes.getAll
- events:
- - http:
- path: ${self:custom.versions.v1}/publications
- method: GET
- cors: true
getPublication:
handler: src/components/publication/routes.get
events:
@@ -176,27 +169,6 @@ functions:
path: ${self:custom.versions.v1}/publications
method: POST
cors: true
- updateStatus:
- handler: src/components/publication/routes.updateStatus
- events:
- - http:
- path: ${self:custom.versions.v1}/publications/{id}/status/{status}
- method: PUT
- cors: true
- updatePublication:
- handler: src/components/publication/routes.update
- events:
- - http:
- path: ${self:custom.versions.v1}/publications/{id}
- method: PATCH
- cors: true
- deletePublication:
- handler: src/components/publication/routes.deletePublication
- events:
- - http:
- path: ${self:custom.versions.v1}/publications/{id}
- method: DELETE
- cors: true
getPublicationLinks:
handler: src/components/publication/routes.getPublicationLinks
events:
@@ -219,6 +191,49 @@ functions:
path: ${self:custom.versions.v1}/publications/research-topics
method: GET
cors: true
+ getPublicationTopics:
+ handler: src/components/publication/routes.getPublicationTopics
+ events:
+ - http:
+ path: ${self:custom.versions.v1}/publications/{id}/topics
+ method: GET
+ cors: true
+ # Publication Versions
+ getPublicationVersion:
+ handler: src/components/publicationVersion/routes.get
+ events:
+ - http:
+ path: ${self:custom.versions.v1}/publications/{id}/publication-versions/{version} # {version} can be a version id, number or "latest"
+ method: GET
+ cors: true
+ getIndexedPublicationVersions:
+ handler: src/components/publicationVersion/routes.getAll
+ events:
+ - http:
+ path: ${self:custom.versions.v1}/publication-versions
+ method: GET
+ cors: true
+ updatePublicationVersion:
+ handler: src/components/publicationVersion/routes.update
+ events:
+ - http:
+ path: ${self:custom.versions.v1}/publication-versions/{id}
+ method: PATCH
+ cors: true
+ updateStatus:
+ handler: src/components/publicationVersion/routes.updateStatus
+ events:
+ - http:
+ path: ${self:custom.versions.v1}/publication-versions/{id}/status/{status}
+ method: PUT
+ cors: true
+ deleteVersion:
+ handler: src/components/publicationVersion/routes.deleteVersion
+ events:
+ - http:
+ path: ${self:custom.versions.v1}/publication-versions/{id}
+ method: DELETE
+ cors: true
# Users
getUsers:
handler: src/components/user/routes.getAll
@@ -234,11 +249,11 @@ functions:
path: ${self:custom.versions.v1}/users/{id}
method: GET
cors: true
- getUserPublications:
- handler: src/components/user/routes.getPublications
+ getUserPublicationVersions:
+ handler: src/components/user/routes.getPublicationVersions
events:
- http:
- path: ${self:custom.versions.v1}/users/{id}/publications
+ path: ${self:custom.versions.v1}/users/{id}/publication-versions
method: GET
cors: true
@@ -298,91 +313,91 @@ functions:
handler: src/components/coauthor/routes.get
events:
- http:
- path: ${self:custom.versions.v1}/publicationVersions/{id}/coauthors
+ path: ${self:custom.versions.v1}/publication-versions/{id}/coauthors
method: GET
cors: true
updateAll:
handler: src/components/coauthor/routes.updateAll
events:
- http:
- path: ${self:custom.versions.v1}/publicationVersions/{id}/coauthors
+ path: ${self:custom.versions.v1}/publication-versions/{id}/coauthors
method: PUT
cors: true
deleteCoAuthor:
handler: src/components/coauthor/routes.remove
events:
- http:
- path: ${self:custom.versions.v1}/publicationVersions/{id}/coauthors/{coauthor}
+ path: ${self:custom.versions.v1}/publication-versions/{id}/coauthors/{coauthor}
method: DELETE
cors: true
linkCoAuthor:
handler: src/components/coauthor/routes.link
events:
- http:
- path: ${self:custom.versions.v1}/publicationVersions/{id}/link-coauthor
+ path: ${self:custom.versions.v1}/publication-versions/{id}/link-coauthor
method: PATCH
cors: true
updateCoAuthorConfirmation:
handler: src/components/coauthor/routes.updateConfirmation
events:
- http:
- path: ${self:custom.versions.v1}/publicationVersions/{id}/coauthor-confirmation
+ path: ${self:custom.versions.v1}/publication-versions/{id}/coauthor-confirmation
method: PATCH
cors: true
requestApproval:
handler: src/components/coauthor/routes.requestApproval
events:
- http:
- path: ${self:custom.versions.v1}/publicationVersions/{id}/coauthors/request-approval
+ path: ${self:custom.versions.v1}/publication-versions/{id}/coauthors/request-approval
method: PUT
cors: true
sendApprovalReminder:
handler: src/components/coauthor/routes.sendApprovalReminder
events:
- http:
- path: ${self:custom.versions.v1}/publicationVersions/{id}/coauthors/{coauthor}/approval-reminder
+ path: ${self:custom.versions.v1}/publication-versions/{id}/coauthors/{coauthor}/approval-reminder
method: POST
cors: true
getFlag:
handler: src/components/flag/routes.get
events:
- http:
- path: ${self:custom.versions.v1}/flag/{id}
+ path: ${self:custom.versions.v1}/flags/{id}
method: GET
cors: true
createFlag:
handler: src/components/flag/routes.createFlag
events:
- http:
- path: ${self:custom.versions.v1}/publications/{id}/flag
+ path: ${self:custom.versions.v1}/publications/{id}/flags
method: POST
cors: true
createFlagComment:
handler: src/components/flag/routes.createFlagComment
events:
- http:
- path: ${self:custom.versions.v1}/flag/{id}/comment
+ path: ${self:custom.versions.v1}/flags/{id}/comment
method: POST
cors: true
resolveFlag:
handler: src/components/flag/routes.resolveFlag
events:
- http:
- path: ${self:custom.versions.v1}/flag/{id}/resolve
+ path: ${self:custom.versions.v1}/flags/{id}/resolve
method: POST
cors: true
getPublicationFlags:
handler: src/components/flag/routes.getPublicationFlags
events:
- http:
- path: ${self:custom.versions.v1}/flag/publication/{id}
+ path: ${self:custom.versions.v1}/publications/{id}/flags
method: GET
cors: true
getUserFlags:
handler: src/components/flag/routes.getUserFlags
events:
- http:
- path: ${self:custom.versions.v1}/flag/user/{id}
+ path: ${self:custom.versions.v1}/user/{id}/flags
method: GET
cors: true
# Images
@@ -448,14 +463,14 @@ functions:
handler: src/components/funder/routes.create
events:
- http:
- path: ${self:custom.versions.v1}/publications/{id}/funders
+ path: ${self:custom.versions.v1}/publication-versions/{id}/funders
method: POST
cors: true
deleteFunder:
handler: src/components/funder/routes.destroy
events:
- http:
- path: ${self:custom.versions.v1}/publications/{id}/funders/{funder}
+ path: ${self:custom.versions.v1}/publication-versions/{id}/funders/{funder}
method: DELETE
cors: true
# References
@@ -463,14 +478,14 @@ functions:
handler: src/components/reference/routes.get
events:
- http:
- path: ${self:custom.versions.v1}/publicationVersions/{id}/reference
+ path: ${self:custom.versions.v1}/publication-versions/{id}/references
method: get
cors: true
updateAllReference:
handler: src/components/reference/routes.updateAll
events:
- http:
- path: ${self:custom.versions.v1}/publicationVersions/{id}/reference
+ path: ${self:custom.versions.v1}/publication-versions/{id}/references
method: put
cors: true
@@ -479,7 +494,7 @@ functions:
handler: src/components/affiliations/routes.updateAffiliations
events:
- http:
- path: ${self:custom.versions.v1}/publicationVersions/{id}/my-affiliations
+ path: ${self:custom.versions.v1}/publication-versions/{id}/my-affiliations
method: PUT
cors: true
@@ -520,4 +535,3 @@ functions:
path: ${self:custom.versions.v1}/publications/{id}/topics
method: PUT
cors: true
-
diff --git a/api/src/components/affiliations/__tests__/updateAffiliations.test.ts b/api/src/components/affiliations/__tests__/updateAffiliations.test.ts
index c310c1ec6..c1b2c679c 100644
--- a/api/src/components/affiliations/__tests__/updateAffiliations.test.ts
+++ b/api/src/components/affiliations/__tests__/updateAffiliations.test.ts
@@ -69,7 +69,7 @@ describe('update coAuthor affiliations per publication', () => {
test('Corresponding author can add an affiliation to their DRAFT publication', async () => {
const updateAffiliationsResponse = await testUtils.agent
- .put('/publicationVersions/publication-problem-draft-v1/my-affiliations')
+ .put('/publication-versions/publication-problem-draft-v1/my-affiliations')
.query({ apiKey: '000000005' })
.send({
affiliations: orcidTestAffiliations,
@@ -82,7 +82,7 @@ describe('update coAuthor affiliations per publication', () => {
test('Corresponding author needs to add affiliations if not independent before locking the publication', async () => {
const updateAffiliationsResponse = await testUtils.agent
- .put('/publicationVersions/publication-problem-draft-v1/my-affiliations')
+ .put('/publication-versions/publication-problem-draft-v1/my-affiliations')
.query({ apiKey: '000000005' })
.send({
affiliations: [],
@@ -92,7 +92,7 @@ describe('update coAuthor affiliations per publication', () => {
expect(updateAffiliationsResponse.status).toEqual(200);
const updateStatusResponse = await testUtils.agent
- .put('/publications/publication-problem-draft/status/LOCKED')
+ .put('/publication-versions/publication-problem-draft-v1/status/LOCKED')
.query({ apiKey: '000000005' })
.send();
@@ -104,7 +104,7 @@ describe('update coAuthor affiliations per publication', () => {
test('Author needs to fill out affiliations if the publication is LOCKED', async () => {
const updateAffiliationsResponse = await testUtils.agent
- .put('/publicationVersions/publication-problem-locked-v1/my-affiliations')
+ .put('/publication-versions/publication-problem-locked-v1/my-affiliations')
.query({ apiKey: '000000005' })
.send({
affiliations: [],
@@ -117,7 +117,7 @@ describe('update coAuthor affiliations per publication', () => {
test('User cannot add affiliations if they are not part of the publication', async () => {
const updateAffiliationsResponse = await testUtils.agent
- .put('/publicationVersions/publication-problem-draft-v1/my-affiliations')
+ .put('/publication-versions/publication-problem-draft-v1/my-affiliations')
.query({ apiKey: '123456789' })
.send({
affiliations: [],
@@ -132,7 +132,7 @@ describe('update coAuthor affiliations per publication', () => {
test('User cannot add affiliations if the publication is LIVE', async () => {
const updateAffiliationsResponse = await testUtils.agent
- .put('/publicationVersions/publication-problem-live-v1/my-affiliations')
+ .put('/publication-versions/publication-problem-live-v1/my-affiliations')
.query({ apiKey: '123456789' })
.send({
affiliations: [],
@@ -145,7 +145,7 @@ describe('update coAuthor affiliations per publication', () => {
test('Only corresponding author can update his affiliations while the publication is DRAFT', async () => {
const updateAffiliationsResponse = await testUtils.agent
- .put('/publicationVersions/publication-problem-draft-v1/my-affiliations')
+ .put('/publication-versions/publication-problem-draft-v1/my-affiliations')
.query({ apiKey: '000000005' })
.send({
affiliations: orcidTestAffiliations,
@@ -156,7 +156,7 @@ describe('update coAuthor affiliations per publication', () => {
expect(updateAffiliationsResponse.body.message).toEqual('Successfully updated affiliations.');
const updateAffiliationsResponse2 = await testUtils.agent
- .put('/publicationVersions/publication-problem-draft-v1/my-affiliations')
+ .put('/publication-versions/publication-problem-draft-v1/my-affiliations')
.query({ apiKey: '000000006' })
.send({
affiliations: orcidTestAffiliations,
@@ -171,7 +171,7 @@ describe('update coAuthor affiliations per publication', () => {
test('Cannot add duplicate affiliations', async () => {
const updateAffiliationsResponse = await testUtils.agent
- .put('/publicationVersions/publication-problem-draft-v1/my-affiliations')
+ .put('/publication-versions/publication-problem-draft-v1/my-affiliations')
.query({ apiKey: '000000005' })
.send({
affiliations: orcidTestAffiliations.concat(orcidTestAffiliations),
diff --git a/api/src/components/affiliations/controller.ts b/api/src/components/affiliations/controller.ts
index cea466723..d14a3d16c 100644
--- a/api/src/components/affiliations/controller.ts
+++ b/api/src/components/affiliations/controller.ts
@@ -16,7 +16,7 @@ export const updateAffiliations = async (
const affiliations = event.body.affiliations;
const publicationVersionId = event.pathParameters.id;
// Get publication version
- const version = await publicationVersionService.get(publicationVersionId);
+ const version = await publicationVersionService.getById(publicationVersionId);
// Check that the version exists
if (!version) {
diff --git a/api/src/components/coauthor/__tests__/createCoAuthor.test.ts b/api/src/components/coauthor/__tests__/createCoAuthor.test.ts
index f10377fb0..87418e7b3 100644
--- a/api/src/components/coauthor/__tests__/createCoAuthor.test.ts
+++ b/api/src/components/coauthor/__tests__/createCoAuthor.test.ts
@@ -9,7 +9,7 @@ describe('create coauthor', () => {
test('Update co-authors for a specific publication version', async () => {
const coauthor = await testUtils.agent
- .put('/publicationVersions/publication-problem-draft-v1/coauthors')
+ .put('/publication-versions/publication-problem-draft-v1/coauthors')
.query({ apiKey: '000000005' })
.send([
{
@@ -34,7 +34,7 @@ describe('create coauthor', () => {
test('Cannot create a co-author with duplicate email', async () => {
const coauthor = await testUtils.agent
- .put('/publicationVersions/publication-problem-draft-v1/coauthors')
+ .put('/publication-versions/publication-problem-draft-v1/coauthors')
.query({ apiKey: '000000005' })
.send([
{
@@ -75,7 +75,7 @@ describe('create coauthor', () => {
test('Cannot create a co-author record if the user is not the author of the publication version', async () => {
const coauthor = await testUtils.agent
- .put('/publicationVersions/publication-problem-draft-v1/coauthors')
+ .put('/publication-versions/publication-problem-draft-v1/coauthors')
.query({ apiKey: '987654321' })
.send([
{
@@ -93,7 +93,7 @@ describe('create coauthor', () => {
test('Cannot create a co-author record on a publication version that does not exist', async () => {
const coauthor = await testUtils.agent
- .put('/publicationVersions/non-existent-publication-version/coauthors')
+ .put('/publication-versions/non-existent-publication-version/coauthors')
.query({ apiKey: '123456789' })
.send([
{
@@ -111,7 +111,7 @@ describe('create coauthor', () => {
test('Cannot create a co-author record on a publication version that is live', async () => {
const coauthor = await testUtils.agent
- .put('/publicationVersions/publication-problem-live-v1/coauthors')
+ .put('/publication-versions/publication-problem-live-v1/coauthors')
.query({ apiKey: '123456789' })
.send([
{
diff --git a/api/src/components/coauthor/__tests__/deleteCoAuthor.test.ts b/api/src/components/coauthor/__tests__/deleteCoAuthor.test.ts
index a8bb2b713..0413b4575 100644
--- a/api/src/components/coauthor/__tests__/deleteCoAuthor.test.ts
+++ b/api/src/components/coauthor/__tests__/deleteCoAuthor.test.ts
@@ -8,7 +8,7 @@ describe('Delete co-author', () => {
test('Delete a co-author', async () => {
const deleteCoAuthor = await testUtils.agent
- .delete('/publicationVersions/publication-problem-draft-v1/coauthors/coauthor-test-user-6-problem-draft')
+ .delete('/publication-versions/publication-problem-draft-v1/coauthors/coauthor-test-user-6-problem-draft')
.query({ apiKey: '000000005' });
expect(deleteCoAuthor.status).toEqual(200);
@@ -16,7 +16,7 @@ describe('Delete co-author', () => {
test('Cannot Delete a co-author without a valid id/coauthor has not been added to this publication version', async () => {
const deleteCoAuthor = await testUtils.agent
- .delete('/publicationVersions/publication-problem-draft-v1/coauthors/invalid-id')
+ .delete('/publication-versions/publication-problem-draft-v1/coauthors/invalid-id')
.query({ apiKey: '000000005' });
expect(deleteCoAuthor.status).toEqual(404);
@@ -24,7 +24,7 @@ describe('Delete co-author', () => {
test('Cannot Delete a co-author record if the user is not the author of a publication version', async () => {
const deleteCoAuthor = await testUtils.agent
- .delete('/publicationVersions/publication-problem-draft-v1/coauthors/coauthor-test-user-5-problem-draft')
+ .delete('/publication-versions/publication-problem-draft-v1/coauthors/coauthor-test-user-5-problem-draft')
.query({ apiKey: '987654321' });
expect(deleteCoAuthor.status).toEqual(403);
@@ -32,7 +32,7 @@ describe('Delete co-author', () => {
test('Cannot Delete a co-author record if the publication version is live', async () => {
const deleteCoAuthor = await testUtils.agent
- .delete('/publicationVersions/publication-problem-draft-v1/coauthors/co-author-test-user-6-problem-live')
+ .delete('/publication-versions/publication-problem-draft-v1/coauthors/co-author-test-user-6-problem-live')
.query({ apiKey: '000000005' });
expect(deleteCoAuthor.status).toEqual(404);
@@ -40,7 +40,7 @@ describe('Delete co-author', () => {
test('Cannot Delete a co-author record on a publication version that does not exist', async () => {
const deleteCoAuthor = await testUtils.agent
- .delete('/publicationVersions/non-existent-publication-v1/coauthors/coauthor-test-user-5-problem-draft')
+ .delete('/publication-versions/non-existent-publication-v1/coauthors/coauthor-test-user-5-problem-draft')
.query({ apiKey: '123456789' });
expect(deleteCoAuthor.status).toEqual(404);
diff --git a/api/src/components/coauthor/__tests__/linkCoAuthor.test.ts b/api/src/components/coauthor/__tests__/linkCoAuthor.test.ts
index cabbf2ed8..b2cd0920b 100644
--- a/api/src/components/coauthor/__tests__/linkCoAuthor.test.ts
+++ b/api/src/components/coauthor/__tests__/linkCoAuthor.test.ts
@@ -9,7 +9,7 @@ describe('Link co-author', () => {
test('Link a co-author to a publication version (allow)', async () => {
const link = await testUtils.agent
- .patch('/publicationVersions/publication-hypothesis-draft-v1/link-coauthor')
+ .patch('/publication-versions/publication-hypothesis-draft-v1/link-coauthor')
.query({ apiKey: '000000007' })
.send({
email: 'test-user-7@jisc.ac.uk',
@@ -27,7 +27,7 @@ describe('Link co-author', () => {
test('Link a co-author to a publication version (do not allow) with authentication', async () => {
const link = await testUtils.agent
- .patch('/publicationVersions/publication-problem-draft-v1/link-coauthor')
+ .patch('/publication-versions/publication-problem-draft-v1/link-coauthor')
.query({ apiKey: '987654321' })
.send({
email: 'test-user-7@jisc.ac.uk',
@@ -45,7 +45,7 @@ describe('Link co-author', () => {
test('Link a co-author to a publication version (do not allow) without authentication', async () => {
const link = await testUtils.agent
- .patch('/publicationVersions/publication-problem-draft-v1/link-coauthor')
+ .patch('/publication-versions/publication-problem-draft-v1/link-coauthor')
.query({ apiKey: '987654321' })
.send({
email: 'test-user-7@jisc.ac.uk',
@@ -58,7 +58,7 @@ describe('Link co-author', () => {
test('Cannot link as co-author if you are the creator', async () => {
const link = await testUtils.agent
- .patch('/publicationVersions/publication-problem-draft-v1/link-coauthor')
+ .patch('/publication-versions/publication-problem-draft-v1/link-coauthor')
.query({ apiKey: '000000005' })
.send({
email: 'test-user-7@jisc.ac.uk',
@@ -71,7 +71,7 @@ describe('Link co-author', () => {
test('Cannot link co-author if user has already been linked as another co-author', async () => {
const link = await testUtils.agent
- .patch('/publicationVersions/publication-problem-draft-v1/link-coauthor')
+ .patch('/publication-versions/publication-problem-draft-v1/link-coauthor')
.query({ apiKey: '000000006' })
.send({
email: 'test-user-6@jisc.ac.uk',
@@ -84,7 +84,7 @@ describe('Link co-author', () => {
test('Cannot override co-authorship', async () => {
const link = await testUtils.agent
- .patch('/publicationVersions/publication-problem-draft-v1/link-coauthor')
+ .patch('/publication-versions/publication-problem-draft-v1/link-coauthor')
.query({ apiKey: '987654321' })
.send({
email: 'test-user-6@jisc.ac.uk',
@@ -99,7 +99,7 @@ describe('Link co-author', () => {
test('Cannot link co-author with a different email address', async () => {
// trying to accept invitation with a user which is not a co-author
const response = await testUtils.agent
- .patch('/publicationVersions/publication-problem-draft-v1/link-coauthor')
+ .patch('/publication-versions/publication-problem-draft-v1/link-coauthor')
.query({ apiKey: '000000004' })
.send({
email: 'test-user-7@jisc.ac.uk',
@@ -112,7 +112,7 @@ describe('Link co-author', () => {
// trying to accept invitation with a different co-author account
const response2 = await testUtils.agent
- .patch('/publicationVersions/publication-problem-draft-v1/link-coauthor')
+ .patch('/publication-versions/publication-problem-draft-v1/link-coauthor')
.query({ apiKey: '000000008' })
.send({
email: 'test-user-7@jisc.ac.uk',
diff --git a/api/src/components/coauthor/__tests__/requestApproval.test.ts b/api/src/components/coauthor/__tests__/requestApproval.test.ts
index 0c12b5ae6..6bed0a8a4 100644
--- a/api/src/components/coauthor/__tests__/requestApproval.test.ts
+++ b/api/src/components/coauthor/__tests__/requestApproval.test.ts
@@ -8,13 +8,13 @@ describe('Request co-authors approvals', () => {
test('Can request approvals only if the publication version is DRAFT or LOCKED', async () => {
const draftPublicationVersionResponse = await testUtils.agent
- .put('/publicationVersions/publication-problem-draft-v1/coauthors/request-approval')
+ .put('/publication-versions/publication-problem-draft-v1/coauthors/request-approval')
.query({ apiKey: '000000005' });
expect(draftPublicationVersionResponse.status).toEqual(200);
const lockedPublicationVersionResponse = await testUtils.agent
- .put('/publicationVersions/publication-problem-locked-v1/coauthors/request-approval')
+ .put('/publication-versions/publication-problem-locked-v1/coauthors/request-approval')
.query({ apiKey: '000000005' });
expect(lockedPublicationVersionResponse.status).toEqual(200);
@@ -22,7 +22,7 @@ describe('Request co-authors approvals', () => {
test('Cannot request approvals for a LIVE publication version', async () => {
const livePublicationVersionResponse = await testUtils.agent
- .put('/publicationVersions/publication-problem-live-v1/coauthors/request-approval')
+ .put('/publication-versions/publication-problem-live-v1/coauthors/request-approval')
.query({ apiKey: '123456789' });
expect(livePublicationVersionResponse.status).toEqual(403);
@@ -30,7 +30,7 @@ describe('Request co-authors approvals', () => {
test('Cannot request approvals if user is not the creator', async () => {
const draftPublicationVersionResponse = await testUtils.agent
- .put('/publicationVersions/publication-problem-draft-v1/coauthors/request-approval')
+ .put('/publication-versions/publication-problem-draft-v1/coauthors/request-approval')
.query({ apiKey: '000000006' });
expect(draftPublicationVersionResponse.status).toEqual(403);
@@ -38,7 +38,7 @@ describe('Request co-authors approvals', () => {
test('Cannot request approvals if publication has no-coauthors', async () => {
const draftPublicationVersionResponse = await testUtils.agent
- .put('/publicationVersions/publication-2-v1/coauthors/request-approval')
+ .put('/publication-versions/publication-2-v1/coauthors/request-approval')
.query({ apiKey: '987654321' });
expect(draftPublicationVersionResponse.status).toEqual(403);
diff --git a/api/src/components/coauthor/__tests__/sendApprovalReminder.test.ts b/api/src/components/coauthor/__tests__/sendApprovalReminder.test.ts
index 8e9447348..5209e7009 100644
--- a/api/src/components/coauthor/__tests__/sendApprovalReminder.test.ts
+++ b/api/src/components/coauthor/__tests__/sendApprovalReminder.test.ts
@@ -10,7 +10,7 @@ describe('Request co-authors approvals', () => {
// test LIVE
const livePublicationVersionResponse = await testUtils.agent
.post(
- '/publicationVersions/publication-problem-live-v1/coauthors/coauthor-test-user-6-problem-live/approval-reminder'
+ '/publication-versions/publication-problem-live-v1/coauthors/coauthor-test-user-6-problem-live/approval-reminder'
)
.query({ apiKey: '123456789' });
@@ -22,7 +22,7 @@ describe('Request co-authors approvals', () => {
// test DRAFT
const draftPublicationVersionResponse = await testUtils.agent
.post(
- '/publicationVersions/publication-problem-draft-v1/coauthors/coauthor-test-user-7-problem-draft/approval-reminder'
+ '/publication-versions/publication-problem-draft-v1/coauthors/coauthor-test-user-7-problem-draft/approval-reminder'
)
.query({ apiKey: '000000005' });
@@ -34,7 +34,7 @@ describe('Request co-authors approvals', () => {
// test LOCKED
const lockedPublicationVersionResponse = await testUtils.agent
.post(
- '/publicationVersions/publication-problem-locked-v1/coauthors/coauthor-test-user-7-problem-locked/approval-reminder'
+ '/publication-versions/publication-problem-locked-v1/coauthors/coauthor-test-user-7-problem-locked/approval-reminder'
)
.query({ apiKey: '000000005' });
@@ -45,7 +45,7 @@ describe('Request co-authors approvals', () => {
test('Cannot send approval reminder if a co-author already approved', async () => {
const response = await testUtils.agent
.post(
- '/publicationVersions/locked-publication-problem-confirmed-co-authors-v1/coauthors/test-user-2/approval-reminder'
+ '/publication-versions/locked-publication-problem-confirmed-co-authors-v1/coauthors/test-user-2/approval-reminder'
)
.query({ apiKey: '123456789' });
@@ -56,7 +56,7 @@ describe('Request co-authors approvals', () => {
test('Cannot send approval reminder to a user which is not a co-author', async () => {
const response = await testUtils.agent
.post(
- '/publicationVersions/locked-publication-problem-confirmed-co-authors-v1/coauthors/test-user-22/approval-reminder'
+ '/publication-versions/locked-publication-problem-confirmed-co-authors-v1/coauthors/test-user-22/approval-reminder'
)
.query({ apiKey: '123456789' });
@@ -67,7 +67,7 @@ describe('Request co-authors approvals', () => {
test('Cannot send approval reminder to the same co-author twice', async () => {
const response1 = await testUtils.agent
.post(
- '/publicationVersions/publication-problem-locked-v1/coauthors/coauthor-test-user-7-problem-locked/approval-reminder'
+ '/publication-versions/publication-problem-locked-v1/coauthors/coauthor-test-user-7-problem-locked/approval-reminder'
)
.query({ apiKey: '000000005' });
@@ -76,7 +76,7 @@ describe('Request co-authors approvals', () => {
const response2 = await testUtils.agent
.post(
- '/publicationVersions/publication-problem-locked-v1/coauthors/coauthor-test-user-7-problem-locked/approval-reminder'
+ '/publication-versions/publication-problem-locked-v1/coauthors/coauthor-test-user-7-problem-locked/approval-reminder'
)
.query({ apiKey: '000000005' });
diff --git a/api/src/components/coauthor/__tests__/updateCoAuthor.test.ts b/api/src/components/coauthor/__tests__/updateCoAuthor.test.ts
index fc8867a3d..63113a56b 100644
--- a/api/src/components/coauthor/__tests__/updateCoAuthor.test.ts
+++ b/api/src/components/coauthor/__tests__/updateCoAuthor.test.ts
@@ -8,7 +8,7 @@ describe('Update co-author status', () => {
test('Co-author updates their status to true', async () => {
const coAuthor = await testUtils.agent
- .patch('/publicationVersions/publication-problem-locked-v1/coauthor-confirmation')
+ .patch('/publication-versions/publication-problem-locked-v1/coauthor-confirmation')
.query({ apiKey: '000000006' })
.send({
confirm: true
@@ -19,7 +19,7 @@ describe('Update co-author status', () => {
test('Co-author updates their status to false', async () => {
const coAuthor = await testUtils.agent
- .patch('/publicationVersions/publication-problem-locked-v1/coauthor-confirmation')
+ .patch('/publication-versions/publication-problem-locked-v1/coauthor-confirmation')
.query({ apiKey: '000000006' })
.send({
confirm: false
@@ -30,7 +30,7 @@ describe('Update co-author status', () => {
test('Cannot update co-author if not a co-author (1)', async () => {
const coAuthor = await testUtils.agent
- .patch('/publicationVersions/publication-problem-locked-v1/coauthor-confirmation')
+ .patch('/publication-versions/publication-problem-locked-v1/coauthor-confirmation')
.query({ apiKey: '123456789' })
.send({
confirm: true
@@ -41,7 +41,7 @@ describe('Update co-author status', () => {
test('Cannot update co-author if not a co-author (2)', async () => {
const coAuthor = await testUtils.agent
- .patch('/publicationVersions/publication-problem-locked-v1/coauthor-confirmation')
+ .patch('/publication-versions/publication-problem-locked-v1/coauthor-confirmation')
.query({ apiKey: '987654321' })
.send({
confirm: true
diff --git a/api/src/components/coauthor/controller.ts b/api/src/components/coauthor/controller.ts
index 614d56df6..85e56c110 100644
--- a/api/src/components/coauthor/controller.ts
+++ b/api/src/components/coauthor/controller.ts
@@ -2,16 +2,13 @@ import * as coAuthorService from 'coauthor/service';
import * as I from 'interface';
import * as email from 'email';
import * as response from 'lib/response';
-import * as publicationService from 'publication/service';
import * as publicationVersionService from 'publicationVersion/service';
export const get = async (
event: I.AuthenticatedAPIRequest
): Promise => {
try {
- const versionId = event.pathParameters.id;
-
- const version = await publicationVersionService.get(versionId);
+ const version = await publicationVersionService.getById(event.pathParameters.id);
if (!version) {
return response.json(404, {
@@ -50,8 +47,7 @@ export const updateAll = async (
event: I.AuthenticatedAPIRequest
): Promise => {
try {
- const versionId = event.pathParameters.id;
- const version = await publicationVersionService.get(versionId);
+ const version = await publicationVersionService.getById(event.pathParameters.id);
// Does the publication version exist?
if (!version) {
@@ -130,7 +126,7 @@ export const remove = async (
event: I.AuthenticatedAPIRequest
): Promise => {
try {
- const version = await publicationVersionService.get(event.pathParameters.id);
+ const version = await publicationVersionService.getById(event.pathParameters.id);
// Does the publication version exist?
if (!version) {
@@ -184,7 +180,7 @@ export const link = async (
event: I.OptionalAuthenticatedAPIRequest
): Promise => {
try {
- const version = await publicationVersionService.get(event.pathParameters.id);
+ const version = await publicationVersionService.getById(event.pathParameters.id);
if (!version) {
return response.json(404, {
@@ -296,7 +292,7 @@ export const updateConfirmation = async (
event: I.AuthenticatedAPIRequest
): Promise => {
try {
- const version = await publicationVersionService.get(event.pathParameters.id);
+ const version = await publicationVersionService.getById(event.pathParameters.id);
// Does the publication version exist?
if (!version) {
@@ -375,7 +371,7 @@ export const requestApproval = async (
): Promise => {
try {
const versionId = event.pathParameters.id;
- const version = await publicationVersionService.get(versionId);
+ const version = await publicationVersionService.getById(versionId);
if (!version) {
return response.json(404, { message: 'Publication version not found' });
@@ -398,35 +394,31 @@ export const requestApproval = async (
}
if (version.currentStatus === 'DRAFT') {
- const publication = await publicationService.getWithVersion(version.versionOf, version.versionNumber);
+ const isReadyToRequestApprovals = await publicationVersionService.checkIsReadyToRequestApprovals(version);
- if (publication) {
- if (!publicationService.isReadyToRequestApproval(publication)) {
- return response.json(403, {
- message:
- 'Approval emails cannot be sent because the publication is not ready to be LOCKED. Make sure all fields are filled in.'
- });
- }
+ if (!isReadyToRequestApprovals) {
+ return response.json(403, {
+ message:
+ 'Approval emails cannot be sent because the publication is not ready to be LOCKED. Make sure all fields are filled in.'
+ });
+ }
+
+ // check if this version was LOCKED before
+ if (version.publicationStatus.some(({ status }) => status === 'LOCKED')) {
+ // notify linked co-authors about changes
+ const linkedCoAuthors = version.coAuthors.filter(
+ (author) => author.linkedUser && author.linkedUser !== version.createdBy
+ );
- // check if this version was LOCKED before
- if (version.publicationStatus.some(({ status }) => status === 'LOCKED')) {
- // notify linked co-authors about changes
- const linkedCoAuthors = version.coAuthors.filter(
- (author) => author.linkedUser && author.linkedUser !== version.createdBy
- );
-
- for (const linkedCoAuthor of linkedCoAuthors) {
- await email.notifyCoAuthorsAboutChanges({
- coAuthor: { email: linkedCoAuthor.email },
- publication: {
- title: version.title || '',
- url: `${process.env.BASE_URL}/publications/${publication.id}`
- }
- });
- }
+ for (const linkedCoAuthor of linkedCoAuthors) {
+ await email.notifyCoAuthorsAboutChanges({
+ coAuthor: { email: linkedCoAuthor.email },
+ publication: {
+ title: version.title || '',
+ url: `${process.env.BASE_URL}/publications/${version.versionOf}`
+ }
+ });
}
- } else {
- throw Error('Could not get details of publication');
}
}
@@ -463,7 +455,7 @@ export const sendApprovalReminder = async (
): Promise => {
const { coauthor, id } = event.pathParameters;
- const version = await publicationVersionService.get(id);
+ const version = await publicationVersionService.getById(id);
const author = await coAuthorService.get(coauthor);
if (!version) {
diff --git a/api/src/components/flag/__tests__/createFlag.test.ts b/api/src/components/flag/__tests__/createFlag.test.ts
index efab652c0..d74881f39 100644
--- a/api/src/components/flag/__tests__/createFlag.test.ts
+++ b/api/src/components/flag/__tests__/createFlag.test.ts
@@ -8,7 +8,7 @@ describe('Create flags on publications', () => {
test('User can create a valid flag on LIVE publication they did not create', async () => {
const createFlag = await testUtils.agent
- .post('/publications/publication-interpretation-live/flag')
+ .post('/publications/publication-interpretation-live/flags')
.query({
apiKey: '987654321'
})
@@ -22,7 +22,7 @@ describe('Create flags on publications', () => {
test('User cannot create a valid flag on LIVE publication they created', async () => {
const createFlag = await testUtils.agent
- .post('/publications/publication-interpretation-live/flag')
+ .post('/publications/publication-interpretation-live/flags')
.query({
apiKey: '123456789'
})
@@ -36,7 +36,7 @@ describe('Create flags on publications', () => {
test('User cannot create a invalid flag on LIVE publication they did not create', async () => {
const createFlag = await testUtils.agent
- .post('/publications/publication-interpretation-live/flag')
+ .post('/publications/publication-interpretation-live/flags')
.query({
apiKey: '987654321'
})
@@ -50,7 +50,7 @@ describe('Create flags on publications', () => {
test('User cannot create a duplicate flag for an unresolved flag', async () => {
const createFlag = await testUtils.agent
- .post('/publications/publication-interpretation-live/flag')
+ .post('/publications/publication-interpretation-live/flags')
.query({
apiKey: '987654321'
})
@@ -62,7 +62,7 @@ describe('Create flags on publications', () => {
expect(createFlag.status).toEqual(200);
const createFlagAttempt2 = await testUtils.agent
- .post('/publications/publication-interpretation-live/flag')
+ .post('/publications/publication-interpretation-live/flags')
.query({
apiKey: '987654321'
})
@@ -76,7 +76,7 @@ describe('Create flags on publications', () => {
test('Cannot create a valid flag for a publication that is in DRAFT', async () => {
const createFlag = await testUtils.agent
- .post('/publications/publication-interpretation-draft/flag')
+ .post('/publications/publication-interpretation-draft/flags')
.query({
apiKey: '987654321'
})
@@ -90,7 +90,7 @@ describe('Create flags on publications', () => {
test('User can create 2 differente flags for the same publication that they did not create', async () => {
const createFlag = await testUtils.agent
- .post('/publications/publication-interpretation-live/flag')
+ .post('/publications/publication-interpretation-live/flags')
.query({
apiKey: '987654321'
})
@@ -102,7 +102,7 @@ describe('Create flags on publications', () => {
expect(createFlag.status).toEqual(200);
const createFlagAttempt2 = await testUtils.agent
- .post('/publications/publication-interpretation-live/flag')
+ .post('/publications/publication-interpretation-live/flags')
.query({
apiKey: '987654321'
})
diff --git a/api/src/components/flag/__tests__/createFlagComment.test.ts b/api/src/components/flag/__tests__/createFlagComment.test.ts
index 141cb041e..cdd39a9a7 100644
--- a/api/src/components/flag/__tests__/createFlagComment.test.ts
+++ b/api/src/components/flag/__tests__/createFlagComment.test.ts
@@ -8,7 +8,7 @@ describe('Create flags comments on a flag', () => {
test('User who created the flag can leave comments', async () => {
const createFlagComment = await testUtils.agent
- .post('/flag/publication-problem-live-flag/comment')
+ .post('/flags/publication-problem-live-flag/comment')
.query({
apiKey: '987654321'
})
@@ -21,7 +21,7 @@ describe('Create flags comments on a flag', () => {
test('Owner of the publication can leave comments', async () => {
const createFlagComment = await testUtils.agent
- .post('/flag/publication-problem-live-flag/comment')
+ .post('/flags/publication-problem-live-flag/comment')
.query({
apiKey: '123456789'
})
@@ -33,7 +33,7 @@ describe('Create flags comments on a flag', () => {
test('You cannot leave a comment on an resolved flag', async () => {
const createFlagComment = await testUtils.agent
- .post('/flag/publication-hypothesis-live/comment')
+ .post('/flags/publication-hypothesis-live/comment')
.query({
apiKey: '123456789'
})
@@ -46,7 +46,7 @@ describe('Create flags comments on a flag', () => {
test('You cannot leave a comment on an un-flagged publication', async () => {
const createFlagComment = await testUtils.agent
- .post('/flag/publication-analysis-live/comment')
+ .post('/flags/publication-analysis-live/comment')
.query({
apiKey: '123456789'
})
@@ -59,7 +59,7 @@ describe('Create flags comments on a flag', () => {
test('You can only leave a comment if you are either the author of the publication or the flagger', async () => {
const createFlagComment = await testUtils.agent
- .post('/flag/publication-problem-live-flag/comment')
+ .post('/flags/publication-problem-live-flag/comment')
.query({
apiKey: '000000003'
})
@@ -72,7 +72,7 @@ describe('Create flags comments on a flag', () => {
test('The body of the request is invalid', async () => {
const createFlagComment = await testUtils.agent
- .post('/flag/publication-problem-live-flag/comment')
+ .post('/flags/publication-problem-live-flag/comment')
.query({
apiKey: '123456789'
})
diff --git a/api/src/components/flag/__tests__/resolveFlag.test.ts b/api/src/components/flag/__tests__/resolveFlag.test.ts
index 4515a9c83..a41866c47 100644
--- a/api/src/components/flag/__tests__/resolveFlag.test.ts
+++ b/api/src/components/flag/__tests__/resolveFlag.test.ts
@@ -7,7 +7,7 @@ describe('Resolve a flag', () => {
});
test('The flagger can resolve the flag', async () => {
- const resolveFlag = await testUtils.agent.post('/flag/publication-problem-live-flag/resolve').query({
+ const resolveFlag = await testUtils.agent.post('/flags/publication-problem-live-flag/resolve').query({
apiKey: '987654321'
});
@@ -15,7 +15,7 @@ describe('Resolve a flag', () => {
});
test('Only the flagger or super user can resolve the flag', async () => {
- const resolveFlag = await testUtils.agent.post('/flag/publication-problem-live-flag/resolve').query({
+ const resolveFlag = await testUtils.agent.post('/flags/publication-problem-live-flag/resolve').query({
apiKey: '123456789'
});
@@ -23,7 +23,7 @@ describe('Resolve a flag', () => {
});
test('A super user can resolve the flag', async () => {
- const resolveFlag = await testUtils.agent.post('/flag/publication-problem-live-flag/resolve').query({
+ const resolveFlag = await testUtils.agent.post('/flags/publication-problem-live-flag/resolve').query({
apiKey: '000000004'
});
@@ -31,7 +31,7 @@ describe('Resolve a flag', () => {
});
test('An unrelated user cannot resolve a flag', async () => {
- const resolveFlag = await testUtils.agent.post('/flag/publication-problem-live-flag/resolve').query({
+ const resolveFlag = await testUtils.agent.post('/flags/publication-problem-live-flag/resolve').query({
apiKey: '000000003'
});
@@ -39,7 +39,7 @@ describe('Resolve a flag', () => {
});
test('You cannot resolve a flag that has already been resolved', async () => {
- const resolveFlag = await testUtils.agent.post('/flag/publication-hypothesis-live-flag/resolve').query({
+ const resolveFlag = await testUtils.agent.post('/flags/publication-hypothesis-live-flag/resolve').query({
apiKey: '987654321'
});
@@ -47,7 +47,7 @@ describe('Resolve a flag', () => {
});
test('You can only resolve a flag that exists', async () => {
- const resolveFlag = await testUtils.agent.post('/flag/publication-does-not-exist-flag/resolve').query({
+ const resolveFlag = await testUtils.agent.post('/flags/publication-does-not-exist-flag/resolve').query({
apiKey: '987654321'
});
diff --git a/api/src/components/funder/__tests__/createFunder.test.ts b/api/src/components/funder/__tests__/createFunder.test.ts
index 19da39d81..dad2c8018 100644
--- a/api/src/components/funder/__tests__/createFunder.test.ts
+++ b/api/src/components/funder/__tests__/createFunder.test.ts
@@ -1,14 +1,14 @@
import * as testUtils from 'lib/testUtils';
describe('create a funder', () => {
- beforeEach(async () => {
+ beforeAll(async () => {
await testUtils.clearDB();
await testUtils.testSeed();
});
- test('User can add a funder to their DRAFT publication', async () => {
+ test('User can add a funder to their DRAFT publication version', async () => {
const funder = await testUtils.agent
- .post('/publications/publication-problem-draft/funders')
+ .post('/publication-versions/publication-problem-draft-v1/funders')
.query({ apiKey: '000000005' })
.send({
name: 'Example name',
@@ -19,9 +19,9 @@ describe('create a funder', () => {
expect(funder.status).toEqual(200);
});
- test('User cannot add a funder to their LIVE publication', async () => {
+ test('User cannot add a funder to their LIVE publication version', async () => {
const funder = await testUtils.agent
- .post('/publications/publication-problem-live/funders')
+ .post('/publication-versions/publication-problem-live-v1/funders')
.query({ apiKey: '123456789' })
.send({
name: 'Example name',
@@ -32,9 +32,9 @@ describe('create a funder', () => {
expect(funder.status).toEqual(403);
});
- test('User cannot add a funder to another DRAFT publication', async () => {
+ test('User cannot add a funder to another DRAFT publication version', async () => {
const funder = await testUtils.agent
- .post('/publications/publication-problem-draft/funders')
+ .post('/publication-versions/publication-problem-draft-v1/funders')
.query({ apiKey: '987654321' })
.send({
name: 'Example name',
@@ -45,9 +45,9 @@ describe('create a funder', () => {
expect(funder.status).toEqual(403);
});
- test('User cannot add a funder to another LIVE publication', async () => {
+ test('User cannot add a funder to another LIVE publication version', async () => {
const funder = await testUtils.agent
- .post('/publications/publication-problem-live/funders')
+ .post('/publication-versions/publication-problem-live-v1/funders')
.query({ apiKey: '987654321' })
.send({
name: 'Example name',
@@ -60,7 +60,7 @@ describe('create a funder', () => {
});
test('User must send correct information to create a funder (no name)', async () => {
const funder = await testUtils.agent
- .post('/publications/publication-problem-draft/funders')
+ .post('/publication-versions/publication-problem-draft-v1/funders')
.query({ apiKey: '000000005' })
.send({
city: 'Example city',
@@ -72,7 +72,7 @@ describe('create a funder', () => {
});
test('User must send correct information to create a funder (no city)', async () => {
const funder = await testUtils.agent
- .post('/publications/publication-problem-draft/funders')
+ .post('/publication-versions/publication-problem-draft-v1/funders')
.query({ apiKey: '000000005' })
.send({
name: 'Example name',
@@ -84,7 +84,7 @@ describe('create a funder', () => {
});
test('User must send correct information to create a funder (no country)', async () => {
const funder = await testUtils.agent
- .post('/publications/publication-problem-draft/funders')
+ .post('/publication-versions/publication-problem-draft-v1/funders')
.query({ apiKey: '000000005' })
.send({
name: 'Example name',
@@ -96,7 +96,7 @@ describe('create a funder', () => {
});
test('User must send correct information to create a funder (no link)', async () => {
const funder = await testUtils.agent
- .post('/publications/publication-problem-draft/funders')
+ .post('/publication-versions/publication-problem-draft-v1/funders')
.query({ apiKey: '000000005' })
.send({
name: 'Example name',
diff --git a/api/src/components/funder/__tests__/deleteFunder.test.ts b/api/src/components/funder/__tests__/deleteFunder.test.ts
index 2a2fbd4e4..680a3b8ee 100644
--- a/api/src/components/funder/__tests__/deleteFunder.test.ts
+++ b/api/src/components/funder/__tests__/deleteFunder.test.ts
@@ -6,17 +6,17 @@ describe('delete a funder', () => {
await testUtils.testSeed();
});
- test('User can delete a funder from a DRAFT publication', async () => {
+ test('User can delete a funder from a DRAFT publication version', async () => {
const funder = await testUtils.agent
- .delete('/publications/publication-problem-draft/funders/publication-problem-draft-funder')
+ .delete('/publication-versions/publication-problem-draft-v1/funders/publication-problem-draft-funder')
.query({ apiKey: '000000005' });
expect(funder.status).toEqual(200);
});
- test('User cannot delete a funder from a DRAFT publication they are not the owner of', async () => {
+ test('User cannot delete a funder from a DRAFT publication version they are not the owner of', async () => {
const funder = await testUtils.agent
- .delete('/publications/publication-problem-draft/funders/publication-problem-draft-funder')
+ .delete('/publication-versions/publication-problem-draft-v1/funders/publication-problem-draft-funder')
.query({ apiKey: '987654321' });
expect(funder.status).toEqual(403);
@@ -24,7 +24,7 @@ describe('delete a funder', () => {
test('User cannot delete a funder from a LIVE publication', async () => {
const funder = await testUtils.agent
- .delete('/publications/publication-problem-live/funders/publication-problem-live-funder')
+ .delete('/publication-versions/publication-problem-live-v1/funders/publication-problem-live-funder')
.query({ apiKey: '123456789' });
expect(funder.status).toEqual(403);
diff --git a/api/src/components/funder/controller.ts b/api/src/components/funder/controller.ts
index 5f7d5f6ba..34c2a73ae 100644
--- a/api/src/components/funder/controller.ts
+++ b/api/src/components/funder/controller.ts
@@ -1,37 +1,35 @@
import * as response from 'lib/response';
import * as funderService from 'funder/service';
-import * as publicationService from 'publication/service';
+import * as publicationVersionService from 'publicationVersion/service';
import * as I from 'interface';
export const create = async (
event: I.AuthenticatedAPIRequest
): Promise => {
try {
- const publication = await publicationService.getWithVersion(event.pathParameters.id);
+ const publicationVersion = await publicationVersionService.getById(event.pathParameters.id);
//check that the publication exists
- if (!publication) {
+ if (!publicationVersion) {
return response.json(404, {
- message: 'This publication does not exist.'
+ message: 'This publication version does not exist.'
});
}
- const currentVersion = publication.versions[0];
-
//check that the publication is live
- if (currentVersion.currentStatus !== 'DRAFT') {
+ if (publicationVersion.currentStatus !== 'DRAFT') {
return response.json(403, {
message: 'You can only add funding to a draft publication.'
});
}
- if (event.user.id !== currentVersion.user.id) {
+ if (event.user.id !== publicationVersion.user.id) {
return response.json(403, {
message: 'You do not have permissions to add a funder.'
});
}
- const funder = await funderService.create(currentVersion.id, event.body);
+ const funder = await funderService.create(publicationVersion.id, event.body);
return response.json(200, funder);
} catch (err) {
@@ -45,31 +43,29 @@ export const destroy = async (
event: I.AuthenticatedAPIRequest
): Promise => {
try {
- const publication = await publicationService.getWithVersion(event.pathParameters.id);
+ const publicationVersion = await publicationVersionService.getById(event.pathParameters.id);
- //check that the publication exists
- if (!publication) {
+ //check that the publication version exists
+ if (!publicationVersion) {
return response.json(404, {
- message: 'This publication does not exist.'
+ message: 'This publication version does not exist.'
});
}
- const currentVersion = publication?.versions[0];
-
//check that the publication is live
- if (currentVersion.currentStatus !== 'DRAFT') {
+ if (publicationVersion.currentStatus !== 'DRAFT') {
return response.json(403, {
message: 'You cannot delete funding from a publication that is not a draft.'
});
}
- if (event.user.id !== currentVersion.user.id) {
+ if (event.user.id !== publicationVersion.user.id) {
return response.json(403, {
message: 'You do not have permissions to delete a funder.'
});
}
- const funder = await funderService.destroy(currentVersion.id, event.pathParameters.funder);
+ const funder = await funderService.destroy(publicationVersion.id, event.pathParameters.funder);
return response.json(200, funder);
} catch (err) {
diff --git a/api/src/components/link/controller.ts b/api/src/components/link/controller.ts
index 330d5aab0..6c61cfb79 100644
--- a/api/src/components/link/controller.ts
+++ b/api/src/components/link/controller.ts
@@ -7,7 +7,7 @@ import * as I from 'interface';
export const create = async (event: I.AuthenticatedAPIRequest): Promise => {
try {
// function checks if the user has permission to see it in DRAFT mode
- const fromPublication = await publicationService.getWithVersion(event.body.from);
+ const fromPublication = await publicationService.get(event.body.from);
// the publication does not exist, is
// publications that are live cannot have links created.
@@ -17,16 +17,22 @@ export const create = async (event: I.AuthenticatedAPIRequest)
});
}
- const fromCurrentVersion = fromPublication.versions[0];
+ const fromLatestVersion = fromPublication.versions.find((version) => version.isLatestVersion);
- if (fromCurrentVersion.currentStatus === 'LIVE') {
+ if (!fromLatestVersion) {
+ return response.json(403, {
+ message: `Cannot find latest version of ${event.body.from}.`
+ });
+ }
+
+ if (fromLatestVersion.currentStatus === 'LIVE') {
return response.json(403, {
message: `Publication with id ${event.body.from} is LIVE.`
});
}
// the authenticated user is not the owner of the publication
- if (fromCurrentVersion.user.id !== event.user.id) {
+ if (fromLatestVersion.user.id !== event.user.id) {
return response.json(401, { message: 'You do not have permission to create publication links' });
}
diff --git a/api/src/components/publication/__tests__/createPublication.test.ts b/api/src/components/publication/__tests__/createPublication.test.ts
index 738a9e766..2c7203c62 100644
--- a/api/src/components/publication/__tests__/createPublication.test.ts
+++ b/api/src/components/publication/__tests__/createPublication.test.ts
@@ -1,7 +1,7 @@
import * as testUtils from 'lib/testUtils';
describe('Create publication', () => {
- beforeEach(async () => {
+ beforeAll(async () => {
await testUtils.clearDB();
await testUtils.testSeed();
});
@@ -22,12 +22,13 @@ describe('Create publication', () => {
expect(createPublicationRequest.status).toEqual(201);
- expect(createPublicationRequest.body.user.id).toEqual('test-user-1');
- expect(createPublicationRequest.body.publicationStatus.length).toEqual(1);
- expect(createPublicationRequest.body.publicationStatus[0].status).toEqual('DRAFT');
- expect(createPublicationRequest.body.keywords.length).toEqual(2);
- expect(createPublicationRequest.body.description).toEqual('description of Publication test 1');
- expect(createPublicationRequest.body.licence).toEqual('CC_BY_SA');
+ expect(createPublicationRequest.body.versions.length).toEqual(1);
+
+ expect(createPublicationRequest.body.versions[0].createdBy).toEqual('test-user-1');
+ expect(createPublicationRequest.body.versions[0].currentStatus).toEqual('DRAFT');
+ expect(createPublicationRequest.body.versions[0].keywords.length).toEqual(2);
+ expect(createPublicationRequest.body.versions[0].description).toEqual('description of Publication test 1');
+ expect(createPublicationRequest.body.versions[0].licence).toEqual('CC_BY_SA');
});
test('Valid publication created by real user with content (200)', async () => {
@@ -127,7 +128,7 @@ describe('Create publication', () => {
});
expect(createPublicationRequest.status).toEqual(201);
- expect(createPublicationRequest.body.publishedDate).toBeNull();
+ expect(createPublicationRequest.body.versions[0].publishedDate).toBeNull();
});
test('Valid publicatiom created by real user when provided a correct ISO-639-1 language code', async () => {
@@ -144,7 +145,7 @@ describe('Create publication', () => {
});
expect(createPublicationRequest.status).toEqual(201);
- expect(createPublicationRequest.body.language).toEqual('fr');
+ expect(createPublicationRequest.body.versions[0].language).toEqual('fr');
});
test('Publication failed to be created if language code provided is not out of the ISO-639-1 language list', async () => {
@@ -208,7 +209,7 @@ describe('Create publication', () => {
});
expect(createPublicationRequest.status).toEqual(201);
- expect(createPublicationRequest.body.language).toEqual('en');
+ expect(createPublicationRequest.body.versions[0].language).toEqual('en');
});
test('Publication can not be created if supplying a self declaration and if not a protocol or hypotheses', async () => {
@@ -240,7 +241,7 @@ describe('Create publication', () => {
expect(createPublicationRequest.status).toEqual(201);
expect(createPublicationRequest.body.type).toEqual('PROTOCOL');
- expect(createPublicationRequest.body.selfDeclaration).toEqual(true);
+ expect(createPublicationRequest.body.versions[0].selfDeclaration).toEqual(true);
});
test('Publication can be created if not supplying a self declration and is of type hypotheses', async () => {
@@ -257,7 +258,7 @@ describe('Create publication', () => {
expect(createPublicationRequest.status).toEqual(201);
expect(createPublicationRequest.body.type).toEqual('HYPOTHESIS');
- expect(createPublicationRequest.body.selfDeclaration).toEqual(true);
+ expect(createPublicationRequest.body.versions[0].selfDeclaration).toEqual(true);
});
test('Publication can be linked to topic on creation', async () => {
diff --git a/api/src/components/publication/__tests__/deletePublication.test.ts b/api/src/components/publication/__tests__/deletePublication.test.ts
deleted file mode 100644
index f5a30dd0f..000000000
--- a/api/src/components/publication/__tests__/deletePublication.test.ts
+++ /dev/null
@@ -1,63 +0,0 @@
-import * as testUtils from 'lib/testUtils';
-import * as client from 'lib/client';
-
-describe('Delete publications', () => {
- beforeEach(async () => {
- await testUtils.clearDB();
- await testUtils.testSeed();
- });
-
- test('User can delete their own DRAFT publication', async () => {
- const getPublication = await testUtils.agent.delete('/publications/publication-problem-draft').query({
- apiKey: '000000005'
- });
-
- expect(getPublication.status).toEqual(200);
-
- const checkForPublication = await client.prisma.publication.count({
- where: {
- id: 'publication-problem-draft'
- }
- });
-
- expect(checkForPublication).toEqual(0);
- });
-
- test('User cannot delete their own LIVE publication', async () => {
- const getPublication = await testUtils.agent.delete('/publications/publication-problem-live').query({
- apiKey: '123456789'
- });
-
- expect(getPublication.status).toEqual(403);
-
- const checkForPublication = await client.prisma.publication.count({
- where: {
- id: 'publication-problem-live'
- }
- });
-
- expect(checkForPublication).toEqual(1);
- });
-
- test('User cannot delete a DRAFT publication they did not create', async () => {
- const getPublication = await testUtils.agent.delete('/publications/publication-problem-draft').query({
- apiKey: '987654321'
- });
-
- expect(getPublication.status).toEqual(403);
- });
-
- test('User cannot delete a LIVE publication they did not create', async () => {
- const getPublication = await testUtils.agent.delete('/publications/publication-problem-live').query({
- apiKey: '987654321'
- });
-
- expect(getPublication.status).toEqual(403);
- });
-
- test('Unauthenticated user cannot delete a DRAFT publication they did not create', async () => {
- const getPublication = await testUtils.agent.delete('/publications/publication-problem-draft');
-
- expect(getPublication.status).toEqual(401);
- });
-});
diff --git a/api/src/components/publication/__tests__/getPublication.test.ts b/api/src/components/publication/__tests__/getPublication.test.ts
index 9dc7030b1..5539ffd9e 100644
--- a/api/src/components/publication/__tests__/getPublication.test.ts
+++ b/api/src/components/publication/__tests__/getPublication.test.ts
@@ -1,33 +1,34 @@
import * as testUtils from 'lib/testUtils';
-describe('View individual publications', () => {
+describe('View publications + versions', () => {
beforeEach(async () => {
await testUtils.clearDB();
await testUtils.testSeed();
});
- test('User who created publication can see DRAFT publication', async () => {
+ test('User who created publication version can see DRAFT versions', async () => {
const getPublication = await testUtils.agent.get('/publications/publication-1').query({
apiKey: '123456789'
});
expect(getPublication.body.id).toEqual('publication-1');
+ expect(getPublication.body.versions.some((version) => version.currentStatus === 'DRAFT'));
});
- test('User who did not create publication cannot see DRAFT publication', async () => {
+ test('User who did not create publication version cannot see DRAFT versions', async () => {
const getPublication = await testUtils.agent.get('/publications/publication-1').query({
apiKey: '987654321'
});
- expect(getPublication.status).toEqual(404);
+ expect(getPublication.status).toEqual(403);
});
- test('Cannot view publication in DRAFT without API key', async () => {
+ test('Cannot view publication version in DRAFT without API key', async () => {
const getPublication = await testUtils.agent.get('/publications/publication-1').query({
apiKey: '987654321'
});
- expect(getPublication.status).toEqual(404);
+ expect(getPublication.status).toEqual(403);
});
test.todo('Any user can see a LIVE publication');
diff --git a/api/src/components/publication/__tests__/updateStatus.test.ts b/api/src/components/publication/__tests__/updateStatus.test.ts
deleted file mode 100644
index 2122fd0cb..000000000
--- a/api/src/components/publication/__tests__/updateStatus.test.ts
+++ /dev/null
@@ -1,253 +0,0 @@
-import * as testUtils from 'lib/testUtils';
-
-beforeEach(async () => {
- await testUtils.clearDB();
- await testUtils.testSeed();
-});
-
-describe('Update publication status', () => {
- test('User with permissions can update their publication to LIVE from DRAFT (after creating a link)', async () => {
- const updatePublicationAttemptOne = await testUtils.agent
- .put('/publications/publication-analysis-draft/status/LIVE')
- .query({
- apiKey: '123456789'
- });
-
- expect(updatePublicationAttemptOne.status).toEqual(403);
-
- // add a valid link
- await testUtils.agent
- .post('/links')
- .query({
- apiKey: '123456789'
- })
- .send({
- from: 'publication-analysis-draft',
- to: 'publication-data-live'
- });
-
- const updatePublicationAttemptTwo = await testUtils.agent
- .put('/publications/publication-analysis-draft/status/LIVE')
- .query({
- apiKey: '123456789'
- });
-
- expect(updatePublicationAttemptTwo.status).toEqual(200);
- });
-
- test('User with permissions can update their publication to LIVE from DRAFT', async () => {
- const updatedPublication = await testUtils.agent
- .put('/publications/publication-hypothesis-draft-problem-live/status/LIVE')
- .query({
- apiKey: '123456789'
- });
-
- expect(updatedPublication.status).toEqual(200);
- });
-
- test('User with permissions cannot update their publication to DRAFT from LIVE', async () => {
- const updatedPublication = await testUtils.agent
- .put('/publications/publication-problem-live/status/DRAFT')
- .query({
- apiKey: '123456789'
- });
-
- expect(updatedPublication.status).toEqual(403);
- });
-
- test('User without permissions cannot update their publication to LIVE from DRAFT', async () => {
- const updatedPublication = await testUtils.agent
- .put('/publications/publication-hypothesis-draft-problem-live/status/LIVE')
- .query({
- apiKey: '987654321'
- });
-
- expect(updatedPublication.status).toEqual(403);
- });
-
- test('User with permissions cannot update their publication to LIVE from DRAFT if there is no content.', async () => {
- const updatedPublication = await testUtils.agent
- .put('/publications/publication-problem-draft-no-content/status/LIVE')
- .query({
- apiKey: '123456789'
- });
-
- expect(updatedPublication.status).toEqual(403);
- });
-
- test('User with permissions cannot update their publication to LIVE from DRAFT if there is no licence.', async () => {
- const updatedPublication = await testUtils.agent
- .put('/publications/publication-hypothesis-draft/status/LIVE')
- .query({
- apiKey: '000000005'
- });
-
- expect(updatedPublication.status).toEqual(403);
- });
-
- test('User with permissions can update their publication to LIVE from DRAFT and a publishedDate is created', async () => {
- const updatedPublication = await testUtils.agent
- .put('/publications/publication-hypothesis-draft-problem-live/status/LIVE')
- .query({
- apiKey: '123456789'
- });
-
- expect(updatedPublication.status).toEqual(200);
- expect(updatedPublication.body.message).toEqual('Publication is now LIVE.');
- });
-
- // COI tests
- test('User with permissions cannot update their publication to LIVE if they have a conflict of interest, but have not provided coi text', async () => {
- const updatedPublication = await testUtils.agent
- .put('/publications/publication-problem-draft-with-coi-but-no-text/status/LIVE')
- .query({
- apiKey: '123456789'
- });
-
- expect(updatedPublication.status).toEqual(403);
- });
-
- test('User with permissions can update their publication to LIVE with a conflict of interest, if they have provided text', async () => {
- const updatedPublication = await testUtils.agent
- .put('/publications/publication-problem-draft-with-coi-with-text/status/LIVE')
- .query({
- apiKey: '123456789'
- });
-
- expect(updatedPublication.status).toEqual(200);
- });
-
- test('User with permissions can update their publication to LIVE if they have no conflict of interest & have not provided text', async () => {
- const updatedPublication = await testUtils.agent
- .put('/publications/publication-problem-draft-with-no-coi-with-no-text/status/LIVE')
- .query({
- apiKey: '123456789'
- });
-
- expect(updatedPublication.status).toEqual(200);
- });
-
- test('User with permissions can update their publication to LIVE if they have no conflict of interest & have provided text', async () => {
- const updatedPublication = await testUtils.agent
- .put('/publications/publication-problem-draft-with-no-coi-with-text/status/LIVE')
- .query({
- apiKey: '123456789'
- });
-
- expect(updatedPublication.status).toEqual(200);
- });
-
- test('Publication owner can publish if all co-authors are confirmed', async () => {
- const updatePublication = await testUtils.agent
- .put('/publications/publication-protocol-draft/status/LIVE')
- .query({
- apiKey: '000000005'
- });
-
- expect(updatePublication.status).toEqual(200);
-
- expect(updatePublication.body.message).toEqual('Publication is now LIVE.');
- });
-
- test('Publication owner cannot publish if not all co-authors are confirmed', async () => {
- const updatePublication = await testUtils.agent
- .put('/publications/publication-hypothesis-draft/status/LIVE')
- .query({
- apiKey: '000000005'
- });
-
- expect(updatePublication.status).toEqual(403);
- expect(updatePublication.body.message).toEqual(
- 'Publication is not ready to be made LIVE. Make sure all fields are filled in.'
- );
-
- const getPublicationStatus = await testUtils.agent.get('/publications/publication-hypothesis-draft').query({
- apiKey: '000000005'
- });
-
- expect(getPublicationStatus.body.currentStatus).toEqual('DRAFT');
- });
-
- test('User other than the owner (does not have permission) cannot publish if co-authors all approved', async () => {
- const updatePublication = await testUtils.agent
- .put('/publications/publication-hypothesis-draft/status/LIVE')
- .query({
- apiKey: '000000006'
- });
-
- expect(updatePublication.status).toEqual(403);
- expect(updatePublication.body.message).toEqual(
- 'You do not have permission to modify the status of this publication.'
- );
-
- const getPublicationStatus = await testUtils.agent.get('/publications/publication-hypothesis-draft').query({
- apiKey: '000000005'
- });
-
- expect(getPublicationStatus.body.currentStatus).toEqual('DRAFT');
- });
-
- test('Publication owner cannot update publication status to LOCKED if there are no co-authors', async () => {
- const response = await testUtils.agent.put('/publications/publication-2/status/LOCKED').query({
- apiKey: '987654321'
- });
-
- expect(response.status).toEqual(403);
- expect(response.body.message).toEqual(
- 'Publication is not ready to be LOCKED. Make sure all fields are filled in.'
- );
- });
-
- test('Throws an error if trying to update publication status to the same status', async () => {
- const response = await testUtils.agent.put('/publications/publication-2/status/DRAFT').query({
- apiKey: '987654321'
- });
-
- expect(response.status).toEqual(403);
- expect(response.body.message).toEqual('Publication status is already DRAFT.');
- });
-
- test('Publication status can be updated from DRAFT to LOCKED only after requesting approvals', async () => {
- // try to update status to LOCKED
- const updateStatusResponse1 = await testUtils.agent
- .put('/publications/publication-problem-draft/status/LOCKED')
- .query({
- apiKey: '000000005'
- });
-
- expect(updateStatusResponse1.status).toEqual(403);
- expect(updateStatusResponse1.body.message).toEqual(
- 'Publication is not ready to be LOCKED. Make sure all fields are filled in.'
- );
-
- // request co-authors approvals
- const requestApprovalsResponse = await testUtils.agent
- .put('/publicationVersions/publication-problem-draft-v1/coauthors/request-approval')
- .query({
- apiKey: '000000005'
- });
-
- expect(requestApprovalsResponse.status).toEqual(200);
-
- // try to update status to LOCKED again
- const updateStatusResponse2 = await testUtils.agent
- .put('/publications/publication-problem-draft/status/LOCKED')
- .query({
- apiKey: '000000005'
- });
-
- expect(updateStatusResponse2.status).toEqual(200);
- expect(updateStatusResponse2.body.message).toEqual('Publication status updated to LOCKED.');
- });
-
- test('Publication status can be updated from LOCKED to LIVE after all co-authors approved', async () => {
- const response = await testUtils.agent
- .put('/publications/locked-publication-problem-confirmed-co-authors/status/LIVE')
- .query({
- apiKey: '123456789'
- });
-
- expect(response.status).toEqual(200);
- expect(response.body.message).toEqual('Publication is now LIVE.');
- });
-});
diff --git a/api/src/components/publication/controller.ts b/api/src/components/publication/controller.ts
index 9493d3380..45f87003d 100644
--- a/api/src/components/publication/controller.ts
+++ b/api/src/components/publication/controller.ts
@@ -1,74 +1,36 @@
-import htmlToText from 'html-to-text';
import axios from 'axios';
import * as s3 from 'lib/s3';
-import * as sqs from 'lib/sqs';
import * as I from 'interface';
import * as helpers from 'lib/helpers';
import * as response from 'lib/response';
import * as publicationService from 'publication/service';
import * as publicationVersionService from 'publicationVersion/service';
-import * as referenceService from 'reference/service';
-import * as coAuthorService from 'coauthor/service';
-
-export const getAll = async (
- event: I.AuthenticatedAPIRequest
-): Promise => {
- try {
- const openSearchPublications = await publicationService.getOpenSearchRecords(event.queryStringParameters);
-
- const publicationIds = openSearchPublications.body.hits.hits.map((hit) => hit._id as string);
-
- const publications = await publicationService.getAllByIds(publicationIds);
-
- const publicationsOrderedBySearch = publicationIds.map((publicationId) =>
- publications.find((publication) => publication.id === publicationId)
- );
-
- return response.json(200, {
- data: publicationsOrderedBySearch,
- metadata: {
- total: openSearchPublications.body.hits.total.value,
- limit: Number(event.queryStringParameters.limit) || 10,
- offset: Number(event.queryStringParameters.offset) || 0
- }
- });
- } catch (err) {
- console.log(err);
-
- return response.json(500, { message: 'Unknown server error.' });
- }
-};
export const get = async (
event: I.APIRequest
): Promise => {
try {
- // Get the publication with the latest version data merged in to keep it simple for the UI.
- const publication = await publicationService.getWithVersionMerged(event.pathParameters.id);
-
- // anyone can see a LIVE publication
- if (publication?.currentStatus === 'LIVE') {
- return response.json(200, publication);
- }
+ const publication = await publicationService.get(event.pathParameters.id);
if (!publication) {
return response.json(404, {
- message:
- 'Publication is either not found, or you do not have permissions to view it in its current state.'
+ message: 'Publication not found.'
});
}
- // only the owner or co-authors can view publications
- if (
- event.user?.id === publication.user.id ||
- publication.coAuthors.some((coAuthor) => coAuthor.linkedUser === event.user?.id)
- ) {
- return response.json(200, publication);
+ // only the owner or co-authors can view the DRAFT/LOCKED versions
+ publication.versions = publication.versions.filter((version) =>
+ version.currentStatus === 'LIVE'
+ ? true
+ : event.user?.id === version.createdBy ||
+ version.coAuthors.some((author) => author.linkedUser === event.user?.id)
+ );
+
+ if (!publication.versions.length) {
+ return response.json(403, { message: "You don't have permissions to view this publication." });
}
- return response.json(404, {
- message: 'Publication is either not found, or you do not have permissions to view it in its current state.'
- });
+ return response.json(200, publication);
} catch (err) {
console.log(err);
@@ -92,57 +54,6 @@ export const getSeedDataPublications = async (
}
};
-export const deletePublication = async (
- event: I.AuthenticatedAPIRequest
-): Promise => {
- try {
- const publication = await publicationService.get(event.pathParameters.id);
-
- if (!publication) {
- return response.json(403, {
- message: 'This publication does not exist.'
- });
- }
-
- // If there has been more than one version of a publication, we can't delete it.
- if (publication.versions.length > 1) {
- return response.json(403, {
- message: 'A publication can not be deleted if there is more than one version of it.'
- });
- } else if (!publication.versions || publication.versions.length === 0) {
- throw Error('Could not get versions for publication');
- }
-
- const version = publication.versions[0];
-
- if (version.user.id !== event.user.id) {
- return response.json(403, {
- message: 'You do not have permission to delete this publication.'
- });
- }
-
- // The logic here is a bit odd, but the currentStatus and publicationStatus array are not intrinsically linked
- // so to be safe, we are checking that the current status is DRAFT and that the entire history of the publication
- // has only ever been draft.
- if (
- version.currentStatus !== 'DRAFT' ||
- (version.publicationStatus && !version.publicationStatus.every((status) => status.status !== 'LIVE'))
- ) {
- return response.json(403, {
- message: 'A publication can only be deleted if it is currently a draft and has never been LIVE.'
- });
- }
-
- await publicationService.deletePublication(event.pathParameters.id);
-
- return response.json(200, { message: `Publication ${event.pathParameters.id} deleted` });
- } catch (err) {
- console.log(err);
-
- return response.json(500, { message: 'Unknown server error.' });
- }
-};
-
export const create = async (
event: I.AuthenticatedAPIRequest
): Promise => {
@@ -181,201 +92,33 @@ export const create = async (
}
};
-export const updateCurrentVersion = async (
- event: I.AuthenticatedAPIRequest
+export const getLinksForPublication = async (
+ event: I.APIRequest
): Promise => {
- try {
- const publication = await publicationService.getWithVersion(event.pathParameters.id);
-
- if (!publication) {
- return response.json(403, {
- message: 'This publication does not exist.'
- });
- }
-
- const currentVersion = publication.versions[0];
-
- if (!currentVersion) {
- throw Error('Unable to find current version for publication');
- }
-
- if (currentVersion.user.id !== event.user.id) {
- return response.json(403, {
- message: 'You do not have permission to modify this publication.'
- });
- }
-
- if (currentVersion.currentStatus !== 'DRAFT') {
- return response.json(404, { message: 'A publication that is not in DRAFT state cannot be updated.' });
- }
-
- if (event.body.content) {
- event.body.content = helpers.getSafeHTML(event.body.content);
- }
-
- if (
- event.body.selfDeclaration !== undefined &&
- publication.type !== 'PROTOCOL' &&
- publication.type !== 'HYPOTHESIS'
- ) {
- return response.json(400, {
- message:
- 'You can not declare a self declaration for a publication that is not a protocol or hypothesis.'
- });
- }
-
- if (event.body.dataAccessStatement !== undefined && publication.type !== 'DATA') {
- return response.json(400, {
- message: 'You can not supply a data access statement on a non data publication.'
- });
- }
-
- if (event.body.dataPermissionsStatement !== undefined && publication.type !== 'DATA') {
- return response.json(400, {
- message: 'You can not supply a data permissions statement on a non data publication.'
- });
- }
-
- await publicationService.updateCurrentVersion(event.pathParameters.id, event.body);
-
- const updatedPublication = await publicationService.getWithVersionMerged(event.pathParameters.id);
-
- return response.json(200, updatedPublication);
- } catch (err) {
- console.log(err);
-
- return response.json(500, { message: 'Unknown server error.' });
- }
-};
+ const publicationId = event.pathParameters.id;
+ const directLinks = event.queryStringParameters?.direct === 'true';
+ const user = event.user;
+ let includeDraft = false;
-export const updateStatus = async (
- event: I.AuthenticatedAPIRequest
-): Promise => {
try {
- const publicationId = event.pathParameters?.id;
- const publication = await publicationService.getWithVersion(publicationId);
-
- if (!publication) {
- return response.json(404, {
- message: 'This publication does not exist.'
- });
- }
-
- const currentVersion = publication.versions[0];
-
- if (currentVersion.createdBy !== event.user.id) {
- return response.json(403, {
- message: 'You do not have permission to modify the status of this publication.'
- });
- }
-
- const newStatus = event.pathParameters?.status;
- const currentStatus = currentVersion.currentStatus;
-
- if (currentStatus === 'LIVE') {
- return response.json(403, {
- message: 'A status of a publication that is not in DRAFT or LOCKED cannot be changed.'
- });
- }
-
- if (currentStatus === newStatus) {
- return response.json(403, { message: `Publication status is already ${newStatus}.` });
- }
-
- if (currentStatus === 'DRAFT') {
- if (newStatus === 'LOCKED') {
- // check if publication version actually has co-authors
- if (currentVersion.coAuthors.length === 1) {
- return response.json(403, { message: 'Publication cannot be LOCKED without co-authors.' });
- }
-
- // check if publication version is ready to be LOCKED
- if (!publicationService.isReadyToLock(publication)) {
- return response.json(403, {
- message: 'Publication is not ready to be LOCKED. Make sure all fields are filled in.'
- });
- }
-
- // Lock publication from editing
- await publicationVersionService.updateStatus(currentVersion.id, 'LOCKED');
-
- return response.json(200, { message: 'Publication status updated to LOCKED.' });
- }
-
- if (newStatus === 'LIVE') {
- const isReadyToPublish = publicationService.isReadyToPublish(publication);
-
- if (!isReadyToPublish) {
- return response.json(403, {
- message: 'Publication is not ready to be made LIVE. Make sure all fields are filled in.'
- });
+ if (directLinks) {
+ if (user) {
+ const latestVersion = await publicationVersionService.get(publicationId, 'latest');
+
+ // if latest version is a DRAFT, check if user can see it
+ if (
+ latestVersion?.currentStatus === 'DRAFT' &&
+ (user.id === latestVersion?.createdBy ||
+ latestVersion?.coAuthors.some((coAuthor) => coAuthor.linkedUser === user.id))
+ ) {
+ includeDraft = true;
}
}
}
- if (currentStatus === 'LOCKED') {
- if (newStatus === 'DRAFT') {
- // Update status to 'DRAFT'
- await publicationVersionService.updateStatus(currentVersion.id, newStatus);
-
- // Cancel co author approvals
- await coAuthorService.resetCoAuthors(currentVersion.id);
-
- return response.json(200, {
- message: 'Publication unlocked for editing'
- });
- }
-
- if (newStatus === 'LIVE') {
- const isReadyToPublish = publicationService.isReadyToPublish(publication);
-
- if (!isReadyToPublish) {
- return response.json(403, {
- message: 'Publication is not ready to be made LIVE. Make sure all fields are filled in.'
- });
- }
- }
- }
-
- const updatedVersion = await publicationVersionService.updateStatus(currentVersion.id, newStatus);
-
- // now that the publication version is LIVE, add/update the opensearch record
- await publicationService.createOpenSearchRecord({
- id: publicationId,
- type: updatedVersion.publication.type,
- title: updatedVersion.title,
- licence: updatedVersion.licence,
- description: updatedVersion.description,
- keywords: updatedVersion.keywords,
- content: updatedVersion.content,
- publishedDate: updatedVersion.publishedDate,
- cleanContent: htmlToText.convert(updatedVersion.content)
- });
-
- const references = await referenceService.getAllByPublicationVersion(updatedVersion.id);
-
- // Publication version is live, so update the DOI
- await helpers.updateDOI(publication.doi, publication, references);
-
- // send message to the pdf generation queue
- // currently only on deployed instances while a local solution is developed
- if (process.env.STAGE !== 'local') await sqs.sendMessage(publicationId);
-
- return response.json(200, { message: 'Publication is now LIVE.' });
- } catch (err) {
- console.log(err);
-
- return response.json(500, { message: 'Unknown server error.' });
- }
-};
-
-export const getLinksForPublication = async (
- event: I.APIRequest
-): Promise => {
- try {
- const { publication, linkedFrom, linkedTo } = await publicationService.getLinksForPublication(
- event.pathParameters.id
- );
+ const { publication, linkedFrom, linkedTo } = directLinks
+ ? await publicationService.getDirectLinksForPublication(publicationId, includeDraft)
+ : await publicationService.getLinksForPublication(publicationId);
if (!publication) {
return response.json(404, { message: 'Not found.' });
@@ -429,22 +172,13 @@ export const getPDF = async (
// generate new PDF
try {
// We know the publication has at least one LIVE version.
- const latestPublishedVersion = publication.versions.find((version) => version.isLatestLiveVersion);
+ const latestPublishedVersion = await publicationVersionService.get(publication.id, 'latestLive');
if (!latestPublishedVersion) {
throw Error('Unable to get latest published version from supplied object');
}
- const publicationWithLatestPublishedVersion = await publicationService.getWithVersion(
- publication.id,
- latestPublishedVersion.versionNumber
- );
-
- if (!publicationWithLatestPublishedVersion) {
- throw Error('Unable to get latest published version from DB');
- }
-
- const newPDFUrl = await publicationService.generatePDF(publicationWithLatestPublishedVersion);
+ const newPDFUrl = await publicationService.generatePDF(latestPublishedVersion);
if (!newPDFUrl) {
throw Error('Failed to generate PDF');
@@ -527,3 +261,17 @@ export const updateTopics = async (
return response.json(500, { message: 'Unknown server error.' });
}
};
+
+export const getPublicationTopics = async (
+ event: I.APIRequest
+): Promise => {
+ try {
+ const topics = await publicationService.getPublicationTopics(event.pathParameters.id);
+
+ return response.json(200, topics);
+ } catch (error) {
+ console.log(error);
+
+ return response.json(500, { message: 'Unknown server error.' });
+ }
+};
diff --git a/api/src/components/publication/routes.ts b/api/src/components/publication/routes.ts
index 859256e98..20218c154 100644
--- a/api/src/components/publication/routes.ts
+++ b/api/src/components/publication/routes.ts
@@ -1,15 +1,9 @@
import middy from '@middy/core';
import * as middleware from 'middleware';
-
import * as publicationController from 'publication/controller';
import * as publicationSchema from 'publication/schema';
-export const getAll = middy(publicationController.getAll)
- .use(middleware.doNotWaitForEmptyEventLoop({ runOnError: true, runOnBefore: true, runOnAfter: true }))
- .use(middleware.httpJsonBodyParser())
- .use(middleware.validator(publicationSchema.getAll, 'queryStringParameters'));
-
export const get = middy(publicationController.get)
.use(middleware.doNotWaitForEmptyEventLoop({ runOnError: true, runOnBefore: true, runOnAfter: true }))
.use(middleware.httpJsonBodyParser())
@@ -25,23 +19,6 @@ export const create = middy(publicationController.create)
.use(middleware.authentication())
.use(middleware.validator(publicationSchema.create, 'body'));
-export const update = middy(publicationController.updateCurrentVersion)
- .use(middleware.doNotWaitForEmptyEventLoop({ runOnError: true, runOnBefore: true, runOnAfter: true }))
- .use(middleware.httpJsonBodyParser())
- .use(middleware.authentication())
- .use(middleware.validator(publicationSchema.update, 'body'));
-
-export const deletePublication = middy(publicationController.deletePublication)
- .use(middleware.doNotWaitForEmptyEventLoop({ runOnError: true, runOnBefore: true, runOnAfter: true }))
- .use(middleware.httpJsonBodyParser())
- .use(middleware.authentication());
-
-export const updateStatus = middy(publicationController.updateStatus)
- .use(middleware.doNotWaitForEmptyEventLoop({ runOnError: true, runOnBefore: true, runOnAfter: true }))
- .use(middleware.httpJsonBodyParser())
- .use(middleware.authentication())
- .use(middleware.validator(publicationSchema.updateStatus, 'pathParameters'));
-
export const getPublicationLinks = middy(publicationController.getLinksForPublication)
.use(middleware.doNotWaitForEmptyEventLoop({ runOnError: true, runOnBefore: true, runOnAfter: true }))
.use(middleware.httpJsonBodyParser())
@@ -61,3 +38,7 @@ export const updateTopics = middy(publicationController.updateTopics)
.use(middleware.httpJsonBodyParser())
.use(middleware.authentication())
.use(middleware.validator(publicationSchema.updateTopics, 'body'));
+
+export const getPublicationTopics = middy(publicationController.getPublicationTopics).use(
+ middleware.doNotWaitForEmptyEventLoop({ runOnError: true, runOnBefore: true, runOnAfter: true })
+);
diff --git a/api/src/components/publication/schema/index.ts b/api/src/components/publication/schema/index.ts
index f88330e35..77bca4bb6 100644
--- a/api/src/components/publication/schema/index.ts
+++ b/api/src/components/publication/schema/index.ts
@@ -1,6 +1,3 @@
export { default as create } from './create';
-export { default as updateStatus } from './updateStatus';
-export { default as getAll } from './getAll';
-export { default as update } from './update';
export { default as getPDF } from './getPDF';
export { default as updateTopics } from './updateTopics';
diff --git a/api/src/components/publication/service.ts b/api/src/components/publication/service.ts
index 5ad5a4685..0690d05db 100644
--- a/api/src/components/publication/service.ts
+++ b/api/src/components/publication/service.ts
@@ -6,97 +6,8 @@ import * as referenceService from 'reference/service';
import * as Helpers from 'lib/helpers';
import { Prisma } from '@prisma/client';
import { Browser, launch } from 'puppeteer-core';
-
import { PutObjectCommand } from '@aws-sdk/client-s3';
-import * as publicationVersionService from 'publicationVersion/service';
-
-export const getAllByIds = async (ids: Array) => {
- // Get base publications
- const publications = await client.prisma.publication.findMany({
- where: {
- id: {
- in: ids
- }
- }
- });
-
- // Get current versions of these publications
- const versions = await client.prisma.publicationVersion.findMany({
- where: {
- versionOf: {
- in: ids
- },
- isLatestVersion: true
- },
- include: {
- user: {
- select: {
- firstName: true,
- lastName: true,
- id: true,
- orcid: true
- }
- },
- coAuthors: {
- select: {
- id: true,
- approvalRequested: true,
- confirmedCoAuthor: true,
- code: true,
- linkedUser: true,
- email: true,
- publicationVersionId: true,
- user: {
- select: {
- orcid: true,
- firstName: true,
- lastName: true
- }
- }
- },
- orderBy: {
- position: 'asc'
- }
- }
- }
- });
-
- if (publications.length !== versions.length) {
- throw Error('Unable to find a current version for all requested publications');
- }
-
- // Merge versioned data into the publication records
- const mergedPublications = publications.map((publication) => {
- const currentVersion = versions.find((version) => version.versionOf === publication.id);
-
- return { ...currentVersion, ...publication };
- });
-
- return mergedPublications;
-};
-
-export const updateCurrentVersion = async (id: string, updateContent: I.UpdatePublicationRequestBody) => {
- // Updates will always be made to the current version.
- const currentVersion = await client.prisma.publicationVersion.findFirst({
- where: {
- versionOf: id,
- isLatestVersion: true
- },
- select: {
- id: true
- }
- });
- const updatedVersion = await client.prisma.publicationVersion.update({
- where: {
- id: currentVersion?.id
- },
- data: updateContent
- });
-
- return updatedVersion;
-};
-
export const isIdInUse = async (id: string) => {
const publication = await client.prisma.publication.count({
where: {
@@ -107,375 +18,6 @@ export const isIdInUse = async (id: string) => {
return Boolean(publication);
};
-// For convenience, sometimes we want to present a publication with the data from
-// a particular version inline, disguised as one entity. For example, when we provide it straight to the UI.
-export const getWithVersionMerged = async (id: string, versionNumber?: number) => {
- // Get the overall publication without versions initially
- const publication = await client.prisma.publication.findFirst({
- where: {
- id
- },
- include: {
- publicationFlags: {
- select: {
- id: true,
- category: true,
- resolved: true,
- createdBy: true,
- createdAt: true,
- user: {
- select: {
- id: true,
- orcid: true,
- firstName: true,
- lastName: true,
- email: true,
- createdAt: true,
- updatedAt: true
- }
- }
- }
- },
- linkedTo: {
- where: {
- publicationToRef: {
- versions: {
- some: {
- isLatestLiveVersion: true
- }
- }
- }
- },
- select: {
- id: true,
- publicationToRef: {
- select: {
- id: true,
- type: true,
- doi: true,
- versions: {
- select: {
- title: true,
- publishedDate: true,
- currentStatus: true,
- description: true,
- keywords: true,
- user: {
- select: {
- id: true,
- firstName: true,
- lastName: true,
- orcid: true
- }
- }
- }
- }
- }
- }
- }
- },
- linkedFrom: {
- where: {
- publicationFromRef: {
- versions: {
- some: {
- isLatestLiveVersion: true
- }
- }
- }
- },
- select: {
- id: true,
- publicationFromRef: {
- select: {
- id: true,
- type: true,
- doi: true,
- versions: {
- select: {
- title: true,
- publishedDate: true,
- currentStatus: true,
- description: true,
- keywords: true,
- user: {
- select: {
- id: true,
- firstName: true,
- lastName: true,
- orcid: true
- }
- }
- }
- }
- }
- }
- }
- },
- topics: {
- select: {
- id: true,
- title: true,
- language: true,
- translations: true
- }
- }
- }
- });
-
- // Get the specified version if we are given a version number, otherwise the current one.
- const versionWhere = {
- versionOf: id,
- ...(versionNumber
- ? {
- versionNumber
- }
- : {
- isLatestVersion: true
- })
- };
-
- const version = await client.prisma.publicationVersion.findFirst({
- where: versionWhere,
- include: {
- user: {
- select: {
- id: true,
- orcid: true,
- firstName: true,
- lastName: true,
- email: true,
- createdAt: true,
- updatedAt: true
- }
- },
- publicationStatus: {
- select: {
- status: true,
- createdAt: true,
- id: true
- },
- orderBy: {
- createdAt: 'desc'
- }
- },
- funders: {
- select: {
- id: true,
- city: true,
- country: true,
- name: true,
- link: true,
- ror: true
- }
- },
- coAuthors: {
- select: {
- id: true,
- email: true,
- linkedUser: true,
- publicationVersionId: true,
- confirmedCoAuthor: true,
- approvalRequested: true,
- createdAt: true,
- reminderDate: true,
- isIndependent: true,
- affiliations: true,
- user: {
- select: {
- firstName: true,
- lastName: true,
- orcid: true
- }
- }
- },
- orderBy: {
- position: 'asc'
- }
- }
- }
- });
-
- if (!version || !publication) {
- return null;
- }
-
- // Discard versionOf field
- const { versionOf, ...versionRest } = version;
-
- // Necessary to name version id as versionId because "id" will be overwritten
- // by spread operator with publication's id
- return { ...versionRest, versionId: version?.id, ...publication };
-};
-
-// Get a publication with a version attached. By default, the current version.
-export const getWithVersion = async (id: string, versionNumber?: number) => {
- return await client.prisma.publication.findFirst({
- where: {
- id
- },
- include: {
- versions: {
- where: {
- versionOf: id,
- ...(versionNumber
- ? {
- versionNumber
- }
- : {
- isLatestVersion: true
- })
- },
- include: {
- user: {
- select: {
- id: true,
- orcid: true,
- firstName: true,
- lastName: true,
- email: true,
- createdAt: true,
- updatedAt: true
- }
- },
- publicationStatus: {
- select: {
- status: true,
- createdAt: true,
- id: true
- },
- orderBy: {
- createdAt: 'desc'
- }
- },
- funders: {
- select: {
- id: true,
- city: true,
- country: true,
- name: true,
- link: true,
- ror: true
- }
- },
- coAuthors: {
- select: {
- id: true,
- email: true,
- linkedUser: true,
- publicationVersionId: true,
- confirmedCoAuthor: true,
- approvalRequested: true,
- createdAt: true,
- reminderDate: true,
- isIndependent: true,
- affiliations: true,
- user: {
- select: {
- firstName: true,
- lastName: true,
- orcid: true
- }
- }
- },
- orderBy: {
- position: 'asc'
- }
- }
- }
- },
- publicationFlags: {
- select: {
- id: true,
- category: true,
- resolved: true,
- createdBy: true,
- createdAt: true,
- user: {
- select: {
- id: true,
- orcid: true,
- firstName: true,
- lastName: true,
- email: true,
- createdAt: true,
- updatedAt: true
- }
- }
- }
- },
- linkedTo: {
- where: {
- publicationToRef: {
- versions: {
- some: {
- isLatestLiveVersion: true
- }
- }
- }
- },
- select: {
- id: true,
- publicationToRef: {
- select: {
- id: true,
- type: true,
- doi: true,
- versions: {
- select: {
- title: true,
- publishedDate: true,
- currentStatus: true,
- description: true,
- keywords: true
- }
- }
- }
- }
- }
- },
- linkedFrom: {
- where: {
- publicationFromRef: {
- versions: {
- some: {
- isLatestLiveVersion: true
- }
- }
- }
- },
- select: {
- id: true,
- publicationFromRef: {
- select: {
- id: true,
- type: true,
- doi: true,
- versions: {
- select: {
- title: true,
- publishedDate: true,
- currentStatus: true,
- description: true,
- keywords: true
- }
- }
- }
- }
- }
- },
- topics: {
- select: {
- id: true,
- title: true,
- language: true,
- translations: true
- }
- }
- }
- });
-};
-
export const get = async (id: string) => {
return await client.prisma.publication.findUnique({
where: {
@@ -686,7 +228,7 @@ export const createOpenSearchRecord = async (data: I.OpenSearchPublication) => {
return publication;
};
-export const getOpenSearchRecords = async (filters: I.PublicationFilters) => {
+export const getOpenSearchPublications = async (filters: I.OpenSearchPublicationFilters) => {
const orderBy = filters.orderBy
? {
[filters.orderBy]: {
@@ -801,141 +343,30 @@ export const create = async (e: I.CreatePublicationRequestBody, user: I.User, do
status: 'DRAFT'
}
},
- coAuthors: {
- // add main author to authors list
- create: {
- linkedUser: user.id,
- email: user.email || '',
- confirmedCoAuthor: true,
- approvalRequested: false
- }
- }
- }
- },
- topics: e.topicIds?.length
- ? {
- connect: e.topicIds.map((topicId) => ({ id: topicId }))
- }
- : undefined
- },
- include: {
- topics: {
- select: {
- id: true,
- title: true,
- language: true,
- translations: true
- }
- },
- versions: {
- include: {
- user: {
- select: {
- id: true,
- firstName: true,
- lastName: true
- }
- },
- publicationStatus: {
- select: {
- status: true,
- createdAt: true,
- id: true
- },
- orderBy: {
- createdAt: 'desc'
+ coAuthors: {
+ // add main author to authors list
+ create: {
+ linkedUser: user.id,
+ email: user.email || '',
+ confirmedCoAuthor: true,
+ approvalRequested: false
}
}
}
- }
- }
- });
-
- // Return first version data with new publication.
- const version = await client.prisma.publicationVersion.findFirst({
- where: {
- versionOf: doiResponse.data.attributes.suffix
- },
- select: {
- id: true,
- versionNumber: true,
- isLatestVersion: true,
- currentStatus: true,
- publishedDate: true,
- title: true,
- licence: true,
- conflictOfInterestStatus: true,
- conflictOfInterestText: true,
- ethicalStatement: true,
- ethicalStatementFreeText: true,
- dataPermissionsStatement: true,
- dataPermissionsStatementProvidedBy: true,
- dataAccessStatement: true,
- selfDeclaration: true,
- description: true,
- keywords: true,
- content: true,
- language: true,
- fundersStatement: true,
- user: {
- select: {
- id: true,
- firstName: true,
- lastName: true
- }
- },
- publicationStatus: {
- select: {
- status: true,
- createdAt: true,
- id: true
- },
- orderBy: {
- createdAt: 'desc'
- }
},
- funders: {
- select: {
- id: true,
- city: true,
- country: true,
- name: true,
- link: true,
- ror: true
- }
- },
- coAuthors: {
- select: {
- id: true,
- email: true,
- linkedUser: true,
- publicationVersionId: true,
- confirmedCoAuthor: true,
- approvalRequested: true,
- createdAt: true,
- reminderDate: true,
- isIndependent: true,
- affiliations: true,
- user: {
- select: {
- firstName: true,
- lastName: true,
- orcid: true
- }
- }
- },
- orderBy: {
- position: 'asc'
- }
- }
+ topics: e.topicIds?.length
+ ? {
+ connect: e.topicIds.map((topicId) => ({ id: topicId }))
+ }
+ : undefined
+ },
+ include: {
+ topics: true,
+ versions: true
}
});
- if (!version || !publication) {
- throw Error('Failed to find publication and/or latest version data');
- }
-
- return { ...version, versionId: version.id, ...publication };
+ return publication;
};
export const doesDuplicateFlagExist = async (publication, category, user) => {
@@ -951,81 +382,6 @@ export const doesDuplicateFlagExist = async (publication, category, user) => {
return flag;
};
-export const isReadyToPublish = (publication: I.PublicationWithVersionAttached): boolean => {
- if (!publication) {
- return false;
- }
-
- const version = publication.versions[0];
-
- const hasAtLeastOneLinkOrTopic =
- publication.linkedTo.length !== 0 || (publication.type === 'PROBLEM' && publication.topics.length !== 0);
- const hasFilledRequiredFields =
- ['title', 'licence'].every((field) => version[field]) && !Helpers.isEmptyContent(version.content || '');
- const conflictOfInterest = publicationVersionService.validateConflictOfInterest(version);
- const hasPublishDate = Boolean(version.publishedDate);
- const isDataAndHasEthicalStatement = publication.type === 'DATA' ? version.ethicalStatement !== null : true;
- const isDataAndHasPermissionsStatement =
- publication.type === 'DATA' ? version.dataPermissionsStatement !== null : true;
-
- const coAuthorsAreVerified = !!version.coAuthors.every(
- (coAuthor) => coAuthor.confirmedCoAuthor && (coAuthor.isIndependent || coAuthor.affiliations.length)
- );
-
- return (
- hasAtLeastOneLinkOrTopic &&
- hasFilledRequiredFields &&
- conflictOfInterest &&
- !hasPublishDate &&
- isDataAndHasEthicalStatement &&
- isDataAndHasPermissionsStatement &&
- coAuthorsAreVerified &&
- version.isLatestVersion
- );
-};
-
-export const isReadyToRequestApproval = (publication: I.PublicationWithVersionAttached): boolean => {
- const version = publication?.versions[0];
-
- if (!publication || !version?.isLatestVersion || version?.currentStatus !== 'DRAFT') {
- return false;
- }
-
- const hasAtLeastOneLinkOrTopic =
- publication.linkedTo.length !== 0 || (publication.type === 'PROBLEM' && publication.topics.length !== 0);
- const hasFilledRequiredFields =
- ['title', 'licence'].every((field) => version[field]) && !Helpers.isEmptyContent(version.content || '');
- const conflictOfInterest = publicationVersionService.validateConflictOfInterest(version);
- const isDataAndHasEthicalStatement = publication.type === 'DATA' ? version.ethicalStatement !== null : true;
- const isDataAndHasPermissionsStatement =
- publication.type === 'DATA' ? version.dataPermissionsStatement !== null : true;
- const hasConfirmedAffiliations = !!version.coAuthors.some(
- (author) => author.linkedUser === version.createdBy && (author.isIndependent || author.affiliations.length)
- );
-
- return (
- hasAtLeastOneLinkOrTopic &&
- hasFilledRequiredFields &&
- conflictOfInterest &&
- isDataAndHasEthicalStatement &&
- isDataAndHasPermissionsStatement &&
- hasConfirmedAffiliations &&
- version.isLatestVersion
- );
-};
-
-export const isReadyToLock = (publication: I.PublicationWithVersionAttached): boolean => {
- const version = publication?.versions[0];
-
- if (!publication || version?.currentStatus !== 'DRAFT') {
- return false;
- }
-
- const hasRequestedApprovals = !!version.coAuthors.some((author) => author.approvalRequested);
-
- return isReadyToRequestApproval(publication) && hasRequestedApprovals;
-};
-
export const getLinksForPublication = async (id: string): Promise => {
const publication = await get(id);
@@ -1064,10 +420,12 @@ export const getLinksForPublication = async (id: string): Promise`
WITH RECURSIVE to_left AS (
- SELECT "Links"."publicationFrom" "childPublication",
+ SELECT "Links"."id" "linkId",
+ "Links"."publicationFrom" "childPublication",
"Links"."publicationTo" "id",
"pfrom".type "childPublicationType",
"pto".type,
+ "pto"."doi",
"pto_version".title,
"pto_version"."createdBy",
"pto_version"."publishedDate",
@@ -1093,10 +451,12 @@ export const getLinksForPublication = async (id: string): Promise`
WITH RECURSIVE to_right AS (
- SELECT "Links"."publicationFrom" "id",
+ SELECT "Links"."id" "linkId",
+ "Links"."publicationFrom" "id",
"Links"."publicationTo" "parentPublication",
"pfrom".type,
+ "pfrom"."doi",
"pto".type "parentPublicationType",
"pfrom_version"."title",
"pfrom_version"."createdBy",
@@ -1159,9 +521,11 @@ export const getLinksForPublication = async (id: string): Promise ({
+ id: author.id,
+ linkedUser: author.linkedUser,
+ user: {
+ orcid: author.user?.orcid || '',
+ firstName: author.user?.firstName || '',
+ lastName: author.user?.lastName || ''
+ }
+ }))
+ },
+ linkedTo,
+ linkedFrom
+ };
+};
+
+export const getDirectLinksForPublication = async (
+ id: string,
+ includeDraft = false
+): Promise => {
+ const publicationFilter: Prisma.PublicationVersionWhereInput = includeDraft
+ ? { isLatestVersion: true }
+ : { isLatestLiveVersion: true };
+
+ const publication = await client.prisma.publication.findUnique({
+ where: {
+ id
+ },
+ include: {
+ versions: {
+ where: {
+ ...publicationFilter
+ },
+ include: {
+ coAuthors: {
+ include: {
+ user: true
+ }
+ },
+ user: true
+ }
+ },
+ linkedTo: {
+ where: {
+ publicationToRef: {
+ versions: {
+ some: {
+ isLatestLiveVersion: true
+ }
+ }
+ }
+ },
+ select: {
+ id: true,
+ publicationToRef: {
+ select: {
+ id: true,
+ doi: true,
+ type: true,
+ versions: {
+ include: {
+ user: true
+ }
+ }
+ }
+ }
+ }
+ },
+ linkedFrom: {
+ where: {
+ publicationFromRef: {
+ versions: {
+ some: {
+ isLatestLiveVersion: true
+ }
+ }
+ }
+ },
+ select: {
+ id: true,
+ publicationFromRef: {
+ select: {
+ id: true,
+ doi: true,
+ type: true,
+ versions: {
+ include: {
+ user: true
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ });
+
+ if (!publication) {
+ return {
+ publication: null,
+ linkedFrom: [],
+ linkedTo: []
+ };
+ }
+
+ const latestLiveVersion = publication.versions[0];
+
+ if (!latestLiveVersion) {
+ return {
+ publication: null,
+ linkedFrom: [],
+ linkedTo: []
+ };
+ }
+
+ const linkedTo: I.LinkedToPublication[] = publication.linkedTo.map((link) => {
+ const { id: linkId, publicationToRef } = link;
+ const { id, type, versions, doi } = publicationToRef;
+ const { createdBy, user, currentStatus, publishedDate, title } = versions[0];
+
+ return {
+ id,
+ linkId,
+ type,
+ doi,
+ childPublication: publication.id,
+ childPublicationType: publication.type,
+ title: title || '',
+ createdBy,
+ authorFirstName: user.firstName,
+ authorLastName: user.lastName || '',
+ currentStatus,
+ publishedDate: publishedDate?.toISOString() || '',
+ authors: []
+ };
+ });
+
+ const linkedFrom: I.LinkedFromPublication[] = publication.linkedFrom.map((link) => {
+ const { id: linkId, publicationFromRef } = link;
+ const { id, type, versions, doi } = publicationFromRef;
+ const { createdBy, user, currentStatus, publishedDate, title } = versions[0];
+
+ return {
+ id,
+ linkId,
+ type,
+ doi,
+ parentPublication: publication.id,
+ parentPublicationType: publication.type,
+ title: title || '',
+ createdBy,
+ authorFirstName: user.firstName,
+ authorLastName: user.lastName || '',
+ currentStatus,
+ publishedDate: publishedDate?.toISOString() || '',
+ authors: []
+ };
+ });
+
+ const publicationIds = linkedTo.map((link) => link.id).concat(linkedFrom.map((link) => link.id));
+
+ // get coAuthors for each latest LIVE version of each publication
+ const versions = await client.prisma.publicationVersion.findMany({
+ where: {
+ isLatestLiveVersion: true,
+ versionOf: {
+ in: publicationIds
+ }
+ },
+ select: {
+ versionOf: true,
+ coAuthors: {
+ select: {
+ id: true,
+ linkedUser: true,
+ user: {
+ select: {
+ orcid: true,
+ firstName: true,
+ lastName: true
+ }
+ }
+ },
+ orderBy: {
+ position: 'asc'
+ }
+ }
+ }
+ });
+
+ // add authors to 'linkedTo' publications
+ linkedTo.forEach((link) => {
+ const authors = versions.find((version) => version.versionOf === link.id)?.coAuthors || [];
+
+ Object.assign(link, {
+ authors
+ });
+ });
+
+ // add authors to 'linkedFrom' publications
+ linkedFrom.forEach((link) => {
+ const authors = versions.find((version) => version.versionOf === link.id)?.coAuthors || [];
+
+ Object.assign(link, {
+ authors
+ });
+ });
+
+ return {
+ publication: {
+ id: publication.id,
+ type: publication.type,
+ doi: publication.doi,
title: latestLiveVersion.title || '',
createdBy: latestLiveVersion.createdBy,
currentStatus: latestLiveVersion.currentStatus,
@@ -1269,10 +852,16 @@ export const getLinksForPublication = async (id: string): Promise => {
- const references = await referenceService.getAllByPublicationVersion(publication.versions[0].id);
- const htmlTemplate = Helpers.createPublicationHTMLTemplate(publication, references);
+export const generatePDF = async (publicationVersion: I.PublicationVersion): Promise => {
+ const references = await referenceService.getAllByPublicationVersion(publicationVersion.id);
+ const { linkedTo } = await getDirectLinksForPublication(publicationVersion.versionOf);
+ const htmlTemplate = Helpers.createPublicationHTMLTemplate(publicationVersion, references, linkedTo);
const isLocal = process.env.STAGE === 'local';
let browser: Browser | null = null;
@@ -1299,23 +888,23 @@ export const generatePDF = async (publication: I.PublicationWithVersionAttached)
preferCSSPageSize: true,
printBackground: true,
displayHeaderFooter: true,
- headerTemplate: Helpers.createPublicationHeaderTemplate(publication),
- footerTemplate: Helpers.createPublicationFooterTemplate(publication)
+ headerTemplate: Helpers.createPublicationHeaderTemplate(publicationVersion),
+ footerTemplate: Helpers.createPublicationFooterTemplate(publicationVersion)
});
// upload pdf to S3
await s3.client.send(
new PutObjectCommand({
Bucket: `science-octopus-publishing-pdfs-${process.env.STAGE}`,
- Key: `${publication.id}.pdf`,
+ Key: `${publicationVersion.versionOf}.pdf`,
ContentType: 'application/pdf',
Body: pdf
})
);
- console.log('Successfully generated PDF for publicationId: ', publication.id);
+ console.log('Successfully generated PDF for publicationId: ', publicationVersion.versionOf);
- return `${s3.endpoint}/science-octopus-publishing-pdfs-${process.env.STAGE}/${publication.id}.pdf`;
+ return `${s3.endpoint}/science-octopus-publishing-pdfs-${process.env.STAGE}/${publicationVersion.versionOf}.pdf`;
} catch (err) {
console.error(err);
@@ -1473,3 +1062,19 @@ export const updateTopics = async (id: string, topics: string[]) => {
return updateTopics.topics;
};
+
+export const getPublicationTopics = (id: string) =>
+ client.prisma.topic.findMany({
+ where: {
+ publications: {
+ some: {
+ id
+ }
+ }
+ },
+ select: {
+ id: true,
+ createdAt: true,
+ title: true
+ }
+ });
diff --git a/api/src/components/publicationVersion/__tests__/deletePublicationVersion.test.ts b/api/src/components/publicationVersion/__tests__/deletePublicationVersion.test.ts
new file mode 100644
index 000000000..cf557eb4c
--- /dev/null
+++ b/api/src/components/publicationVersion/__tests__/deletePublicationVersion.test.ts
@@ -0,0 +1,73 @@
+import * as testUtils from 'lib/testUtils';
+import * as client from 'lib/client';
+
+describe('Delete publication versions', () => {
+ beforeEach(async () => {
+ await testUtils.clearDB();
+ await testUtils.testSeed();
+ });
+
+ test('User can delete their own DRAFT publication version', async () => {
+ const deletePublicationVersion = await testUtils.agent
+ .delete('/publication-versions/publication-problem-draft-v1')
+ .query({
+ apiKey: '000000005'
+ });
+
+ expect(deletePublicationVersion.status).toEqual(200);
+
+ const checkForPublicationVersion = await client.prisma.publication.count({
+ where: {
+ id: 'publication-problem-draft'
+ }
+ });
+
+ expect(checkForPublicationVersion).toEqual(0);
+ });
+
+ test('User cannot delete their own LIVE publication version', async () => {
+ const deletePublicationVersion = await testUtils.agent
+ .delete('/publication-versions/publication-problem-live-v1')
+ .query({
+ apiKey: '123456789'
+ });
+
+ expect(deletePublicationVersion.status).toEqual(403);
+
+ const checkForPublicationVersion = await client.prisma.publication.count({
+ where: {
+ id: 'publication-problem-live'
+ }
+ });
+
+ expect(checkForPublicationVersion).toEqual(1);
+ });
+
+ test('User cannot delete a DRAFT publication version they did not create', async () => {
+ const deletePublicationVersion = await testUtils.agent
+ .delete('/publication-versions/publication-problem-draft-v1')
+ .query({
+ apiKey: '987654321'
+ });
+
+ expect(deletePublicationVersion.status).toEqual(403);
+ });
+
+ test('User cannot delete a LIVE publication version they did not create', async () => {
+ const deletePublicationVersion = await testUtils.agent
+ .delete('/publication-versions/publication-problem-live-v1')
+ .query({
+ apiKey: '987654321'
+ });
+
+ expect(deletePublicationVersion.status).toEqual(403);
+ });
+
+ test('Unauthenticated user cannot delete a DRAFT publication version they did not create', async () => {
+ const deletePublicationVersion = await testUtils.agent.delete(
+ '/publication-versions/publication-problem-draft-v1'
+ );
+
+ expect(deletePublicationVersion.status).toEqual(401);
+ });
+});
diff --git a/api/src/components/publication/__tests__/updatePublication.test.ts b/api/src/components/publicationVersion/__tests__/updatePublicationVersion.test.ts
similarity index 57%
rename from api/src/components/publication/__tests__/updatePublication.test.ts
rename to api/src/components/publicationVersion/__tests__/updatePublicationVersion.test.ts
index e152cbe61..6d13c5768 100644
--- a/api/src/components/publication/__tests__/updatePublication.test.ts
+++ b/api/src/components/publicationVersion/__tests__/updatePublicationVersion.test.ts
@@ -5,130 +5,130 @@ beforeEach(async () => {
await testUtils.testSeed();
});
-describe('Update publication', () => {
+describe('Update publication version', () => {
test('Cannot update without permission', async () => {
- const updatePublication = await testUtils.agent.patch('/publications/publication-interpretation-draft');
+ const updatedVersion = await testUtils.agent.patch('/publication-versions/publication-interpretation-draft-v1');
- expect(updatePublication.status).toEqual(401);
+ expect(updatedVersion.status).toEqual(401);
});
test('Cannot update with incorrect permissions', async () => {
- const updatePublication = await testUtils.agent
- .patch('/publications/publication-interpretation-draft')
+ const updatedVersion = await testUtils.agent
+ .patch('/publication-versions/publication-interpretation-draft-v1')
.query({ apiKey: 987654321 });
- expect(updatePublication.status).toEqual(403);
+ expect(updatedVersion.status).toEqual(403);
});
- test('Can update publication title', async () => {
- const updatePublication = await testUtils.agent
- .patch('/publications/publication-interpretation-draft')
+ test('Can update publication version title', async () => {
+ const updatedVersion = await testUtils.agent
+ .patch('/publication-versions/publication-interpretation-draft-v1')
.query({ apiKey: 123456789 })
.send({ title: 'New title' });
- expect(updatePublication.status).toEqual(200);
- expect(updatePublication.body.title).toEqual('New title');
+ expect(updatedVersion.status).toEqual(200);
+ expect(updatedVersion.body.title).toEqual('New title');
});
- test('Can update publication content if "safe" HTML', async () => {
- const updatePublication = await testUtils.agent
- .patch('/publications/publication-interpretation-draft')
+ test('Can update publication version content if "safe" HTML', async () => {
+ const updatedVersion = await testUtils.agent
+ .patch('/publication-versions/publication-interpretation-draft-v1')
.query({ apiKey: 123456789 })
.send({ content: 'Hello Nathan
' });
- expect(updatePublication.status).toEqual(200);
+ expect(updatedVersion.status).toEqual(200);
});
test('HTML is sanitised if not "safe" (1)', async () => {
- const updatePublication = await testUtils.agent
- .patch('/publications/publication-interpretation-draft')
+ const updatedVersion = await testUtils.agent
+ .patch('/publication-versions/publication-interpretation-draft-v1')
.query({ apiKey: 123456789 })
.send({ content: 'Hello Nathan
' });
- expect(updatePublication.body.content).toEqual('Hello Nathan
');
+ expect(updatedVersion.body.content).toEqual('Hello Nathan
');
});
test('HTML is sanitised if not "safe" (2)', async () => {
- const updatePublication = await testUtils.agent
- .patch('/publications/publication-interpretation-draft')
+ const updatedVersion = await testUtils.agent
+ .patch('/publication-versions/publication-interpretation-draft-v1')
.query({ apiKey: 123456789 })
.send({ content: 'Hello Nathan
' });
- expect(updatePublication.body.content).toEqual('Hello Nathan
');
+ expect(updatedVersion.body.content).toEqual('Hello Nathan
');
});
- test('Cannot update publication licence', async () => {
+ test('Cannot update publication version licence', async () => {
// This was previously possible but we have now removed the ability because
// there is only one licence type we want people to use and we set it automatically.
- const updatePublication = await testUtils.agent
- .patch('/publications/publication-interpretation-draft')
+ const updatedVersion = await testUtils.agent
+ .patch('/publication-versions/publication-interpretation-draft-v1')
.query({ apiKey: 123456789 })
.send({ licence: 'CC_BY_SA' });
- expect(updatePublication.status).toEqual(422);
+ expect(updatedVersion.status).toEqual(422);
});
test('Can update keywords', async () => {
- const updatePublication = await testUtils.agent
- .patch('/publications/publication-interpretation-draft')
+ const updatedVersion = await testUtils.agent
+ .patch('/publication-versions/publication-interpretation-draft-v1')
.query({ apiKey: 123456789 })
.send({ keywords: ['science', 'technology'] });
- expect(updatePublication.body.keywords.length).toEqual(2);
+ expect(updatedVersion.body.keywords.length).toEqual(2);
});
test('Can update description', async () => {
- const updatePublication = await testUtils.agent
- .patch('/publications/publication-interpretation-draft')
+ const updatedVersion = await testUtils.agent
+ .patch('/publication-versions/publication-interpretation-draft-v1')
.query({ apiKey: 123456789 })
.send({ description: 'Test description' });
- expect(updatePublication.body.description).toEqual('Test description');
+ expect(updatedVersion.body.description).toEqual('Test description');
});
- test('Cannot update publication with invalid update parameter', async () => {
- const updatePublication = await testUtils.agent
- .patch('/publications/publication-interpretation-draft')
+ test('Cannot update publication version with invalid update parameter', async () => {
+ const updatedVersion = await testUtils.agent
+ .patch('/publication-versions/publication-interpretation-draft-v1')
.query({ apiKey: 123456789 })
.send({ doesNotExist: 'invalid-parameter' });
- expect(updatePublication.status).toEqual(422);
+ expect(updatedVersion.status).toEqual(422);
});
test('Cannot update LIVE publication', async () => {
- const updatePublication = await testUtils.agent
- .patch('/publications/publication-real-world-application-live')
+ const updatedVersion = await testUtils.agent
+ .patch('/publication-versions/publication-real-world-application-live-v1')
.query({ apiKey: 123456789 })
.send({ title: 'Brand new title' });
- expect(updatePublication.status).toEqual(404);
+ expect(updatedVersion.status).toEqual(404);
});
test('Cannot add more than 10 keywords', async () => {
- const updatePublication = await testUtils.agent
- .patch('/publications/publication-interpretation-draft')
+ const updatedVersion = await testUtils.agent
+ .patch('/publication-versions/publication-interpretation-draft-v1')
.query({ apiKey: 123456789 })
.send({ keywords: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11'] });
- expect(updatePublication.status).toEqual(422);
+ expect(updatedVersion.status).toEqual(422);
});
test('Cannot add more than 160 characters into a description', async () => {
- const updatePublication = await testUtils.agent
- .patch('/publications/publication-interpretation-draft')
+ const updatedVersion = await testUtils.agent
+ .patch('/publication-versions/publication-interpretation-draft-v1')
.query({ apiKey: 123456789 })
.send({
description:
'testing123testing123testing123testing123testing123testing123testing123testing123testing123testing123testing123testing123testing123testing123testing123testing123x'
});
- expect(updatePublication.status).toEqual(422);
+ expect(updatedVersion.status).toEqual(422);
});
// Language tests
test('Valid publication updated by real user when provided a correct ISO-639-1 language code', async () => {
const createPublicationRequest = await testUtils.agent
- .patch('/publications/publication-interpretation-draft')
+ .patch('/publication-versions/publication-interpretation-draft-v1')
.query({
apiKey: '123456789'
})
@@ -142,7 +142,7 @@ describe('Update publication', () => {
test('Publication failed to be updated if language code provided is not out of the ISO-639-1 language list', async () => {
const createPublicationRequest = await testUtils.agent
- .patch('/publications/publication-interpretation-draft')
+ .patch('/publication-versions/publication-interpretation-draft-v1')
.query({
apiKey: '123456789'
})
@@ -155,7 +155,7 @@ describe('Update publication', () => {
test('Publication failed to be updated if language provided is less than 2 chars', async () => {
const createPublicationRequest = await testUtils.agent
- .patch('/publications/publication-interpretation-draft')
+ .patch('/publication-versions/publication-interpretation-draft-v1')
.query({
apiKey: '123456789'
})
@@ -168,7 +168,7 @@ describe('Update publication', () => {
test('Publication failed to be updated if language provided is more than 2 chars', async () => {
const createPublicationRequest = await testUtils.agent
- .patch('/publications/publication-interpretation-draft')
+ .patch('/publication-versions/publication-interpretation-draft-v1')
.query({
apiKey: '123456789'
})
@@ -181,7 +181,7 @@ describe('Update publication', () => {
test('Publication failed to update if is not protocol or hypotheses and supplies a self declaration', async () => {
const createPublicationRequest = await testUtils.agent
- .patch('/publications/publication-interpretation-draft')
+ .patch('/publication-versions/publication-interpretation-draft-v1')
.query({
apiKey: '123456789'
})
diff --git a/api/src/components/publicationVersion/__tests__/updateStatus.test.ts b/api/src/components/publicationVersion/__tests__/updateStatus.test.ts
new file mode 100644
index 000000000..13218d6b6
--- /dev/null
+++ b/api/src/components/publicationVersion/__tests__/updateStatus.test.ts
@@ -0,0 +1,257 @@
+import * as testUtils from 'lib/testUtils';
+
+beforeEach(async () => {
+ await testUtils.clearDB();
+ await testUtils.testSeed();
+});
+
+describe('Update publication version status', () => {
+ test('User with permissions can update their publication version to LIVE from DRAFT (after creating a link)', async () => {
+ const updatePublicationVersionAttemptOne = await testUtils.agent
+ .put('/publication-versions/publication-analysis-draft-v1/status/LIVE')
+ .query({
+ apiKey: '123456789'
+ });
+
+ expect(updatePublicationVersionAttemptOne.status).toEqual(403);
+
+ // add a valid link
+ await testUtils.agent
+ .post('/links')
+ .query({
+ apiKey: '123456789'
+ })
+ .send({
+ from: 'publication-analysis-draft',
+ to: 'publication-data-live'
+ });
+
+ const updatePublicationVersionAttemptTwo = await testUtils.agent
+ .put('/publication-versions/publication-analysis-draft-v1/status/LIVE')
+ .query({
+ apiKey: '123456789'
+ });
+
+ expect(updatePublicationVersionAttemptTwo.status).toEqual(200);
+ });
+
+ test('User with permissions can update their publication version to LIVE from DRAFT', async () => {
+ const updatePublicationVersion = await testUtils.agent
+ .put('/publication-versions/publication-hypothesis-draft-problem-live-v1/status/LIVE')
+ .query({
+ apiKey: '123456789'
+ });
+
+ expect(updatePublicationVersion.status).toEqual(200);
+ });
+
+ test('User with permissions cannot update their publication to DRAFT from LIVE', async () => {
+ const updatePublicationVersion = await testUtils.agent
+ .put('/publication-versions/publication-problem-live-v1/status/DRAFT')
+ .query({
+ apiKey: '123456789'
+ });
+
+ expect(updatePublicationVersion.status).toEqual(403);
+ });
+
+ test('User without permissions cannot update their publication to LIVE from DRAFT', async () => {
+ const updatePublicationVersion = await testUtils.agent
+ .put('/publication-versions/publication-hypothesis-draft-problem-live-v1/status/LIVE')
+ .query({
+ apiKey: '987654321'
+ });
+
+ expect(updatePublicationVersion.status).toEqual(403);
+ });
+
+ test('User with permissions cannot update their publication to LIVE from DRAFT if there is no content.', async () => {
+ const updatePublicationVersion = await testUtils.agent
+ .put('/publication-versions/publication-problem-draft-no-content-v1/status/LIVE')
+ .query({
+ apiKey: '123456789'
+ });
+
+ expect(updatePublicationVersion.status).toEqual(403);
+ });
+
+ test('User with permissions cannot update their publication to LIVE from DRAFT if there is no licence.', async () => {
+ const updatePublicationVersion = await testUtils.agent
+ .put('/publication-versions/publication-hypothesis-draft-v1/status/LIVE')
+ .query({
+ apiKey: '000000005'
+ });
+
+ expect(updatePublicationVersion.status).toEqual(403);
+ });
+
+ test('User with permissions can update their publication version to LIVE from DRAFT and a publishedDate is created', async () => {
+ const updatePublicationVersion = await testUtils.agent
+ .put('/publication-versions/publication-hypothesis-draft-problem-live-v1/status/LIVE')
+ .query({
+ apiKey: '123456789'
+ });
+
+ expect(updatePublicationVersion.status).toEqual(200);
+ expect(updatePublicationVersion.body.message).toEqual('Publication is now LIVE.');
+ });
+
+ // COI tests
+ test('User with permissions cannot update their publication to LIVE if they have a conflict of interest, but have not provided coi text', async () => {
+ const updatePublicationVersion = await testUtils.agent
+ .put('/publication-versions/publication-problem-draft-with-coi-but-no-text-v1/status/LIVE')
+ .query({
+ apiKey: '123456789'
+ });
+
+ expect(updatePublicationVersion.status).toEqual(403);
+ });
+
+ test('User with permissions can update their publication version to LIVE with a conflict of interest, if they have provided text', async () => {
+ const updatePublicationVersion = await testUtils.agent
+ .put('/publication-versions/publication-problem-draft-with-coi-with-text-v1/status/LIVE')
+ .query({
+ apiKey: '123456789'
+ });
+
+ expect(updatePublicationVersion.status).toEqual(200);
+ });
+
+ test('User with permissions can update their publication version to LIVE if they have no conflict of interest & have not provided text', async () => {
+ const updatePublicationVersion = await testUtils.agent
+ .put('/publication-versions/publication-problem-draft-with-no-coi-with-no-text-v1/status/LIVE')
+ .query({
+ apiKey: '123456789'
+ });
+
+ expect(updatePublicationVersion.status).toEqual(200);
+ });
+
+ test('User with permissions can update their publication version to LIVE if they have no conflict of interest & have provided text', async () => {
+ const updatePublicationVersion = await testUtils.agent
+ .put('/publication-versions/publication-problem-draft-with-no-coi-with-text-v1/status/LIVE')
+ .query({
+ apiKey: '123456789'
+ });
+
+ expect(updatePublicationVersion.status).toEqual(200);
+ });
+
+ test('Publication owner can publish if all co-authors are confirmed', async () => {
+ const updatePublicationVersion = await testUtils.agent
+ .put('/publication-versions/publication-protocol-draft-v1/status/LIVE')
+ .query({
+ apiKey: '000000005'
+ });
+
+ expect(updatePublicationVersion.status).toEqual(200);
+
+ expect(updatePublicationVersion.body.message).toEqual('Publication is now LIVE.');
+ });
+
+ test('Publication owner cannot publish if not all co-authors are confirmed', async () => {
+ const updatePublicationVersion = await testUtils.agent
+ .put('/publication-versions/publication-hypothesis-draft-v1/status/LIVE')
+ .query({
+ apiKey: '000000005'
+ });
+
+ expect(updatePublicationVersion.status).toEqual(403);
+ expect(updatePublicationVersion.body.message).toEqual(
+ 'Publication is not ready to be made LIVE. Make sure all fields are filled in.'
+ );
+
+ const getPublicationVersion = await testUtils.agent
+ .get('/publications/publication-hypothesis-draft/publication-versions/latest')
+ .query({
+ apiKey: '000000005'
+ });
+
+ expect(getPublicationVersion.body.currentStatus).toEqual('DRAFT');
+ });
+
+ test('User other than the owner (does not have permission) cannot publish if co-authors all approved', async () => {
+ const updatePublicationVersion = await testUtils.agent
+ .put('/publication-versions/publication-hypothesis-draft-v1/status/LIVE')
+ .query({
+ apiKey: '000000006'
+ });
+
+ expect(updatePublicationVersion.status).toEqual(403);
+ expect(updatePublicationVersion.body.message).toEqual(
+ 'You do not have permission to modify the status of this publication.'
+ );
+
+ const getPublicationVersion = await testUtils.agent
+ .get('/publications/publication-hypothesis-draft/publication-versions/latest')
+ .query({
+ apiKey: '000000005'
+ });
+
+ expect(getPublicationVersion.body.currentStatus).toEqual('DRAFT');
+ });
+
+ test('Publication owner cannot update publication status to LOCKED if there are no co-authors', async () => {
+ const response = await testUtils.agent.put('/publication-versions/publication-2-v1/status/LOCKED').query({
+ apiKey: '987654321'
+ });
+
+ expect(response.status).toEqual(403);
+ expect(response.body.message).toEqual(
+ 'Publication is not ready to be LOCKED. Make sure all fields are filled in.'
+ );
+ });
+
+ test('Throws an error if trying to update publication status to the same status', async () => {
+ const response = await testUtils.agent.put('/publication-versions/publication-2-v1/status/DRAFT').query({
+ apiKey: '987654321'
+ });
+
+ expect(response.status).toEqual(403);
+ expect(response.body.message).toEqual('Publication status is already DRAFT.');
+ });
+
+ test('Publication status can be updated from DRAFT to LOCKED only after requesting approvals', async () => {
+ // try to update status to LOCKED
+ const updateStatusResponse1 = await testUtils.agent
+ .put('/publication-versions/publication-problem-draft-v1/status/LOCKED')
+ .query({
+ apiKey: '000000005'
+ });
+
+ expect(updateStatusResponse1.status).toEqual(403);
+ expect(updateStatusResponse1.body.message).toEqual(
+ 'Publication is not ready to be LOCKED. Make sure all fields are filled in.'
+ );
+
+ // request co-authors approvals
+ const requestApprovalsResponse = await testUtils.agent
+ .put('/publication-versions/publication-problem-draft-v1/coauthors/request-approval')
+ .query({
+ apiKey: '000000005'
+ });
+
+ expect(requestApprovalsResponse.status).toEqual(200);
+
+ // try to update status to LOCKED again
+ const updateStatusResponse2 = await testUtils.agent
+ .put('/publication-versions/publication-problem-draft-v1/status/LOCKED')
+ .query({
+ apiKey: '000000005'
+ });
+
+ expect(updateStatusResponse2.status).toEqual(200);
+ expect(updateStatusResponse2.body.message).toEqual('Publication status updated to LOCKED.');
+ });
+
+ test('Publication status can be updated from LOCKED to LIVE after all co-authors approved', async () => {
+ const response = await testUtils.agent
+ .put('/publication-versions/locked-publication-problem-confirmed-co-authors-v1/status/LIVE')
+ .query({
+ apiKey: '123456789'
+ });
+
+ expect(response.status).toEqual(200);
+ expect(response.body.message).toEqual('Publication is now LIVE.');
+ });
+});
diff --git a/api/src/components/publicationVersion/controller.ts b/api/src/components/publicationVersion/controller.ts
new file mode 100644
index 000000000..e157801b0
--- /dev/null
+++ b/api/src/components/publicationVersion/controller.ts
@@ -0,0 +1,303 @@
+import htmlToText from 'html-to-text';
+import * as I from 'interface';
+import * as response from 'lib/response';
+import * as publicationVersionService from 'publicationVersion/service';
+import * as publicationService from 'publication/service';
+import * as coAuthorService from 'coauthor/service';
+import * as referenceService from 'reference/service';
+import * as helpers from 'lib/helpers';
+import * as sqs from 'lib/sqs';
+
+export const get = async (
+ event: I.APIRequest
+): Promise => {
+ const { id, version } = event.pathParameters;
+
+ try {
+ const publicationVersion = await publicationVersionService.get(id, version);
+
+ if (!publicationVersion) {
+ return response.json(404, {
+ message: 'Publication version not found.'
+ });
+ }
+
+ // anyone can see a LIVE publication
+ if (publicationVersion.currentStatus === 'LIVE') {
+ return response.json(200, publicationVersion);
+ }
+
+ // only the owner or co-authors can view publications
+ if (
+ event.user?.id === publicationVersion.user.id ||
+ publicationVersion.coAuthors.some((coAuthor) => coAuthor.linkedUser === event.user?.id)
+ ) {
+ return response.json(200, publicationVersion);
+ }
+
+ return response.json(404, {
+ message:
+ 'Publication version is either not found, or you do not have permissions to view it in its current state.'
+ });
+ } catch (err) {
+ console.log(err);
+
+ return response.json(500, { message: 'Unknown server error.' });
+ }
+};
+
+export const getAll = async (
+ event: I.AuthenticatedAPIRequest
+): Promise => {
+ try {
+ const openSearchPublications = await publicationService.getOpenSearchPublications(event.queryStringParameters);
+
+ const publicationIds: string[] = openSearchPublications.body.hits.hits.map((hit) => hit._id as string);
+
+ const publicationVersions = await publicationVersionService.getAllByPublicationIds(publicationIds);
+
+ const versionsOrderedBySearch = publicationIds.map((publicationId) =>
+ publicationVersions.find((version) => version.versionOf === publicationId)
+ );
+
+ return response.json(200, {
+ data: versionsOrderedBySearch,
+ metadata: {
+ total: openSearchPublications.body.hits.total.value,
+ limit: Number(event.queryStringParameters.limit) || 10,
+ offset: Number(event.queryStringParameters.offset) || 0
+ }
+ });
+ } catch (err) {
+ console.log(err);
+
+ return response.json(500, { message: 'Unknown server error.' });
+ }
+};
+
+export const update = async (
+ event: I.AuthenticatedAPIRequest<
+ I.UpdatePublicationVersionRequestBody,
+ undefined,
+ I.UpdatePublicationVersionPathParams
+ >
+): Promise => {
+ try {
+ const publicationVersion = await publicationVersionService.getById(event.pathParameters.id);
+
+ if (!publicationVersion) {
+ return response.json(403, {
+ message: 'This publication version does not exist.'
+ });
+ }
+
+ if (publicationVersion.user.id !== event.user.id) {
+ return response.json(403, {
+ message: 'You do not have permission to modify this publication version.'
+ });
+ }
+
+ if (publicationVersion.currentStatus !== 'DRAFT') {
+ return response.json(404, {
+ message: 'A publication version that is not in DRAFT state cannot be updated.'
+ });
+ }
+
+ if (event.body.content) {
+ event.body.content = helpers.getSafeHTML(event.body.content);
+ }
+
+ if (
+ event.body.selfDeclaration !== undefined &&
+ publicationVersion.publication.type !== 'PROTOCOL' &&
+ publicationVersion.publication.type !== 'HYPOTHESIS'
+ ) {
+ return response.json(400, {
+ message:
+ 'You can not declare a self declaration for a publication that is not a protocol or hypothesis.'
+ });
+ }
+
+ if (event.body.dataAccessStatement !== undefined && publicationVersion.publication.type !== 'DATA') {
+ return response.json(400, {
+ message: 'You can not supply a data access statement on a non data publication.'
+ });
+ }
+
+ if (event.body.dataPermissionsStatement !== undefined && publicationVersion.publication.type !== 'DATA') {
+ return response.json(400, {
+ message: 'You can not supply a data permissions statement on a non data publication.'
+ });
+ }
+
+ await publicationVersionService.update(event.pathParameters.id, event.body);
+
+ const updatedPublicationVersion = await publicationVersionService.getById(event.pathParameters.id);
+
+ return response.json(200, updatedPublicationVersion);
+ } catch (err) {
+ console.log(err);
+
+ return response.json(500, { message: 'Unknown server error.' });
+ }
+};
+
+export const updateStatus = async (
+ event: I.AuthenticatedAPIRequest
+): Promise => {
+ try {
+ const publicationVersion = await publicationVersionService.getById(event.pathParameters.id);
+
+ if (!publicationVersion) {
+ return response.json(404, {
+ message: 'This publication version does not exist.'
+ });
+ }
+
+ if (publicationVersion.createdBy !== event.user.id) {
+ return response.json(403, {
+ message: 'You do not have permission to modify the status of this publication.'
+ });
+ }
+
+ const newStatus = event.pathParameters?.status;
+ const currentStatus = publicationVersion.currentStatus;
+
+ if (currentStatus === 'LIVE') {
+ return response.json(403, {
+ message: 'A status of a publication that is not in DRAFT or LOCKED cannot be changed.'
+ });
+ }
+
+ if (currentStatus === newStatus) {
+ return response.json(403, { message: `Publication status is already ${newStatus}.` });
+ }
+
+ if (currentStatus === 'DRAFT') {
+ if (newStatus === 'LOCKED') {
+ // check if publication version actually has co-authors
+ if (publicationVersion.coAuthors.length === 1) {
+ return response.json(403, { message: 'Publication cannot be LOCKED without co-authors.' });
+ }
+
+ // check if publication version is ready to be LOCKED
+ if (!(await publicationVersionService.checkIsReadyToLock(publicationVersion))) {
+ return response.json(403, {
+ message: 'Publication is not ready to be LOCKED. Make sure all fields are filled in.'
+ });
+ }
+
+ // Lock publication from editing
+ await publicationVersionService.updateStatus(publicationVersion.id, 'LOCKED');
+
+ return response.json(200, { message: 'Publication status updated to LOCKED.' });
+ }
+
+ if (newStatus === 'LIVE') {
+ const isReadyToPublish = await publicationVersionService.checkIsReadyToPublish(publicationVersion);
+
+ if (!isReadyToPublish) {
+ return response.json(403, {
+ message: 'Publication is not ready to be made LIVE. Make sure all fields are filled in.'
+ });
+ }
+ }
+ }
+
+ if (currentStatus === 'LOCKED') {
+ if (newStatus === 'DRAFT') {
+ // Update status to 'DRAFT'
+ await publicationVersionService.updateStatus(publicationVersion.id, newStatus);
+
+ // Cancel co author approvals
+ await coAuthorService.resetCoAuthors(publicationVersion.id);
+
+ return response.json(200, {
+ message: 'Publication unlocked for editing'
+ });
+ }
+
+ if (newStatus === 'LIVE') {
+ const isReadyToPublish = await publicationVersionService.checkIsReadyToPublish(publicationVersion);
+
+ if (!isReadyToPublish) {
+ return response.json(403, {
+ message: 'Publication is not ready to be made LIVE. Make sure all fields are filled in.'
+ });
+ }
+ }
+ }
+
+ const updatedVersion = await publicationVersionService.updateStatus(publicationVersion.id, newStatus);
+
+ // now that the publication version is LIVE, add/update the opensearch record
+ await publicationService.createOpenSearchRecord({
+ id: updatedVersion.versionOf,
+ type: updatedVersion.publication.type,
+ title: updatedVersion.title,
+ licence: updatedVersion.licence,
+ description: updatedVersion.description,
+ keywords: updatedVersion.keywords,
+ content: updatedVersion.content,
+ publishedDate: updatedVersion.publishedDate,
+ cleanContent: htmlToText.convert(updatedVersion.content)
+ });
+
+ const references = await referenceService.getAllByPublicationVersion(updatedVersion.id);
+ const { linkedTo } = await publicationService.getDirectLinksForPublication(publicationVersion.versionOf, true);
+
+ // Publication version is live, so update the DOI
+ await helpers.updateDOI(publicationVersion.publication.doi, publicationVersion, linkedTo, references);
+
+ // send message to the pdf generation queue
+ // currently only on deployed instances while a local solution is developed
+ if (process.env.STAGE !== 'local') await sqs.sendMessage(publicationVersion.versionOf);
+
+ return response.json(200, { message: 'Publication is now LIVE.' });
+ } catch (err) {
+ console.log(err);
+
+ return response.json(500, { message: 'Unknown server error.' });
+ }
+};
+
+export const deleteVersion = async (
+ event: I.AuthenticatedAPIRequest
+): Promise => {
+ try {
+ const publicationVersion = await publicationVersionService.getById(event.pathParameters.id);
+
+ if (!publicationVersion) {
+ return response.json(403, {
+ message: 'This publication version does not exist.'
+ });
+ }
+
+ if (publicationVersion.user.id !== event.user.id) {
+ return response.json(403, {
+ message: 'You do not have permission to delete this publication.'
+ });
+ }
+
+ // The logic here is a bit odd, but the currentStatus and publicationStatus array are not intrinsically linked
+ // so to be safe, we are checking that the current status is DRAFT and that the entire history of the publication
+ // has only ever been draft.
+ if (
+ publicationVersion.currentStatus !== 'DRAFT' ||
+ (publicationVersion.publicationStatus &&
+ !publicationVersion.publicationStatus.every((status) => status.status !== 'LIVE'))
+ ) {
+ return response.json(403, {
+ message: 'A publication can only be deleted if it is currently a draft and has never been LIVE.'
+ });
+ }
+
+ await publicationVersionService.deleteVersion(publicationVersion);
+
+ return response.json(200, { message: `Publication version ${event.pathParameters.id} has been deleted` });
+ } catch (err) {
+ console.log(err);
+
+ return response.json(500, { message: 'Unknown server error.' });
+ }
+};
diff --git a/api/src/components/publicationVersion/routes.ts b/api/src/components/publicationVersion/routes.ts
new file mode 100644
index 000000000..1414dcb58
--- /dev/null
+++ b/api/src/components/publicationVersion/routes.ts
@@ -0,0 +1,32 @@
+import middy from '@middy/core';
+
+import * as middleware from 'middleware';
+import * as publicationVersionController from './controller';
+import * as publicationVersionSchema from './schema';
+
+export const get = middy(publicationVersionController.get)
+ .use(middleware.doNotWaitForEmptyEventLoop({ runOnError: true, runOnBefore: true, runOnAfter: true }))
+ .use(middleware.httpJsonBodyParser())
+ .use(middleware.authentication(true));
+
+export const getAll = middy(publicationVersionController.getAll)
+ .use(middleware.doNotWaitForEmptyEventLoop({ runOnError: true, runOnBefore: true, runOnAfter: true }))
+ .use(middleware.httpJsonBodyParser())
+ .use(middleware.validator(publicationVersionSchema.getAll, 'queryStringParameters'));
+
+export const update = middy(publicationVersionController.update)
+ .use(middleware.doNotWaitForEmptyEventLoop({ runOnError: true, runOnBefore: true, runOnAfter: true }))
+ .use(middleware.httpJsonBodyParser())
+ .use(middleware.authentication())
+ .use(middleware.validator(publicationVersionSchema.update, 'body'));
+
+export const updateStatus = middy(publicationVersionController.updateStatus)
+ .use(middleware.doNotWaitForEmptyEventLoop({ runOnError: true, runOnBefore: true, runOnAfter: true }))
+ .use(middleware.httpJsonBodyParser())
+ .use(middleware.authentication())
+ .use(middleware.validator(publicationVersionSchema.updateStatus, 'pathParameters'));
+
+export const deleteVersion = middy(publicationVersionController.deleteVersion)
+ .use(middleware.doNotWaitForEmptyEventLoop({ runOnError: true, runOnBefore: true, runOnAfter: true }))
+ .use(middleware.httpJsonBodyParser())
+ .use(middleware.authentication());
diff --git a/api/src/components/publication/schema/getAll.ts b/api/src/components/publicationVersion/schema/getAll.ts
similarity index 92%
rename from api/src/components/publication/schema/getAll.ts
rename to api/src/components/publicationVersion/schema/getAll.ts
index 0c5d3f9d7..663fcad4f 100644
--- a/api/src/components/publication/schema/getAll.ts
+++ b/api/src/components/publicationVersion/schema/getAll.ts
@@ -1,6 +1,6 @@
import * as I from 'interface';
-const getAllSchema: I.JSONSchemaType = {
+const getAll: I.JSONSchemaType = {
type: 'object',
properties: {
type: {
@@ -48,4 +48,4 @@ const getAllSchema: I.JSONSchemaType = {
additionalProperties: false
};
-export default getAllSchema;
+export default getAll;
diff --git a/api/src/components/publicationVersion/schema/index.ts b/api/src/components/publicationVersion/schema/index.ts
new file mode 100644
index 000000000..9cc369163
--- /dev/null
+++ b/api/src/components/publicationVersion/schema/index.ts
@@ -0,0 +1,3 @@
+export { default as getAll } from './getAll';
+export { default as update } from './update';
+export { default as updateStatus } from './updateStatus';
diff --git a/api/src/components/publication/schema/update.ts b/api/src/components/publicationVersion/schema/update.ts
similarity index 93%
rename from api/src/components/publication/schema/update.ts
rename to api/src/components/publicationVersion/schema/update.ts
index 1baff73d7..9e273c65b 100644
--- a/api/src/components/publication/schema/update.ts
+++ b/api/src/components/publicationVersion/schema/update.ts
@@ -1,7 +1,7 @@
import * as I from 'interface';
import * as H from 'lib/helpers';
-const updatePublicationSchema: I.Schema = {
+const updatePublicationVersionSchema: I.Schema = {
type: 'object',
properties: {
title: {
@@ -61,4 +61,4 @@ const updatePublicationSchema: I.Schema = {
additionalProperties: false
};
-export default updatePublicationSchema;
+export default updatePublicationVersionSchema;
diff --git a/api/src/components/publication/schema/updateStatus.ts b/api/src/components/publicationVersion/schema/updateStatus.ts
similarity index 79%
rename from api/src/components/publication/schema/updateStatus.ts
rename to api/src/components/publicationVersion/schema/updateStatus.ts
index afd14fb5b..62af1f796 100644
--- a/api/src/components/publication/schema/updateStatus.ts
+++ b/api/src/components/publicationVersion/schema/updateStatus.ts
@@ -1,6 +1,6 @@
import * as I from 'interface';
-const updatedPublicationSchema: I.Schema = {
+const updateStatusSchema: I.Schema = {
type: 'object',
properties: {
status: {
@@ -15,4 +15,4 @@ const updatedPublicationSchema: I.Schema = {
additionalProperties: false
};
-export default updatedPublicationSchema;
+export default updateStatusSchema;
diff --git a/api/src/components/publicationVersion/service.ts b/api/src/components/publicationVersion/service.ts
index ed3ecc7d4..c17f2714a 100644
--- a/api/src/components/publicationVersion/service.ts
+++ b/api/src/components/publicationVersion/service.ts
@@ -1,9 +1,11 @@
import { Prisma } from '@prisma/client';
import * as I from 'interface';
import * as client from 'lib/client';
+import * as publicationService from 'publication/service';
+import * as Helpers from 'lib/helpers';
-export const get = async (id: string) => {
- const publicationVersion = client.prisma.publicationVersion.findFirst({
+export const getById = (id: string) =>
+ client.prisma.publicationVersion.findFirst({
where: {
id
},
@@ -13,9 +15,7 @@ export const get = async (id: string) => {
id: true,
type: true,
doi: true,
- linkedTo: true,
- linkedFrom: true,
- topics: true
+ url_slug: true
}
},
publicationStatus: {
@@ -76,9 +76,147 @@ export const get = async (id: string) => {
}
});
- return publicationVersion;
+export const get = (publicationId: string, version: string | number) =>
+ client.prisma.publicationVersion.findFirst({
+ where: {
+ versionOf: publicationId,
+ ...(typeof version === 'number' || Number(version)
+ ? { versionNumber: Number(version) }
+ : version === 'latest'
+ ? { isLatestVersion: true }
+ : version === 'latestLive'
+ ? {
+ isLatestLiveVersion: true
+ }
+ : { id: version })
+ },
+ include: {
+ publication: {
+ select: {
+ id: true,
+ type: true,
+ doi: true,
+ url_slug: true
+ }
+ },
+ publicationStatus: {
+ select: {
+ status: true,
+ createdAt: true,
+ id: true
+ },
+ orderBy: {
+ createdAt: 'desc'
+ }
+ },
+ funders: {
+ select: {
+ id: true,
+ city: true,
+ country: true,
+ name: true,
+ link: true,
+ ror: true
+ }
+ },
+ coAuthors: {
+ select: {
+ id: true,
+ email: true,
+ linkedUser: true,
+ publicationVersionId: true,
+ confirmedCoAuthor: true,
+ approvalRequested: true,
+ createdAt: true,
+ reminderDate: true,
+ isIndependent: true,
+ affiliations: true,
+ user: {
+ select: {
+ firstName: true,
+ lastName: true,
+ orcid: true
+ }
+ }
+ },
+ orderBy: {
+ position: 'asc'
+ }
+ },
+ user: {
+ select: {
+ id: true,
+ orcid: true,
+ firstName: true,
+ lastName: true,
+ email: true,
+ createdAt: true,
+ updatedAt: true
+ }
+ }
+ }
+ });
+
+export const getAllByPublicationIds = async (ids: string[]) => {
+ // Get latest versions of these publications
+ const latestVersions = await client.prisma.publicationVersion.findMany({
+ where: {
+ versionOf: {
+ in: ids
+ },
+ isLatestLiveVersion: true
+ },
+ include: {
+ publication: {
+ select: {
+ id: true,
+ type: true,
+ doi: true,
+ url_slug: true
+ }
+ },
+ user: {
+ select: {
+ firstName: true,
+ lastName: true,
+ id: true,
+ orcid: true
+ }
+ },
+ coAuthors: {
+ select: {
+ id: true,
+ linkedUser: true,
+ user: {
+ select: {
+ orcid: true,
+ firstName: true,
+ lastName: true
+ }
+ }
+ },
+ orderBy: {
+ position: 'asc'
+ }
+ }
+ }
+ });
+
+ if (ids.length !== latestVersions.length) {
+ throw Error('Unable to find all latest versions for all requested publications.');
+ }
+
+ return latestVersions;
};
+export const update = (id: string, updateContent: I.UpdatePublicationVersionRequestBody) =>
+ client.prisma.publicationVersion.update({
+ where: {
+ id
+ },
+ data: updateContent
+ });
+
export const updateStatus = async (id: string, status: I.PublicationStatusEnum) => {
const query = {
where: {
@@ -136,3 +274,133 @@ export const validateConflictOfInterest = (version: I.PublicationVersion) => {
return true;
};
+
+export const checkIsReadyToPublish = async (publicationVersion: I.PublicationVersion): Promise => {
+ if (!publicationVersion) {
+ return false;
+ }
+
+ const { linkedTo } = await publicationService.getDirectLinksForPublication(publicationVersion.versionOf, true);
+ const topics = await publicationService.getPublicationTopics(publicationVersion.versionOf);
+
+ const hasAtLeastOneLinkOrTopic =
+ linkedTo.length !== 0 || (publicationVersion.publication.type === 'PROBLEM' && topics.length !== 0);
+ const hasFilledRequiredFields =
+ ['title', 'licence'].every((field) => publicationVersion[field]) &&
+ !Helpers.isEmptyContent(publicationVersion.content || '');
+ const conflictOfInterest = validateConflictOfInterest(publicationVersion);
+ const hasPublishDate = Boolean(publicationVersion.publishedDate);
+ const isDataAndHasEthicalStatement =
+ publicationVersion.publication.type === 'DATA' ? publicationVersion.ethicalStatement !== null : true;
+ const isDataAndHasPermissionsStatement =
+ publicationVersion.publication.type === 'DATA' ? publicationVersion.dataPermissionsStatement !== null : true;
+
+ const coAuthorsAreVerified = !!publicationVersion.coAuthors.every(
+ (coAuthor) => coAuthor.confirmedCoAuthor && (coAuthor.isIndependent || coAuthor.affiliations.length)
+ );
+
+ return (
+ hasAtLeastOneLinkOrTopic &&
+ hasFilledRequiredFields &&
+ conflictOfInterest &&
+ !hasPublishDate &&
+ isDataAndHasEthicalStatement &&
+ isDataAndHasPermissionsStatement &&
+ coAuthorsAreVerified &&
+ publicationVersion.isLatestVersion
+ );
+};
+
+export const checkIsReadyToRequestApprovals = async (publicationVersion: I.PublicationVersion): Promise => {
+ if (!publicationVersion) {
+ return false;
+ }
+
+ if (!publicationVersion.isLatestVersion || publicationVersion.currentStatus !== 'DRAFT') {
+ return false;
+ }
+
+ const { linkedTo } = await publicationService.getDirectLinksForPublication(publicationVersion.versionOf, true);
+ const topics = await publicationService.getPublicationTopics(publicationVersion.versionOf);
+
+ const hasAtLeastOneLinkOrTopic =
+ linkedTo.length !== 0 || (publicationVersion.publication.type === 'PROBLEM' && topics.length !== 0);
+ const hasFilledRequiredFields =
+ ['title', 'licence'].every((field) => publicationVersion[field]) &&
+ !Helpers.isEmptyContent(publicationVersion.content || '');
+ const conflictOfInterest = validateConflictOfInterest(publicationVersion);
+ const isDataAndHasEthicalStatement =
+ publicationVersion.publication.type === 'DATA' ? publicationVersion.ethicalStatement !== null : true;
+ const isDataAndHasPermissionsStatement =
+ publicationVersion.publication.type === 'DATA' ? publicationVersion.dataPermissionsStatement !== null : true;
+ const hasConfirmedAffiliations = !!publicationVersion.coAuthors.some(
+ (author) =>
+ author.linkedUser === publicationVersion.createdBy && (author.isIndependent || author.affiliations.length)
+ );
+
+ return (
+ hasAtLeastOneLinkOrTopic &&
+ hasFilledRequiredFields &&
+ conflictOfInterest &&
+ isDataAndHasEthicalStatement &&
+ isDataAndHasPermissionsStatement &&
+ hasConfirmedAffiliations &&
+ publicationVersion.isLatestVersion
+ );
+};
+
+export const checkIsReadyToLock = async (publicationVersion: I.PublicationVersion): Promise => {
+ if (!publicationVersion) {
+ return false;
+ }
+
+ if (publicationVersion.currentStatus !== 'DRAFT') {
+ return false;
+ }
+
+ const isReadyToRequestApprovals = await checkIsReadyToRequestApprovals(publicationVersion);
+ const hasRequestedApprovals = !!publicationVersion.coAuthors.some((author) => author.approvalRequested);
+
+ return isReadyToRequestApprovals && hasRequestedApprovals;
+};
+
+export const deleteVersion = async (publicationVersion: I.PublicationVersion) => {
+ if (
+ publicationVersion.isLatestVersion &&
+ publicationVersion.versionNumber === 1 &&
+ publicationVersion.currentStatus !== 'LIVE'
+ ) {
+ // if there's only one DRAFT version and that's the latest one, we can safely delete the entire publication
+ await publicationService.deletePublication(publicationVersion.versionOf);
+ } else {
+ // delete this version
+ await client.prisma.publicationVersion.delete({
+ where: {
+ id: publicationVersion.id
+ }
+ });
+
+ // get previous version
+ const previousVersion = await client.prisma.publicationVersion.findFirst({
+ where: {
+ versionOf: publicationVersion.versionOf,
+ versionNumber: publicationVersion.versionNumber - 1
+ },
+ select: {
+ id: true
+ }
+ });
+
+ if (previousVersion) {
+ // make the previous version "isLatestVersion=true"
+ await client.prisma.publicationVersion.update({
+ where: {
+ id: previousVersion.id
+ },
+ data: {
+ isLatestVersion: true
+ }
+ });
+ }
+ }
+};
diff --git a/api/src/components/reference/__tests__/getReference.test.ts b/api/src/components/reference/__tests__/getReference.test.ts
index 91adc909d..0e16740fb 100644
--- a/api/src/components/reference/__tests__/getReference.test.ts
+++ b/api/src/components/reference/__tests__/getReference.test.ts
@@ -8,7 +8,7 @@ describe('get references', () => {
test('Any user (unauthenticated) can get the references for a live publication', async () => {
const reference = await testUtils.agent.get(
- '/publicationVersions/publication-real-world-application-live-v1/reference'
+ '/publication-versions/publication-real-world-application-live-v1/references'
);
expect(reference.status).toEqual(200);
diff --git a/api/src/components/reference/__tests__/updateAllReferences.test.ts b/api/src/components/reference/__tests__/updateAllReferences.test.ts
index 44f21c285..a08d15c38 100644
--- a/api/src/components/reference/__tests__/updateAllReferences.test.ts
+++ b/api/src/components/reference/__tests__/updateAllReferences.test.ts
@@ -8,7 +8,7 @@ describe('update all references', () => {
test('User can update all references from their own draft publication', async () => {
const reference = await testUtils.agent
- .put('/publicationVersions/publication-interpretation-draft-v1/reference')
+ .put('/publication-versions/publication-interpretation-draft-v1/references')
.query({ apiKey: '123456789' })
.send([
{
@@ -25,7 +25,7 @@ describe('update all references', () => {
test('User must be the author of the publication to update the references', async () => {
const reference = await testUtils.agent
- .put('/publicationVersions/publication-interpretation-draft-v1/reference')
+ .put('/publication-versions/publication-interpretation-draft-v1/references')
.query({ apiKey: '987654321' })
.send([
{
@@ -42,7 +42,7 @@ describe('update all references', () => {
test('The author can only update the references for a draft publication', async () => {
const reference = await testUtils.agent
- .put('/publicationVersions/publication-real-world-application-live-v1/reference')
+ .put('/publication-versions/publication-real-world-application-live-v1/references')
.query({ apiKey: '123456789' })
.send([
{
@@ -76,12 +76,12 @@ describe('update all references', () => {
];
const reference = await testUtils.agent
- .put('/publicationVersions/publication-interpretation-draft-v1/reference')
+ .put('/publication-versions/publication-interpretation-draft-v1/references')
.query({ apiKey: '123456789' })
.send(newReferencesArray);
const checkReference = await testUtils.agent.get(
- '/publicationVersions/publication-interpretation-draft-v1/reference'
+ '/publication-versions/publication-interpretation-draft-v1/references'
);
expect(reference.body.count).toEqual(2);
diff --git a/api/src/components/reference/controller.ts b/api/src/components/reference/controller.ts
index 72a731a59..1e52ff9f0 100644
--- a/api/src/components/reference/controller.ts
+++ b/api/src/components/reference/controller.ts
@@ -19,7 +19,7 @@ export const updateAll = async (
event: I.AuthenticatedAPIRequest
): Promise => {
try {
- const version = await publicationVersionService.get(event.pathParameters.id);
+ const version = await publicationVersionService.getById(event.pathParameters.id);
//check that the version exists
if (!version) {
diff --git a/api/src/components/user/__tests__/getUserPublications.test.ts b/api/src/components/user/__tests__/getUserPublications.test.ts
index 58e633a4b..7d5c03241 100644
--- a/api/src/components/user/__tests__/getUserPublications.test.ts
+++ b/api/src/components/user/__tests__/getUserPublications.test.ts
@@ -1,41 +1,43 @@
import * as testUtils from 'lib/testUtils';
-describe('Get a given users publications', () => {
- beforeEach(async () => {
+describe('Get a given users publication versions', () => {
+ beforeAll(async () => {
await testUtils.clearDB();
await testUtils.testSeed();
});
- test('Current user can view publications including drafts', async () => {
- const publications = await testUtils.agent
- .get('/users/test-user-1/publications')
+ test('Current user can view publication versions including drafts', async () => {
+ const versions = await testUtils.agent
+ .get('/users/test-user-1/publication-versions')
.query({ apiKey: 123456789, offset: 0, limit: 100 });
- expect(publications.status).toEqual(200);
- expect(publications.body.results.length).toEqual(20);
+ expect(versions.status).toEqual(200);
+ expect(versions.body.results.length).toEqual(20);
});
- test('Unauthenticated user can only view live publications', async () => {
- const publications = await testUtils.agent.get('/users/test-user-1/publications');
+ test('Unauthenticated user can only view live versions', async () => {
+ const versions = await testUtils.agent.get('/users/test-user-1/publication-versions');
- expect(publications.status).toEqual(200);
- expect(publications.body.results.length).toEqual(8);
+ expect(versions.status).toEqual(200);
+ expect(versions.body.results.length).toEqual(8);
});
- test('An authenticated user can only view live publications of another user', async () => {
- const publications = await testUtils.agent.get('/users/test-user-1/publications').query({ apiKey: 987654321 });
+ test('An authenticated user can only view live versions of another user', async () => {
+ const versions = await testUtils.agent
+ .get('/users/test-user-1/publication-versions')
+ .query({ apiKey: 987654321 });
- expect(publications.status).toEqual(200);
- expect(publications.body.results.length).toEqual(8);
+ expect(versions.status).toEqual(200);
+ expect(versions.body.results.length).toEqual(8);
});
test('Error message returned for a user that does not exist', async () => {
- const publications = await testUtils.agent
- .get('/users/user-does-not-exist/publications')
+ const versions = await testUtils.agent
+ .get('/users/user-does-not-exist/publication-versions')
.query({ apiKey: 987654321 });
- expect(publications.body.results).toBe(undefined);
- expect(publications.body.message).toBe('User not found');
- expect(publications.status).toEqual(400);
+ expect(versions.body.results).toBe(undefined);
+ expect(versions.body.message).toBe('User not found');
+ expect(versions.status).toEqual(400);
});
});
diff --git a/api/src/components/user/controller.ts b/api/src/components/user/controller.ts
index 0d8da2236..6cd2e583e 100644
--- a/api/src/components/user/controller.ts
+++ b/api/src/components/user/controller.ts
@@ -35,8 +35,8 @@ export const get = async (
}
};
-export const getPublications = async (
- event: I.OptionalAuthenticatedAPIRequest
+export const getPublicationVersions = async (
+ event: I.OptionalAuthenticatedAPIRequest
): Promise => {
try {
const isAccountOwner = Boolean(event.user?.id === event.pathParameters.id);
@@ -47,13 +47,13 @@ export const getPublications = async (
return response.json(400, { message: 'User not found' });
}
- const userPublications = await userService.getPublications(
+ const userPublicationVersions = await userService.getPublicationVersions(
event.pathParameters.id,
event.queryStringParameters,
isAccountOwner
);
- return response.json(200, userPublications);
+ return response.json(200, userPublicationVersions);
} catch (err) {
console.log(err);
diff --git a/api/src/components/user/routes.ts b/api/src/components/user/routes.ts
index baf8719b2..c62cf0df9 100644
--- a/api/src/components/user/routes.ts
+++ b/api/src/components/user/routes.ts
@@ -14,10 +14,10 @@ export const get = middy(userController.get)
.use(middleware.httpJsonBodyParser())
.use(middleware.authentication(true));
-export const getPublications = middy(userController.getPublications)
+export const getPublicationVersions = middy(userController.getPublicationVersions)
.use(middleware.doNotWaitForEmptyEventLoop({ runOnError: true, runOnBefore: true, runOnAfter: true }))
.use(middleware.httpJsonBodyParser())
.use(middleware.authentication(true))
- .use(middleware.validator(userSchema.getPublications, 'queryStringParameters'));
+ .use(middleware.validator(userSchema.getPublicationVersions, 'queryStringParameters'));
export const getUserList = userController.getUserList;
diff --git a/api/src/components/user/schema/getPublications.ts b/api/src/components/user/schema/getPublicationVersions.ts
similarity index 81%
rename from api/src/components/user/schema/getPublications.ts
rename to api/src/components/user/schema/getPublicationVersions.ts
index e35d01fe1..7eb685750 100644
--- a/api/src/components/user/schema/getPublications.ts
+++ b/api/src/components/user/schema/getPublicationVersions.ts
@@ -1,6 +1,6 @@
import * as I from 'interface';
-const getPublicationsSchema: I.JSONSchemaType = {
+const getPublicationVersionsSchema: I.JSONSchemaType = {
type: 'object',
properties: {
offset: {
@@ -27,4 +27,4 @@ const getPublicationsSchema: I.JSONSchemaType = {
required: []
};
-export default getPublicationsSchema;
+export default getPublicationVersionsSchema;
diff --git a/api/src/components/user/schema/index.ts b/api/src/components/user/schema/index.ts
index 3e40e4f77..b58962c7b 100644
--- a/api/src/components/user/schema/index.ts
+++ b/api/src/components/user/schema/index.ts
@@ -1,2 +1,2 @@
export { default as getAll } from './getAll';
-export { default as getPublications } from './getPublications';
+export { default as getPublicationVersions } from './getPublicationVersions';
diff --git a/api/src/components/user/service.ts b/api/src/components/user/service.ts
index a77ded77d..9ff9404f7 100644
--- a/api/src/components/user/service.ts
+++ b/api/src/components/user/service.ts
@@ -132,88 +132,71 @@ export const get = async (id: string, isAccountOwner = false) => {
return user;
};
-export const getPublications = async (id: string, params: I.UserPublicationsFilters, isAccountOwner: boolean) => {
+export const getPublicationVersions = async (
+ id: string,
+ params: I.UserPublicationVersionsFilters,
+ isAccountOwner: boolean
+) => {
const { offset, limit, orderBy, orderDirection } = params;
// Account owners can retrieve their DRAFT publications also
const statuses: Array = isAccountOwner ? ['DRAFT', 'LIVE', 'LOCKED'] : ['LIVE'];
- const where: Prisma.PublicationWhereInput = {
+ const where: Prisma.PublicationVersionWhereInput = {
OR: [
{
- versions: {
- some: {
- isLatestVersion: true,
- createdBy: id
- }
- }
+ createdBy: id
},
{
- versions: {
+ coAuthors: {
some: {
- isLatestVersion: true,
- coAuthors: {
- some: {
- linkedUser: id
- }
- }
+ linkedUser: id
}
}
}
],
- versions: {
- some: {
- isLatestVersion: true,
- currentStatus: {
- in: statuses
- }
- }
- }
+ currentStatus: {
+ in: statuses
+ },
+ ...(isAccountOwner ? { isLatestVersion: true } : { isLatestLiveVersion: true })
};
- const userPublications = await client.prisma.publication.findMany({
+ const userPublicationVersions = await client.prisma.publicationVersion.findMany({
skip: offset,
take: limit,
where,
- select: {
- id: true,
- type: true,
- doi: true,
- url_slug: true,
- versions: {
- where: {
- isLatestVersion: true
- },
+ include: {
+ publication: {
select: {
- createdBy: true,
- createdAt: true,
- updatedAt: true,
- title: true,
- publishedDate: true,
- currentStatus: true,
- licence: true,
- content: true,
- coAuthors: {
+ id: true,
+ type: true,
+ doi: true,
+ url_slug: true
+ }
+ },
+ user: {
+ select: {
+ firstName: true,
+ lastName: true,
+ id: true,
+ orcid: true
+ }
+ },
+ coAuthors: {
+ select: {
+ id: true,
+ linkedUser: true,
+ confirmedCoAuthor: true,
+ user: {
select: {
- id: true,
- approvalRequested: true,
- confirmedCoAuthor: true,
- code: true,
- email: true,
- publicationVersionId: true,
- linkedUser: true,
- user: {
- select: {
- orcid: true,
- firstName: true,
- lastName: true
- }
- }
- },
- orderBy: {
- position: 'asc'
+ orcid: true,
+ firstName: true,
+ lastName: true
}
}
+ },
+ orderBy: {
+ position: 'asc'
}
}
},
@@ -225,21 +208,9 @@ export const getPublications = async (id: string, params: I.UserPublicationsFilt
: undefined
});
- // Simplify publications
- const simplifiedPublications = userPublications.map((publication) => {
- const simplifiedPublication = {
- ...publication,
- ...publication.versions[0]
- };
- // Discard versions field
- const { versions, ...simplifiedPublicationRest } = simplifiedPublication;
-
- return simplifiedPublicationRest;
- });
-
- const totalUserPublications = await client.prisma.publication.count({ where });
+ const totalUserPublications = await client.prisma.publicationVersion.count({ where });
- return { offset, limit, total: totalUserPublications, results: simplifiedPublications };
+ return { offset, limit, total: totalUserPublications, results: userPublicationVersions };
};
export const getUserList = async () => {
diff --git a/api/src/lib/helpers.ts b/api/src/lib/helpers.ts
index 6f1dc598b..7e048deea 100644
--- a/api/src/lib/helpers.ts
+++ b/api/src/lib/helpers.ts
@@ -84,22 +84,21 @@ export const getFullDOIsStrings = (text: string): [] | RegExpMatchArray =>
export const updateDOI = async (
doi: string,
- publication: I.PublicationWithVersionAttached,
+ publicationVersion: I.PublicationVersion,
+ linkedTo: I.LinkedToPublication[],
references: I.Reference[]
): Promise => {
- if (!publication) {
+ if (!publicationVersion) {
throw Error('Publication not found');
}
- const currentVersion = publication.versions[0];
-
- if (!currentVersion.isLatestVersion) {
+ if (!publicationVersion.isLatestVersion) {
throw Error('Supplied version is not current');
}
const creators: I.DataCiteCreator[] = [];
- currentVersion.coAuthors.forEach((author) => {
+ publicationVersion.coAuthors.forEach((author) => {
if (author.user) {
creators.push(
createCreatorObject({
@@ -112,10 +111,10 @@ export const updateDOI = async (
}
});
- const linkedPublications = publication.linkedTo.map((relatedIdentifier) => ({
- relatedIdentifier: relatedIdentifier.publicationToRef.doi,
+ const linkedPublications = linkedTo.map((link) => ({
+ relatedIdentifier: link.doi,
relatedIdentifierType: 'DOI',
- relationType: relatedIdentifier.publicationToRef.type === 'PEER_REVIEW' ? 'Reviews' : 'Continues'
+ relationType: link.type === 'PEER_REVIEW' ? 'Reviews' : 'Continues'
}));
const doiReferences = references.map((reference) => {
@@ -157,13 +156,13 @@ export const updateDOI = async (
});
// check if the creator of this version of the publication is not listed as an author
- if (!currentVersion.coAuthors.find((author) => author.linkedUser === currentVersion.createdBy)) {
+ if (!publicationVersion.coAuthors.find((author) => author.linkedUser === publicationVersion.createdBy)) {
// add creator to authors list as first author
creators?.unshift(
createCreatorObject({
- firstName: currentVersion.user.firstName,
- lastName: currentVersion.user.lastName,
- orcid: currentVersion.user.orcid,
+ firstName: publicationVersion.user.firstName,
+ lastName: publicationVersion.user.lastName,
+ orcid: publicationVersion.user.orcid,
affiliations: []
})
);
@@ -174,7 +173,7 @@ export const updateDOI = async (
types: 'doi',
attributes: {
event: 'publish',
- url: `${process.env.BASE_URL}/publications/${publication.id}`,
+ url: `${process.env.BASE_URL}/publications/${publicationVersion.versionOf}`,
doi: doi,
identifiers: [
{
@@ -185,22 +184,22 @@ export const updateDOI = async (
creators,
titles: [
{
- title: currentVersion.title,
+ title: publicationVersion.title,
lang: 'en'
}
],
publisher: 'Octopus',
- publicationYear: currentVersion.createdAt.getFullYear(),
+ publicationYear: publicationVersion.createdAt.getFullYear(),
contributors: [
{
- name: `${currentVersion.user.lastName} ${currentVersion.user.firstName}`,
+ name: `${publicationVersion.user.lastName} ${publicationVersion.user.firstName}`,
contributorType: 'ContactPerson',
nameType: 'Personal',
- givenName: currentVersion.user.firstName,
- familyName: currentVersion.user.lastName,
+ givenName: publicationVersion.user.firstName,
+ familyName: publicationVersion.user.lastName,
nameIdentifiers: [
{
- nameIdentifier: currentVersion.user.orcid,
+ nameIdentifier: publicationVersion.user.orcid,
nameIdentifierScheme: 'ORCID',
schemeUri: 'https://orcid.org/'
}
@@ -210,11 +209,11 @@ export const updateDOI = async (
language: 'en',
types: {
resourceTypeGeneral: 'Other',
- resourceType: publication.type
+ resourceType: publicationVersion.publication.type
},
relatedIdentifiers: allReferencesWithDOI,
relatedItems: otherReferences,
- fundingReferences: currentVersion.funders.map((funder) => ({
+ fundingReferences: publicationVersion.funders.map((funder) => ({
funderName: funder.name,
funderReference: funder.ror || funder.link,
funderIdentifierType: funder.ror ? 'ROR' : 'Other'
@@ -489,13 +488,10 @@ export const formatAffiliationName = (affiliation: I.MappedOrcidAffiliation): st
};
export const createPublicationHTMLTemplate = (
- publication: I.PublicationWithVersionAttached,
- references: I.Reference[]
+ publicationVersion: I.PublicationVersion,
+ references: I.Reference[],
+ linkedTo: I.LinkedToPublication[]
): string => {
- const { type, doi, linkedTo } = publication;
-
- const currentVersion = publication.versions[0];
-
const {
title,
content,
@@ -510,7 +506,7 @@ export const createPublicationHTMLTemplate = (
dataPermissionsStatementProvidedBy,
dataAccessStatement,
selfDeclaration
- } = currentVersion;
+ } = publicationVersion;
// cheerio uses htmlparser2
// parsing the publication content can sometimes help with unpaired opening/closing tags
@@ -519,16 +515,16 @@ export const createPublicationHTMLTemplate = (
const authors = coAuthors.filter((author) => Boolean(author.confirmedCoAuthor && author.linkedUser));
// If corresponding author is not found in coauthors list, and we have the necessary fields, mock them up
- if (!authors.find((author) => author.linkedUser === currentVersion.createdBy)) {
+ if (!authors.find((author) => author.linkedUser === publicationVersion.createdBy)) {
authors.unshift({
- id: currentVersion.createdBy,
+ id: publicationVersion.createdBy,
approvalRequested: false,
confirmedCoAuthor: true,
createdAt: new Date(),
- email: currentVersion.user.email || '',
- linkedUser: currentVersion.createdBy,
- publicationVersionId: currentVersion.id,
- user: currentVersion.user,
+ email: publicationVersion.user.email || '',
+ linkedUser: publicationVersion.createdBy,
+ publicationVersionId: publicationVersion.id,
+ user: publicationVersion.user,
reminderDate: null,
isIndependent: true,
affiliations: []
@@ -758,12 +754,12 @@ export const createPublicationHTMLTemplate = (
.join(', ')}
- Publication Type: ${formatPublicationType(type)}
+ Publication Type: ${formatPublicationType(publicationVersion.publication.type)}
Publication Date: ${
- currentVersion.publishedDate
- ? formatPDFDate(currentVersion.publishedDate)
+ publicationVersion.publishedDate
+ ? formatPDFDate(publicationVersion.publishedDate)
: formatPDFDate(new Date())
}
@@ -775,8 +771,8 @@ export const createPublicationHTMLTemplate = (
DOI:
-
- ${doi}
+
+ ${publicationVersion.publication.doi}
@@ -827,7 +823,7 @@ export const createPublicationHTMLTemplate = (
${linkedTo
.map(
(link) =>
- `${link.publicationToRef.versions[0].title}
`
+ `${link.title}
`
)
.join('')}
`
@@ -835,11 +831,11 @@ export const createPublicationHTMLTemplate = (
}
${
- selfDeclaration && ['PROTOCOL', 'HYPOTHESIS'].includes(type)
+ selfDeclaration && ['PROTOCOL', 'HYPOTHESIS'].includes(publicationVersion.publication.type)
? `
Data access statement
${
- type === 'PROTOCOL'
+ publicationVersion.publication.type === 'PROTOCOL'
? '
Data has not yet been collected according to this method/protocol.
'
: '
Data has not yet been collected to test this hypothesis (i.e. this is a preregistration)
'
}
@@ -910,20 +906,19 @@ export const createPublicationHTMLTemplate = (
return htmlTemplate;
};
-export const createPublicationHeaderTemplate = (publication: I.PublicationWithVersionAttached): string => {
- const currentVersion = publication.versions[0];
- const authors = currentVersion.coAuthors.filter((author) => author.confirmedCoAuthor && author.linkedUser);
+export const createPublicationHeaderTemplate = (publicationVersion: I.PublicationVersion): string => {
+ const authors = publicationVersion.coAuthors.filter((author) => author.confirmedCoAuthor && author.linkedUser);
- if (!authors.find((author) => author.linkedUser === currentVersion.createdBy)) {
+ if (!authors.find((author) => author.linkedUser === publicationVersion.createdBy)) {
authors.unshift({
- id: currentVersion.createdBy,
+ id: publicationVersion.createdBy,
approvalRequested: false,
confirmedCoAuthor: true,
createdAt: new Date(),
- email: currentVersion.user.email || '',
- linkedUser: currentVersion.createdBy,
- publicationVersionId: currentVersion.id,
- user: currentVersion.user,
+ email: publicationVersion.user.email || '',
+ linkedUser: publicationVersion.createdBy,
+ publicationVersionId: publicationVersion.id,
+ user: publicationVersion.user,
reminderDate: null,
isIndependent: true,
affiliations: []
@@ -966,13 +961,15 @@ export const createPublicationHeaderTemplate = (publication: I.PublicationWithVe
Published ${
- currentVersion.publishedDate ? formatPDFDate(currentVersion.publishedDate) : formatPDFDate(new Date())
+ publicationVersion.publishedDate
+ ? formatPDFDate(publicationVersion.publishedDate)
+ : formatPDFDate(new Date())
}
`;
};
-export const createPublicationFooterTemplate = (publication: I.PublicationWithVersionAttached): string => {
+export const createPublicationFooterTemplate = (publicationVersion: I.PublicationVersion): string => {
const base64InterRegular = fs.readFileSync('assets/fonts/Inter-Regular.ttf', { encoding: 'base64' });
const base64OctopusLogo = fs.readFileSync('assets/img/OCTOPUS_LOGO_ILLUSTRATION_WHITE_500PX.svg', {
encoding: 'base64'
@@ -1026,7 +1023,7 @@ export const createPublicationFooterTemplate = (publication: I.PublicationWithVe