From 8fe19b48797611787d70bc8be55ad7e934b15012 Mon Sep 17 00:00:00 2001
From: Jure Triglav <juretriglav@gmail.com>
Date: Sat, 8 Aug 2020 01:57:59 +0200
Subject: [PATCH] feat: add model-review-comment

---
 server/model-review-comment/src/index.js      | 10 +++
 .../1596830548-add-review-comments.sql        | 12 +++
 server/model-review-comment/src/resolvers.js  |  7 ++
 .../src/review_comment.js                     | 75 +++++++++++++++++++
 server/model-review-comment/src/typeDefs.js   | 17 +++++
 ...50834-review.sql => 1596830547-review.sql} |  2 +-
 server/model-review/src/review.js             | 12 +--
 server/model-review/src/typeDefs.js           | 15 +---
 8 files changed, 130 insertions(+), 20 deletions(-)
 create mode 100644 server/model-review-comment/src/index.js
 create mode 100644 server/model-review-comment/src/migrations/1596830548-add-review-comments.sql
 create mode 100644 server/model-review-comment/src/resolvers.js
 create mode 100644 server/model-review-comment/src/review_comment.js
 create mode 100644 server/model-review-comment/src/typeDefs.js
 rename server/model-review/src/migrations/{1537450834-review.sql => 1596830547-review.sql} (80%)

diff --git a/server/model-review-comment/src/index.js b/server/model-review-comment/src/index.js
new file mode 100644
index 0000000000..95f8a2b510
--- /dev/null
+++ b/server/model-review-comment/src/index.js
@@ -0,0 +1,10 @@
+const resolvers = require('./resolvers')
+const typeDefs = require('./typeDefs')
+const model = require('./review_comment')
+
+module.exports = {
+  model,
+  modelName: 'ReviewComment',
+  resolvers,
+  typeDefs,
+}
diff --git a/server/model-review-comment/src/migrations/1596830548-add-review-comments.sql b/server/model-review-comment/src/migrations/1596830548-add-review-comments.sql
new file mode 100644
index 0000000000..7a7843c91e
--- /dev/null
+++ b/server/model-review-comment/src/migrations/1596830548-add-review-comments.sql
@@ -0,0 +1,12 @@
+CREATE TABLE review_comments (
+    id UUID PRIMARY KEY,
+    created TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT current_timestamp,
+    updated TIMESTAMP WITH TIME ZONE,
+    -- If a review is deleted for some reason, we want its comments to go away too
+    review_id UUID REFERENCES reviews(id) ON DELETE CASCADE,
+    -- If a user is deleted, it's likely we want to keep the comments they have made
+    user_id UUID REFERENCES users(id) ON DELETE SET NULL,
+    content TEXT,
+    comment_type TEXT,
+    type TEXT
+);
\ No newline at end of file
diff --git a/server/model-review-comment/src/resolvers.js b/server/model-review-comment/src/resolvers.js
new file mode 100644
index 0000000000..a1cbb733ad
--- /dev/null
+++ b/server/model-review-comment/src/resolvers.js
@@ -0,0 +1,7 @@
+// const ReviewComment = require('./review_comment')
+
+const resolvers = {
+  Mutation: {},
+}
+
+module.exports = resolvers
diff --git a/server/model-review-comment/src/review_comment.js b/server/model-review-comment/src/review_comment.js
new file mode 100644
index 0000000000..15a3b443f1
--- /dev/null
+++ b/server/model-review-comment/src/review_comment.js
@@ -0,0 +1,75 @@
+const BaseModel = require('@pubsweet/base-model')
+
+class ReviewComment extends BaseModel {
+  static get tableName() {
+    return 'review_comments'
+  }
+
+  constructor(properties) {
+    super(properties)
+    this.type = 'ReviewComment'
+  }
+
+  static get schema() {
+    return {
+      properties: {
+        content: { type: ['string', 'null'] },
+        userId: { type: 'string', format: 'uuid' },
+        reviewId: { type: 'string', format: 'uuid' },
+        commentType: { type: ['string', 'null'] },
+      },
+    }
+  }
+
+  static get relationMappings() {
+    const { File, Review, User } = require('@pubsweet/models')
+
+    return {
+      review: {
+        relation: BaseModel.BelongsToOneRelation,
+        modelClass: Review,
+        join: {
+          from: 'review_comments.reviewId',
+          to: 'reviews.id',
+        },
+      },
+      user: {
+        relation: BaseModel.BelongsToOneRelation,
+        modelClass: User,
+        join: {
+          from: 'reviews_comments.userId',
+          to: 'users.id',
+        },
+      },
+      files: {
+        relation: BaseModel.ManyToManyRelation,
+        modelClass: File,
+        join: {
+          from: 'review_comments.id',
+          through: {
+            from: 'fileables.review_comment_id',
+            to: 'fileables.file_id',
+          },
+          to: 'files.id',
+        },
+      },
+    }
+  }
+
+  async $beforeDelete() {
+    // TODO: Do this with ON DELETE CASCADE?
+    const File = require('../../model-file/src/file')
+    const files = await File.query().where({
+      objectId: this.id,
+      objectType: 'ReviewComment',
+    })
+    if (files.length > 0) {
+      files.forEach(async fl => {
+        await new File(fl).delete()
+      })
+    }
+  }
+}
+
+ReviewComment.type = 'Review'
+module.exports = ReviewComment
diff --git a/server/model-review-comment/src/typeDefs.js b/server/model-review-comment/src/typeDefs.js
new file mode 100644
index 0000000000..288c208456
--- /dev/null
+++ b/server/model-review-comment/src/typeDefs.js
@@ -0,0 +1,17 @@
+const typeDefs = `
+  type ReviewComment implements Object {
+    id: ID!
+    created: DateTime!
+    updated: DateTime
+    type: String
+    content: String
+    files: [File]
+  }
+
+  input ReviewCommentInput {
+    type: String
+    content: String
+  }
+`
+
+module.exports = typeDefs
diff --git a/server/model-review/src/migrations/1537450834-review.sql b/server/model-review/src/migrations/1596830547-review.sql
similarity index 80%
rename from server/model-review/src/migrations/1537450834-review.sql
rename to server/model-review/src/migrations/1596830547-review.sql
index d679395061..bde0c4a5a7 100644
--- a/server/model-review/src/migrations/1537450834-review.sql
+++ b/server/model-review/src/migrations/1596830547-review.sql
@@ -6,6 +6,6 @@ CREATE TABLE reviews (
     is_decision BOOLEAN DEFAULT FALSE,
     comments JSONB,
     user_id UUID,
-    manuscript_id UUID,
+    manuscript_id UUID REFERENCES manuscripts(id) ON DELETE CASCADE,
     type TEXT NOT NULL
 );
