From d617e7ef49a7eda8221b4308d77daebe4a54abf0 Mon Sep 17 00:00:00 2001 From: finlay-jisc <132363734+finlay-jisc@users.noreply.github.com> Date: Mon, 13 Nov 2023 09:38:02 +0000 Subject: [PATCH] OC-725: Revert change to force co-authors to accept with the email they were invited with (#528) * allow coauthors to accept with different email * OC-389 Versioned DOIs (#527) * finished script, API and UI work * fixed ts issue after rebase * fixed alias paths when running seed * fixed createVersionedDOIs script --------- Co-authored-by: Florin H --------- Co-authored-by: florin-holhos <48569671+florin-holhos@users.noreply.github.com> Co-authored-by: Florin H --- api/prisma/schema.prisma | 106 +++++++++--------- .../coauthor/__tests__/linkCoAuthor.test.ts | 31 ----- api/src/components/coauthor/controller.ts | 18 ++- 3 files changed, 60 insertions(+), 95 deletions(-) diff --git a/api/prisma/schema.prisma b/api/prisma/schema.prisma index dcfa08188..3986d705e 100644 --- a/api/prisma/schema.prisma +++ b/api/prisma/schema.prisma @@ -53,35 +53,35 @@ model Images { } model Publication { - id String @id @default(cuid()) - url_slug String @unique @default(cuid()) - type PublicationType - doi String - publicationFlags PublicationFlags[] - PublicationBookmarks PublicationBookmarks[] - linkedTo Links[] @relation("from") - linkedFrom Links[] @relation("to") - topics Topic[] - versions PublicationVersion[] + id String @id @default(cuid()) + url_slug String @unique @default(cuid()) + type PublicationType + doi String + publicationFlags PublicationFlags[] + PublicationBookmarks PublicationBookmarks[] + linkedTo Links[] @relation("from") + linkedFrom Links[] @relation("to") + topics Topic[] + versions PublicationVersion[] } model PublicationVersion { - id String @id @default(cuid()) + id String @id @default(cuid()) doi String? versionOf String versionNumber Int - isLatestVersion Boolean @default(true) - isLatestLiveVersion Boolean @default(false) - publication Publication @relation(fields: [versionOf], references: [id], onDelete: Cascade) + isLatestVersion Boolean @default(true) + isLatestLiveVersion Boolean @default(false) + publication Publication @relation(fields: [versionOf], references: [id], onDelete: Cascade) createdBy String - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - user User @relation(fields: [createdBy], references: [id], onDelete: Cascade) - currentStatus PublicationStatusEnum @default(DRAFT) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + user User @relation(fields: [createdBy], references: [id], onDelete: Cascade) + currentStatus PublicationStatusEnum @default(DRAFT) publicationStatus PublicationStatus[] publishedDate DateTime? title String? - licence LicenceType @default(CC_BY) + licence LicenceType @default(CC_BY) conflictOfInterestStatus Boolean? conflictOfInterestText String? ethicalStatement String? @@ -89,11 +89,11 @@ model PublicationVersion { dataPermissionsStatement String? dataPermissionsStatementProvidedBy String? dataAccessStatement String? - selfDeclaration Boolean? @default(false) + selfDeclaration Boolean? @default(false) description String? keywords String[] content String? - language Languages @default(en) + language Languages @default(en) coAuthors CoAuthors[] References References[] funders Funders[] @@ -138,27 +138,25 @@ model PublicationStatus { status PublicationStatusEnum createdAt DateTime @default(now()) publicationVersionId String - publicationVersion PublicationVersion @relation(fields: [publicationVersionId], references: [id], onDelete: Cascade) + publicationVersion PublicationVersion @relation(fields: [publicationVersionId], references: [id], onDelete: Cascade) } model CoAuthors { - id String @id @default(cuid()) - email String - code String @default(cuid()) - confirmedCoAuthor Boolean @default(false) - approvalRequested Boolean @default(false) - linkedUser String? - position Int @default(0) - createdAt DateTime @default(now()) - reminderDate DateTime? - affiliations Json[] - isIndependent Boolean @default(false) - user User? @relation(fields: [linkedUser], references: [id]) - publicationVersion PublicationVersion @relation(fields: [publicationVersionId], references: [id], onDelete: Cascade) - publicationVersionId String + id String @id @default(cuid()) + email String + code String @default(cuid()) + confirmedCoAuthor Boolean @default(false) + approvalRequested Boolean @default(false) + linkedUser String? + position Int @default(0) + createdAt DateTime @default(now()) + reminderDate DateTime? + affiliations Json[] + isIndependent Boolean @default(false) + user User? @relation(fields: [linkedUser], references: [id]) + publicationVersion PublicationVersion @relation(fields: [publicationVersionId], references: [id], onDelete: Cascade) + publicationVersionId String - // This @@unique needs to be disabled for the migration step because the fields involved must be mandatory. - // It needs to be reinstated after migration with publicationVersionId, not publicationId. @@unique([publicationVersionId, email]) } @@ -196,24 +194,24 @@ model PublicationBookmarks { } model Topic { - id String @id @default(cuid()) - title String - language Languages @default(en) - translations TopicTranslation[] - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - parents Topic[] @relation("TopicHierarchy") - children Topic[] @relation("TopicHierarchy") - publications Publication[] + id String @id @default(cuid()) + title String + language Languages @default(en) + translations TopicTranslation[] + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + parents Topic[] @relation("TopicHierarchy") + children Topic[] @relation("TopicHierarchy") + publications Publication[] } model Bookmark { - id String @id @default(cuid()) - type BookmarkType - entityId String - userId String - createdAt DateTime @default(now()) - user User @relation(fields: [userId], references: [id], onDelete: Cascade) + id String @id @default(cuid()) + type BookmarkType + entityId String + userId String + createdAt DateTime @default(now()) + user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@unique([entityId, type, userId]) } @@ -472,4 +470,4 @@ enum ReferenceType { enum BookmarkType { PUBLICATION TOPIC -} \ No newline at end of file +} diff --git a/api/src/components/coauthor/__tests__/linkCoAuthor.test.ts b/api/src/components/coauthor/__tests__/linkCoAuthor.test.ts index b2cd0920b..cb3cfbd58 100644 --- a/api/src/components/coauthor/__tests__/linkCoAuthor.test.ts +++ b/api/src/components/coauthor/__tests__/linkCoAuthor.test.ts @@ -94,35 +94,4 @@ describe('Link co-author', () => { expect(link.status).toEqual(404); }); - - // the following test covers an edge case, but still possible - 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('/publication-versions/publication-problem-draft-v1/link-coauthor') - .query({ apiKey: '000000004' }) - .send({ - email: 'test-user-7@jisc.ac.uk', - code: 'test-code-user-7', - approve: true - }); - - expect(response.status).toEqual(404); - expect(response.body.message).toBe('You are not currently listed as an author on this draft'); - - // trying to accept invitation with a different co-author account - const response2 = await testUtils.agent - .patch('/publication-versions/publication-problem-draft-v1/link-coauthor') - .query({ apiKey: '000000008' }) - .send({ - email: 'test-user-7@jisc.ac.uk', - code: 'test-code-user-7', - approve: true - }); - - expect(response2.status).toEqual(403); - expect(response2.body.message).toBe( - 'Your email address does not match the one to which the invitation has been sent.' - ); - }); }); diff --git a/api/src/components/coauthor/controller.ts b/api/src/components/coauthor/controller.ts index c3267fdc5..7010000ff 100644 --- a/api/src/components/coauthor/controller.ts +++ b/api/src/components/coauthor/controller.ts @@ -269,19 +269,17 @@ export const link = async ( }); } - // check if the user email is the same as the one the invitation has been sent to - if (event.user.email !== coAuthorByEmail.email) { - const isCoAuthor = version.coAuthors.some((coAuthor) => coAuthor.email === event.user?.email); // check that this user is a coAuthor + await coAuthorService.linkUser(event.user.id, version.id, event.body.email, event.body.code); - return response.json(isCoAuthor ? 403 : 404, { - message: isCoAuthor - ? 'Your email address does not match the one to which the invitation has been sent.' - : 'You are not currently listed as an author on this draft' - }); + // The email of the linked user may not match the email the invitation was sent to + // (e.g. user manages their orcid account with a different email to their work email). + // In this case, we need to update the coauthor's email field because it becomes outdated. + if (event.user.email !== coAuthorByEmail.email) { + // We already check that the logged in user's email is not already a coauthor on this version, + // so this is safe. + await coAuthorService.update(coAuthorByEmail.id, { email: event.user.email }); } - await coAuthorService.linkUser(event.user.id, version.id, event.body.email, event.body.code); - return response.json(200, 'Linked user account'); } catch (err) { console.log(err);