strapi-plugin-comments 2.0.0-rc.1 → 2.0.0-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,9 +1,11 @@
1
- <div align="center">
1
+ <div align="center" width="150px">
2
2
  <img style="width: 150px; height: auto;" src="public/assets/logo.png" alt="Logo - Strapi Comments plugin" />
3
+ </div>
4
+ <div align="center">
3
5
  <h1>Strapi v4 - Comments plugin</h1>
4
6
  <p>Powerfull Strapi based comments moderation tool for you and your users</p>
5
7
  <a href="https://www.npmjs.org/package/strapi-plugin-comments">
6
- <img src="https://img.shields.io/npm/v/strapi-plugin-comments/latest.svg" alt="NPM Version" />
8
+ <img alt="GitHub package.json version" src="https://img.shields.io/github/package-json/v/VirtusLab-Open-Source/strapi-plugin-comments?label=npm&logo=npm">
7
9
  </a>
8
10
  <a href="https://www.npmjs.org/package/strapi-plugin-comments">
9
11
  <img src="https://img.shields.io/npm/dm/strapi-plugin-comments.svg" alt="Monthly download on NPM" />
@@ -76,7 +78,7 @@ In our minimum support we're following [official Node.js releases timelines](htt
76
78
 
77
79
  **Supported Strapi versions**:
78
80
 
79
- - Strapi v4.0.5 (recently tested)
81
+ - Strapi v4.0.6 (recently tested)
80
82
  - Strapi v4.x
81
83
 
82
84
  > This plugin is designed for **Strapi v4** and is not working with v3.x. To get version for **Strapi v3** install version [v1.x](https://github.com/VirtusLab-Open-Source/strapi-plugin-comments/tree/strapi-v3).
@@ -101,6 +103,9 @@ To setup amend default plugin configuration we recommend to put following snippe
101
103
  reportReasons: {
102
104
  'MY_CUSTOM_REASON': 'MY_CUSTOM_REASON',
103
105
  },
106
+ gql: {
107
+ // ...
108
+ },
104
109
  },
105
110
  //...
106
111
  });
@@ -112,12 +117,36 @@ To setup amend default plugin configuration we recommend to put following snippe
112
117
  - `approvalFlow` - list of Content Types which are supporting approval flow. Values must be in format like `'api::<collection name>.<content type name>'`. For not included, posted comments are going to be immediately visible.
113
118
  - `entryLabel` - ordered list of property names per Content Type to generate related entity label. Keys must be in format like `'api::<collection name>.<content type name>'`. Default formatting set as `*`.
114
119
  - `reportReasons` - set of enums you would like to use for issuing abuse reports. Provided by default `'BAD_LANGUAGE'`, `'DISCRIMINATION'` and `'OTHER'`.