\ No newline at end of file
diff --git a/server/model-review/src/review.js b/server/model-review/src/review.js
index bc07009f5a..15d8c980fe 100644
--- a/server/model-review/src/review.js
+++ b/server/model-review/src/review.js
@@ -20,9 +20,9 @@ class Review extends BaseModel {
 
     await Promise.all(
       (this.comments || []).map(async comment => {
-        const files = await File.findByObject({
-          object: 'Review',
-          object_id: this.id,
+        const files = await File.query().where({
+          objectType: 'Review',
+          objectId: this.id,
         })
         const commentFile = files.find(file => file.fileType === comment.type)
         if (commentFile) {
@@ -76,9 +76,9 @@ class Review extends BaseModel {
 
   async $beforeDelete() {
     const File = require('../../model-file/src/file')
-    const files = await File.findByObject({
-      object_id: this.id,
-      object: 'Review',
+    const files = await File.query().where({
+      objectId: this.id,
+      objectType: 'Review',
     })
     if (files.length > 0) {
       files.forEach(async fl => {
diff --git a/server/model-review/src/typeDefs.js b/server/model-review/src/typeDefs.js
index fb39b5e390..a186d1dafd 100644
--- a/server/model-review/src/typeDefs.js
+++ b/server/model-review/src/typeDefs.js
@@ -8,7 +8,7 @@ const typeDefs = `
     id: ID!
     created: DateTime!
     updated: DateTime
-    comments: [Comment]
+    comments: [ReviewComment]
     recommendation: String
     isDecision: Boolean
     open: Boolean
@@ -16,22 +16,11 @@ const typeDefs = `
   }
 
   input ReviewInput {
-    comments: [CommentInput]
+    comments: [ReviewCommentInput]
     recommendation: String
     isDecision: Boolean
     manuscriptId: ID!
   }
-
-  input CommentInput {
-    type: String
-    content: String
-  }
-
-  type Comment {
-    type: String
-    content: String
-    files: [File]
-  }
 `
 
 module.exports = typeDefs
-- 
GitLab