120
+ - `gql` - specific configuration for GraphQL. See [Additional GQL Configuration](#additional-gql-configuration)
115
121
 
116
122
  ## Additional GQL Configuration
117
- All you need to do is to install and enable `@strapi/plugin-graphql` for you instance based on the **[official Strapi v4 docs](https://docs.strapi.io/developer-docs/latest/plugins/graphql.html#configurations)**.
123
+ All you need to do is to install and enable `@strapi/plugin-graphql` for you instance based on the **[official Strapi v4 docs](https://docs.strapi.io/developer-docs/latest/plugins/graphql.html#configurations)** and decide if you would like to call it by anyone (open for world) or only by authenticated users (Strapi users).
124
+
125
+ ```json
126
+ {
127
+ // ...
128
+ gql: {
129
+ auth: true, // Default: false
130
+ },
131
+ // ...
132
+ }
133
+ ```
134
+
135
+ ### Properties
136
+ - `auth` - does GraphQL Queries should be authenticated or not? Default: `false`
137
+
138
+ ### Queries
118
139
 
119
140
  See [available GQL specification section](#public-graphql-specification).
120
141
 
142
+ > If `auth` is set to `true` you must provide relevant authentication headers to all requests like for example:
143
+ >
144
+ > ```json
145
+ > {
146
+ > "Authorization": "Bearer <your token here>"
147
+ > }
148
+ > ```
149
+
121
150
  ## 👤 RBAC
122
151
  Plugin provides granular permissions based on Strapi RBAC functionality.
123
152
 
@@ -134,7 +163,6 @@ Feature / Capability focused permissions:
134
163
 
135
164
  ## Base Comment model
136
165
 
137
- ### Generic (non Strapi User)
138
166
  ```json
139
167
  {
140
168
  "id": 1,
@@ -146,8 +174,8 @@ Feature / Capability focused permissions:
146
174
  "removed": null,
147
175
  "approvalStatus": "APPROVED", // Only in case of enabled approval flow. Default: null
148
176
  "author": {
149
- "id": "207ccfdc-94ba-45eb-979c-790f6f49c392",
150
- "name": "Joe Doe",
177
+ "id": "207ccfdc-94ba-45eb-979c-790f6f49c392", // For Strapi users id reflects to the format used by your Strapi
178
+ "name": "Joe Doe", // For Strapi users it is equal to 'username' field
151
179
  "email": "jdoe@sample.com",
152
180
  "avatar": null,
153
181
  },
@@ -156,47 +184,18 @@ Feature / Capability focused permissions:
156
184
  "related": {}, // Related content type entity
157
185
  "reports": [] // Reports issued against this comment
158
186
  }
159
- ```
160
- ### Strapi User
161
- ```json
162
- {
163
- "id": 1,
164
- "content": "My comment content",
165
- "blocked": true,
166
- "blockedThread": null,
167
- "blockReason": null,
168
- "removed": null,
169
- "approvalStatus": "REJECTED", // Only in case of enabled approval flow. Default: null
170
- "authorUser": {
171
- "id": 1,
172
- "username": "Sample User",
173
- "email": "user@sample.com",
174
- "provider": "local",
175
- "confirmed": true,
176
- "blocked": false,
177
- "role": 1,
178
- "created_at": "2020-07-10T08:38:03.157Z",
179
- "updated_at": "2020-07-10T08:38:03.170Z"
180
- },
181
- "authorId": null,
182
- "authorName": null,
183
- "authorEmail": null,
184
- "authorAvatar": null,
185
- "createdAt": "2020-07-14T20:13:01.649Z",
186
- "updatedAt": "2020-07-14T20:13:01.670Z",
187
- "related": {}, // Related content type entity
188
- "reports": [] // Reports issued against this comment
189
- }
190
-
191
187
  ```
192
188
 
193
189
  ## 🕸️ Public REST API specification
194
190
 
191
+ **Strapi Users vs. Generic authors**
192
+ > Keep in mind that if you're using auth / authz your requests to setup proper user contexts it has got higher priority in order to take author data comparing to `author` property provided as part of your payload.
193
+
195
194
  ### Get Comments
196
195
 
197
196
  *GraphQL equivalent: [Public GraphQL API -> Get Comments](#get-comments-1)*
198
197
 
199
- `GET <host>/comments/api::<collection name>.<content type name>:<entity id>`
198
+ `GET <host>/api/comments/api::<collection name>.<content type name>:<entity id>`
200
199
 
201
200
  Return a hierarchical tree structure of comments for specified instance of Content Type like for example `Page` with `ID: 1`
202
201
 
@@ -226,7 +225,7 @@ Return a hierarchical tree structure of comments for specified instance of Conte
226
225
 
227
226
  *GraphQL equivalent: [Public GraphQL API -> Get Comments (flat structure)](#get-comments-flat-structure-1)*
228
227
 
229
- `GET <host>/comments/api::<collection name>.<content type name>:<entity id>/flat`
228
+ `GET <host>/api/comments/api::<collection name>.<content type name>:<entity id>/flat`
230
229
 
231
230
  Return a flat structure of comments for specified instance of Content Type like for example `Page` with `ID: 1`
232
231
 
@@ -254,7 +253,7 @@ Return a flat structure of comments for specified instance of Content Type like
254
253
 
255
254
  *GraphQL equivalent: [Public GraphQL API -> Post a Comments](#post-a-comment-1)*
256
255
 
257
- `POST <host>/comments/api::<collection name>.<content type name>:<entity id>`
256
+ `POST <host>/api/comments/api::<collection name>.<content type name>:<entity id>`
258
257
 
259
258
  Posts a Comment related to specified instance of Content Type like for example `Page` with `ID: 1`
260
259
 
@@ -265,21 +264,23 @@ Posts a Comment related to specified instance of Content Type like for example `
265
264
  *Generic (non Strapi User)*
266
265
  ```json
267
266
  {
268
- "author": {
269
- "id": "<any ID like value>",
270
- "name": "Joe Doe",
271
- "email": "jdoe@sample.com",
272
- "avatar": "<any image url>"
273
- },
267
+ "author": {
268
+ "id": "<any ID like value>",
269
+ "name": "Joe Doe",
270
+ "email": "jdoe@sample.com",
271
+ "avatar": "<any image url>"
272
+ },
274
273
  "content": "My sample response",
275
274
  "threadOf": 2, // id of comment we would like to start / continue the thread (Optional)
276
275
  }
277
276
  ```
278
277
 
279
278
  *Strapi user*
279
+
280
+ > Author is taken directly from the request context
281
+
280
282
  ```json
281
283
  {
282
- "authorUser": 1, // id of a author user. Optional in case of 'enableUsers: true' in the plugin configuration
283
284
  "content": "My sample response",
284
285
  "threadOf": 2, // id of comment we would like to start / continue the thread (Optional)
285
286
  }
@@ -301,7 +302,7 @@ Posts a Comment related to specified instance of Content Type like for example `
301
302
 
302
303
  *GraphQL equivalent: [Public GraphQL API -> Update Comments](#update-comment-1)*
303
304
 
304
- `PUT <host>/comments/api::<collection name>.<content type name>:<entity id>/comment/<commentId>`
305
+ `PUT <host>/api/comments/api::<collection name>.<content type name>:<entity id>/comment/<commentId>`
305
306
 
306
307
  Updates a specified Comment content based on it `commentId` and related to specified instance of Content Type like for example `Page` with `ID: 1`
307
308
 
@@ -312,17 +313,19 @@ Updates a specified Comment content based on it `commentId` and related to speci
312
313
  *Generic (non Strapi User)*
313
314
  ```json
314
315
  {
315
- "author": {
316
- "id": "<any ID like value>"
317
- },
316
+ "author": {
317
+ "id": "<any ID like value>"
318
+ },
318
319
  "content": "My sample response"
319
320
  }
320
321
  ```
321
322
 
322
323
  *Strapi user*
324
+
325
+ > Author is taken directly from the request context
326
+
323
327
  ```json
324
328
  {
325
- "authorUser": 1, // id of a author user. Optional in case of 'enableUsers: true' in the plugin configuration
326
329
  "content": "My sample response"
327
330
  }
328
331
  ```
@@ -344,7 +347,7 @@ Updates a specified Comment content based on it `commentId` and related to speci
344
347
 
345
348
  *GraphQL equivalent: [Public GraphQL API -> Delete Comment](#delete-comment-1)*
346
349
 
347
- `DELETE <host>/comments/api::<collection name>.<content type name>:<entity id>/<commentId>?authorId=<authorId>`
350
+ `DELETE <host>/api/comments/api::<collection name>.<content type name>:<entity id>/<commentId>?authorId=<authorId>`
348
351
 
349
352
  Deletes a specified Comment based on it `commentId` and related to specified instance of Content Type like for example `Page` with `ID: 1`.
350
353
 
@@ -367,7 +370,7 @@ Deletes a specified Comment based on it `commentId` and related to specified ins
367
370
 
368
371
  *GraphQL equivalent: [Public GraphQL API -> Issue Abuse Report against specified Comment](#issue-abuse-report-against-specified-comment-1)*
369
372
 
370
- `POST <host>/comments/api::<collection name>.<content type name>:<entity id>/comment/<commentId>/report-abuse`
373
+ `POST <host>/api/comments/api::<collection name>.<content type name>:<entity id>/comment/<commentId>/report-abuse`
371
374
 
372
375
  Reports abuse in specified Comment content based on it `commentId` and related to specified instance of Content Type like for example `Page` with `ID: 1` and requests moderator attention.
373
376
 
@@ -398,7 +401,11 @@ Reports abuse in specified Comment content based on it `commentId` and related t
398
401
 
399
402
  ## 🕸️ Public GraphQL specification
400
403
 
401
- > *To test all queries and understand the schemas use GraphQL Playground exposed by `@strapi/plugin-graphql` on `http://localhost:1337/graphql`*
404
+ **Strapi Users vs. Generic authors**
405
+ > Keep in mind that if you're using auth / authz your requests to setup proper user contexts it has got higher priority in order to take author data comparing to `author` property provided as part of your payload.
406
+
407
+ **Testing**
408
+ > To test all queries and understand the schemas use GraphQL Playground exposed by `@strapi/plugin-graphql` on `http://localhost:1337/graphql`
402
409
 
403
410
  ### Get Comments
404
411
 
@@ -516,7 +523,7 @@ mutation createComment {
516
523
  relation: "api::page.page:1"
517
524
  content: "Hello World!"
518
525
  threadOf: 3
519
- author: { id: "12345678", name: "John Wick", email: "test@test.pl" }
526
+ author: { id: "12345678", name: "John Wick", email: "test@test.pl" } # Optional if using auth / authz requests
520
527
  }
521
528
  ) {
522
529
  id
@@ -565,7 +572,7 @@ mutation updateComment {
565
572
  id: 34
566
573
  relation: "api::page.page:1"
567
574
  content: "I've changed it!"
568
- author: { id: "12345678" }
575
+ author: { id: "12345678" } # Optional if using auth / authz requests
569
576
  }
570
577
  ) {
571
578
  id
@@ -614,7 +621,11 @@ mutation updateComment {
614
621
  ```graphql
615
622
  mutation removeComment {
616
623
  removeComment(
617
- input: { id: 33, relation: "api::page.page:1", author: { id: "12345678" } }
624
+ input: {
625
+ id: 33,
626
+ relation: "api::page.page:1",
627
+ author: { id: "12345678" } # Optional if using auth / authz requests
628
+ }
618
629
  ) {
619
630
  id
620
631
  removed
@@ -37,7 +37,7 @@ const DiscussionThread = ({ level = [], selected = {}, allowedActions, isReloadi
37
37
  { rootThread && (<DiscussionThreadItem {...rootThread} allowedActions={allowedActions} isThreadAuthor root pinned />) }
38
38
  { level.map(item => {
39
39
  const isSelected = selected.id === item.id;
40
- const isThreadAuthor = !isNil(selected?.threadOf?.authorId) && (selected?.threadOf?.authorId === item.authorId);
40
+ const isThreadAuthor = !isNil(selected?.threadOf?.author?.id) && (selected?.threadOf?.author?.id === item?.author?.id);
41
41
  return (<DiscussionThreadItem
42
42
  key={`comment-${item.id}`}
43
43
  {...item}
@@ -32,8 +32,7 @@ const DiscussionThreadItem = (props) => {
32
32
  blockedThread,
33
33
  gotThread,
34
34
  threadFirstItemId,
35
- authorUser,
36
- author } = props;
35
+ author = {} } = props;
37
36
 
38
37
  let background = 'neutral100';
39
38
  let borderColor = 'neutral200';
@@ -57,8 +56,7 @@ const DiscussionThreadItem = (props) => {
57
56
  StyledComponent = DiscussionThreadItemRoot;
58
57
  }
59
58
 
60
- const avatar = author?.avatar || authorUser?.avatar;
61
- const name = author?.name || `${authorUser?.firstName} ${authorUser?.lastName}`;
59
+ const { name, avatar } = author;
62
60
  const isBlocked = blocked || blockedThread;
63
61
 
64
62
  return (<StyledComponent as="li">
@@ -48,10 +48,6 @@ module.exports = {
48
48
  target: 'plugin::users-permissions.user',
49
49
  configurable: false,
50
50
  },
51
- authorType: {
52
- type: 'string',
53
- configurable: false
54
- },
55
51
  authorId: {
56
52
  type: 'string',
57
53
  configurable: false
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "strapi-plugin-comments",
3
- "version": "2.0.0-rc.1",
3
+ "version": "2.0.0-rc.2",
4
4
  "description": "Strapi - Comments plugin",
5
5
  "strapi": {
6
6
  "name": "comments",
@@ -8,6 +8,7 @@
8
8
  "kind": "plugin"
9
9
  },
10
10
  "scripts": {
11
+ "publish": "npm publish --tag latest",
11
12
  "test:unit": "jest --verbose --coverage"
12
13
  },
13
14
  "repository": {
@@ -15,8 +16,8 @@
15
16
  "url": "https://github.com/VirtusLab-Open-Source/strapi-plugin-comments"
16
17
  },
17
18
  "dependencies": {
18
- "@strapi/utils": "^4.0.5",
19
- "@strapi/plugin-graphql": "^4.0.5",
19
+ "@strapi/utils": "^4.0.6",
20
+ "@strapi/plugin-graphql": "^4.0.6",
20
21
  "bad-words": "^3.0.4"
21
22
  },
22
23
  "devDependencies": {
@@ -82,14 +82,19 @@ module.exports = {
82
82
  },
83
83
 
84
84
  async removeComment(ctx) {
85
- const { params: { relationId, commentId }, query: { authorId } } = ctx;
86
- if (authorId) {
85
+ const {
86
+ params: { relationId, commentId },
87
+ query: { authorId },
88
+ state: { user },
89
+ } = ctx;
90
+ if (authorId || user?.id) {
87
91
  try {
88
92
  return await this.getService()
89
93
  .markAsRemoved(
90
94
  commentId,
91
95
  relationId,
92
96
  authorId,
97
+ user
93
98
  );
94
99
  } catch (e) {
95
100
  if (!e.isBoom) {
@@ -9,7 +9,9 @@ module.exports = {
9
9
  related: relation,
10
10
  },
11
11
  populate: {
12
- threadOf: true,
12
+ threadOf: {
13
+ populate: { authorUser: true },
14
+ },
13
15
  },
14
16
  };
15
17
  },
@@ -10,11 +10,16 @@ module.exports = ({ nexus }) => {
10
10
  args: {
11
11
  input: nonNull('CreateReport'),
12
12
  },
13
- async resolve(obj, args) {
13
+ async resolve(obj, args, ctx) {
14
14
  const { input } = args;
15
+ const { state: { user } = {} } = ctx;
15
16
  const { commentId, relation, ...body } = input;
16
- return await getPluginService('client')
17
- .reportAbuse(commentId, relation, body);
17
+ try {
18
+ return await getPluginService('client')
19
+ .reportAbuse(commentId, relation, body, user);
20
+ } catch(e) {
21
+ throw new Error(e);
22
+ }
18
23
  },
19
24
  };
20
25
  }
@@ -10,11 +10,16 @@ module.exports = ({ nexus }) => {
10
10
  args: {
11
11
  input: nonNull('CreateComment'),
12
12
  },
13
- async resolve(obj, args) {
13
+ async resolve(obj, args, ctx) {
14
14
  const { input } = args;
15
+ const { state: { user } = {} } = ctx;
15
16
  const { relation, ...body } = input;
16
- return await getPluginService('client')
17
- .create(relation, body);
17
+ try {
18
+ return await getPluginService('client')
19
+ .create(relation, body, user);
20
+ } catch(e) {
21
+ throw new Error(e);
22
+ }
18
23
  },
19
24
  };
20
25
  }
@@ -10,11 +10,16 @@ module.exports = ({ nexus }) => {
10
10
  args: {
11
11
  input: nonNull('RemoveComment'),
12
12
  },
13
- async resolve(obj, args) {
13
+ async resolve(obj, args, ctx) {
14
14
  const { input } = args;
15
+ const { state: { user } = {} } = ctx;
15
16
  const { id, relation, author } = input;
16
- return await getPluginService('client')
17
- .markAsRemoved(id, relation, author?.id);
17
+ try {
18
+ return await getPluginService('client')
19
+ .markAsRemoved(id, relation, author?.id, user);
20
+ } catch(e) {
21
+ throw new Error(e);
22
+ }
18
23
  },
19
24
  };
20
25
  }
@@ -10,11 +10,16 @@ module.exports = ({ nexus }) => {
10
10
  args: {
11
11
  input: nonNull('UpdateComment'),
12
12
  },
13
- async resolve(obj, args) {
13
+ async resolve(obj, args, ctx) {
14
14
  const { input } = args;
15
+ const { state: { user } = {} } = ctx;
15
16
  const { id, relation, ...body } = input;
16
- return await getPluginService('client')
17
- .update(id, relation, body);
17
+ try {
18
+ return await getPluginService('client')
19
+ .update(id, relation, body, user);
20
+ } catch(e) {
21
+ throw new Error(e);
22
+ }
18
23
  },
19
24
  };
20
25
  }
@@ -1,10 +1,15 @@
1
1
  'use strict'
2
2
 
3
- module.exports = ({ }) => ({
4
- 'Query.findAllFlat': { auth: false },
5
- 'Query.findAllInHierarchy': { auth: false },
6
- 'Mutation.createComment': { auth: false },
7
- 'Mutation.updateComment': { auth: false },
8
- 'Mutation.removeComment': { auth: false },
9
- 'Mutation.createAbuseReport': { auth: false },
10
- });
3
+ const { getPluginService } = require("../utils/functions");
4
+
5
+ module.exports = () => {
6
+ const auth = getPluginService('common').getConfig('gql.auth', false);
7
+ return {
8
+ 'Query.findAllFlat': { auth },
9
+ 'Query.findAllInHierarchy': { auth },
10
+ 'Mutation.createComment': { auth },
11
+ 'Mutation.updateComment': { auth },
12
+ 'Mutation.removeComment': { auth },
13
+ 'Mutation.createAbuseReport': { auth },
14
+ };
15
+ };
@@ -8,7 +8,6 @@ module.exports = ({ nexus }) => nexus.objectType({
8
8
  t.string("approvalStatus")
9
9
  t.field("threadOf", { type: 'CommentSingle' })
10
10
  t.list.field("children", { type: 'CommentNested' })
11
- t.field("authorUser", { type: 'CommentAuthorUser' })
12
11
  t.field("author", { type: 'CommentAuthor' })
13
12
  t.string("createdAt")
14
13
  t.string("updatedAt")
@@ -8,7 +8,6 @@ module.exports = ({ nexus }) => nexus.objectType({
8
8
  t.boolean("removed")
9
9
  t.string("approvalStatus")
10
10
  t.field("threadOf", { type: 'CommentSingle' })
11
- t.field("authorUser", { type: 'CommentAuthorUser' })
12
11
  t.field("author", { type: 'CommentAuthor' })
13
12
  t.string("createdAt")
14
13
  t.string("updatedAt")
@@ -1,7 +1,7 @@
1
1
  module.exports = ({ nexus }) => nexus.inputObjectType({
2
2
  name: "CreateCommentAuthor",
3
3
  definition(t) {
4
- t.nonNull.string("id")
4
+ t.nonNull.id("id")
5
5
  t.nonNull.string("name")
6
6
  t.string("email")
7
7
  t.string("avatar")
@@ -1,6 +1,6 @@
1
1
  module.exports = ({ nexus }) => nexus.inputObjectType({
2
2
  name: "IdentifyCommentAuthor",
3
3
  definition(t) {
4
- t.nonNull.string("id")
4
+ t.nonNull.id("id")
5
5
  }
6
6
  })
@@ -3,7 +3,6 @@ const typesFactories = [
3
3
  require('./comment'),
4
4
  require('./comment-nested'),
5
5
  require('./comment-author'),
6
- require('./comment-author-user'),
7
6
  require('./create-comment'),
8
7
  require('./create-comment-author'),
9
8
  require('./update-comment'),
@@ -117,19 +117,24 @@ const { APPROVAL_STATUS, REGEX } = require('./../utils/constants')
117
117
  }
118
118
  };
119
119
 
120
- const entity = await strapi.db.query(getModelUid('comment')).findOne({
121
- where: {
122
- id,
123
- },
120
+ const defaultPopulate = {
124
121
  populate: {
125
122
  authorUser: true,
126
123
  threadOf: {
127
124
  populate: {
125
+ authorUser: true,
128
126
  ...reportsPopulation
129
127
  },
130
128
  },
131
129
  ...reportsPopulation,
132
130
  },
131
+ }
132
+
133
+ const entity = await strapi.db.query(getModelUid('comment')).findOne({
134
+ where: {
135
+ id,
136
+ },
137
+ ...defaultPopulate,
133
138
  });
134
139
 
135
140
  if (!entity){
@@ -159,9 +164,7 @@ const { APPROVAL_STATUS, REGEX } = require('./../utils/constants')
159
164
  threadOf: levelThreadId,
160
165
  related: entity.related,
161
166
  },
162
- populate: {
163
- ...reportsPopulation,
164
- },
167
+ ...defaultPopulate,
165
168
  startingFromId: levelThreadId,
166
169
  isAdmin: true
167
170
  }, false);
@@ -9,7 +9,6 @@ const {
9
9
  getModelUid,
10
10
  getRelatedGroups,
11
11
  checkBadWords,
12
- isValidUserContext,
13
12
  resolveUserContextError,
14
13
  } = require('./utils/functions');
15
14
  const { APPROVAL_STATUS, REGEX } = require('./../utils/constants')
@@ -49,7 +48,7 @@ module.exports = ({ strapi }) => ({
49
48
  id: threadOf,
50
49
  related: relation,
51
50
  }) : true;
52
- const validContext = isValidUserContext(user);
51
+ const validContext = this.getCommonService().isValidUserContext(user);
53
52
 
54
53
  if (!linkToThread) {
55
54
  throw new PluginError(400, 'Thread does not exist');
@@ -60,15 +59,11 @@ module.exports = ({ strapi }) => ({
60
59
  }
61
60
 
62
61
  if (checkBadWords(content) && singleRelationFulfilled) {
63
- const { author, authorUser, ...rest } = data;
62
+ const { author = {}, ...rest } = data;
64
63
  let authorData = {};
65
64
  if (validContext && user) {
66
65
  authorData = {
67
- authorUser: user?.id || authorUser,
68
- };
69
- } else if (authorUser) {
70
- authorData = {
71
- authorUser,
66
+ authorUser: user?.id,
72
67
  };
73
68
  } else {
74
69
  if (author.email && !REGEX.email.test(author.email)) {
@@ -83,8 +78,8 @@ module.exports = ({ strapi }) => ({
83
78
  };
84
79
  }
85
80
 
86
- if (!isEmpty(authorData) && !authorData.authorId) {
87
- throw new PluginError(400, 'Object: "author" is invalid. Check your payload');
81
+ if (!isEmpty(authorData) && !(authorData.authorId || authorData.authorUser)) {
82
+ throw new PluginError(400, `Not able to recognise author of a comment. Make sure you've provided "author" property in a payload or authenticated your request properly.`);
88
83
  }
89
84
 
90
85
  if (isApprovalFlowEnabled &&
@@ -98,7 +93,10 @@ module.exports = ({ strapi }) => ({
98
93
  ...authorData,
99
94
  related: relation,
100
95
  approvalStatus: isApprovalFlowEnabled ? APPROVAL_STATUS.PENDING : null,
101
- }
96
+ },
97
+ populate: {
98
+ authorUser: true,
99
+ },
102
100
  });
103
101
  return this.getCommonService().sanitizeCommentEntity({
104
102
  ...entity,
@@ -109,20 +107,21 @@ module.exports = ({ strapi }) => ({
109
107
  },
110
108
 
111
109
  // Update a comment
112
- async update(id, relation, data, user) {
110
+ async update(id, relation, data, user = undefined) {
113
111
  const { content } = data;
114
112
 
115
113
  const singleRelationFulfilled = relation && REGEX.relatedUid.test(relation)
116
114
 
117
115
  if(!singleRelationFulfilled) {
118
- throw new PluginError(400, `Field "related" got incorrect format, use format like "api::<collection name>.<content type name>:<entity id>"`);
116
+ throw new PluginError(400, `Request property "relation" got incorrect format, use format like "api::<collection name>.<content type name>:<entity id>"`);
119
117
  }
120
118
 
121
119
  const existingEntity = await this.getCommonService().findOne({
122
120
  id,
123
121
  related: relation,
124
122
  });
125
- const validContext = isValidUserContext(user);
123
+
124
+ const validContext = this.getCommonService().isValidUserContext(user);
126
125
 
127
126
  if (!validContext) {
128
127
  throw resolveUserContextError(user);
@@ -133,17 +132,17 @@ module.exports = ({ strapi }) => ({
133
132
  const entity = await strapi.db.query(getModelUid('comment')).update({
134
133
  where: { id },
135
134
  data: { content },
136
- populate: { threadOf: true },
135
+ populate: { threadOf: true, authorUser: true },
137
136
  });
138
137
  return this.getCommonService().sanitizeCommentEntity(entity);
139
138
  }
140
139
  }
141
- throw new PluginError(409, 'Action on that entity is not allowed');
140
+ throw new PluginError(403, `You're not allowed to take an action on that entity. Make sure you've provided "author" property in a payload or authenticated your request properly.`);
142
141
  },
143
142
 
144
143
  // Report abuse in comment
145
- async reportAbuse(id, relation, payload, user) {
146
- if (!isValidUserContext(user)) {
144
+ async reportAbuse(id, relation, payload, user = undefined) {
145
+ if (!this.getCommonService().isValidUserContext(user)) {
147
146
  throw resolveUserContextError(user);
148
147
  }
149
148
  const reportAgainstEntity = await this.getCommonService().findOne({
@@ -173,18 +172,34 @@ module.exports = ({ strapi }) => ({
173
172
  throw new PluginError(500, 'Report cannot be created');
174
173
  }
175
174
  }
176
- throw new PluginError(409, 'Action on that entity is not allowed');
175
+ throw new PluginError(403, `You're not allowed to take an action on that entity. Make sure that comment exist or you've authenticated your request properly.`);
177
176
  },
178
177
 
179
- async markAsRemoved(id, relation, authorId){
180
- const entity = await this.getCommonService().findOne({
181
- id,
182
- related: relation,
183
- $or: [{ authorUser: authorId }, { authorId }],
184
- });
178
+ async markAsRemoved(id, relation, authorId, user = undefined){
179
+ if (!this.getCommonService().isValidUserContext(user)) {
180
+ throw resolveUserContextError(user);
181
+ }
182
+
183
+ const author = user?.id || authorId;
185
184
 
186
- if (!entity) {
187
- throw new PluginError(404, 'Entity not exist');
185
+ if (!author) {
186
+ throw new PluginError(403, `You're not allowed to take an action on that entity. Make sure that you've provided proper "authorId" or authenticated your request properly.`);
187
+ }
188
+
189
+ let entity;
190
+ try {
191
+ const byAuthor = user?.id ? {
192
+ authorUser: author
193
+ } : {
194
+ authorId: author
195
+ }
196
+ entity = await this.getCommonService().findOne({
197
+ id,
198
+ related: relation,
199
+ ...byAuthor,
200
+ });
201
+ } catch(e) {
202
+ throw new PluginError(404, `Entity does not exist or you're not allowed to take an action on it`);
188
203
  }
189
204
 
190
205
  const removedEntity = await strapi.db.query(getModelUid('comment'))
@@ -194,7 +209,7 @@ module.exports = ({ strapi }) => ({
194
209
  related: relation
195
210
  },
196
211
  data: { removed: true },
197
- populate: { threadOf: true },
212
+ populate: { threadOf: true, authorUser: true },
198
213
  })
199
214
  await this.markAsRemovedNested(id, true);
200
215
 
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const { isArray, isNumber, isObject, first, parseInt } = require('lodash');
3
+ const { isArray, isNumber, isObject, isNil, first, parseInt } = require('lodash');
4
4
  // const { sanitizeEntity } = require('strapi-utils');
5
5
  const PluginError = require('./../utils/error');
6
6
  const {
@@ -86,8 +86,12 @@ module.exports = ({ strapi }) => ({
86
86
  async findOne(criteria) {
87
87
  const entity = await strapi.db.query(getModelUid('comment'))
88
88
  .findOne({
89
- where: criteria
90
- }, ['related', 'reports']);
89
+ where: criteria,
90
+ populate: {
91
+ reports: true,
92
+ authorUser: true,
93
+ },
94
+ });
91
95
  if (!entity){
92
96
  throw new PluginError(404, 'Not found');
93
97
  }
@@ -154,15 +158,16 @@ module.exports = ({ strapi }) => ({
154
158
  }
155
159
  },
156
160
 
157
- sanitizeCommentEntity: (entity) => ({
158
- ...buildAuthorModel({
159
- ...entity,
160
- threadOf: isObject(entity.threadOf) ? buildAuthorModel(entity.threadOf) : entity.threadOf,
161
- }),
162
- }),
163
-
164
- // sanitizeCommentEntity: (entity) => ({
165
- // ...entity,
166
- // authorUser: sanitizeEntity(entity.authorUser, { model: strapi.plugins['users-permissions'].models.user }),
167
- // }),
161
+ sanitizeCommentEntity(entity) {
162
+ return {
163
+ ...buildAuthorModel({
164
+ ...entity,
165
+ threadOf: isObject(entity.threadOf) ? buildAuthorModel(entity.threadOf) : entity.threadOf,
166
+ }),
167
+ };
168
+ },
169
+
170
+ isValidUserContext(user) {
171
+ return user ? !isNil(user?.id) : true;
172
+ },
168
173
  });
@@ -1,7 +1,7 @@
1
1
  const BadWordsFilter = require('bad-words');
2
2
  const PluginError = require('./../../utils/error');
3
3
  const { REGEX } = require('./../../utils/constants')
4
- const { first, get, isObject, isNil } = require('lodash');
4
+ const { first, get, isObject, isEmpty } = require('lodash');
5
5
 
6
6
  const buildNestedStructure = (
7
7
  entities,
@@ -33,7 +33,7 @@ const buildNestedStructure = (
33
33
 
34
34
  module.exports = {
35
35
  isEqualEntity: (existing, data, user) => {
36
- const { authorUser, author: existingAuthor } = existing;
36
+ const { author: existingAuthor } = existing;
37
37
  const { author } = data;
38
38
 
39
39
  // Disallow approval status change by Client
@@ -42,10 +42,10 @@ module.exports = {
42
42
  }
43
43
 
44
44
  // Make sure that author is exact the same
45
- if (authorUser) {
46
- const existingUserId = authorUser?.id || authorUser;
47
- const receivedUserId = user?.id || author;
48
- return existingUserId === receivedUserId;
45
+ if (user) {
46
+ const existingUserId = existingAuthor?.id || existingAuthor;
47
+ const receivedUserId = user?.id || author?.id;
48
+ return receivedUserId && (existingUserId === receivedUserId);
49
49
  }
50
50
  return existingAuthor.id === author?.id;
51
51
  },
@@ -85,22 +85,27 @@ module.exports = {
85
85
 
86
86
  buildNestedStructure,
87
87
 
88
- isValidUserContext: (user = {}) => {
89
- const builtInContextEnabled = get(strapi.config, 'plugins.comments.enableUsers', false);
90
- return builtInContextEnabled ? !isNil(user.id) : true;
91
- },
92
-
93
88
  buildAuthorModel: (item) => {
94
89
  const { authorUser, authorId, authorName, authorEmail, authorAvatar, ...rest } = item;
95
- const author = authorUser || {
96
- id: authorId,
97
- name: authorName,
98
- email: authorEmail,
99
- avatar: authorAvatar,
100
- };
90
+ let author = {};
91
+ if (authorUser) {
92
+ author = {
93
+ id: authorUser.id,
94
+ name: authorUser.username,
95
+ email: authorUser.email,
96
+ avatar: authorUser.avatar,
97
+ };
98
+ } else if(authorId) {
99
+ author = {
100
+ id: authorId,
101
+ name: authorName,
102
+ email: authorEmail,
103
+ avatar: authorAvatar,
104
+ };
105
+ }
101
106
  return {
102
107
  ...rest,
103
- author,
108
+ author: isEmpty(author) ? undefined : author,
104
109
  };
105
110
  },
106
111
 
@@ -1,9 +0,0 @@
1
- module.exports = ({ nexus }) => nexus.objectType({
2
- name: "CommentAuthorUser",
3
- definition(t) {
4
- t.id("id")
5
- t.nonNull.string("firstName")
6
- t.string("lastName")
7
- t.string("email")
8
- }
9
- })