Comments API: hide deleted and hidden comments (plus docs) (#6482) [deploy]
* Modernize index test
* Hide comments body_html and user info if the comment is either deleted or hidden
* Shorten lines a bit
* Freeze repeated strings
* Document all /comments
* Document /comments/{id}
This commit is contained in:
parent
9000a42de3
commit
21fca3e0f2
5 changed files with 568 additions and 71 deletions
|
|
@ -27,7 +27,9 @@ module Api
|
|||
set_surrogate_key_header Comment.table_key, edge_cache_keys(tree_with_root_comment)
|
||||
end
|
||||
|
||||
ATTRIBUTES_FOR_SERIALIZATION = %i[id processed_html user_id ancestry].freeze
|
||||
ATTRIBUTES_FOR_SERIALIZATION = %i[
|
||||
id processed_html user_id ancestry deleted hidden_by_commentable_user
|
||||
].freeze
|
||||
private_constant :ATTRIBUTES_FOR_SERIALIZATION
|
||||
|
||||
private
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
class Comment < ApplicationRecord
|
||||
TITLE_DELETED = "[deleted]".freeze
|
||||
TITLE_HIDDEN = "[hidden by post author]".freeze
|
||||
|
||||
has_ancestry
|
||||
resourcify
|
||||
include Reactable
|
||||
|
||||
belongs_to :commentable, polymorphic: true, optional: true
|
||||
counter_culture :commentable
|
||||
belongs_to :user
|
||||
|
|
@ -76,8 +80,8 @@ class Comment < ApplicationRecord
|
|||
end
|
||||
|
||||
def title(length = 80)
|
||||
return "[deleted]" if deleted
|
||||
return "[hidden by post author]" if hidden_by_commentable_user
|
||||
return TITLE_DELETED if deleted
|
||||
return TITLE_HIDDEN if hidden_by_commentable_user
|
||||
|
||||
text = ActionController::Base.helpers.strip_tags(processed_html).strip
|
||||
truncated_text = ActionController::Base.helpers.truncate(text, length: length).gsub("'", "'").gsub("&", "&")
|
||||
|
|
|
|||
|
|
@ -1,6 +1,13 @@
|
|||
json.type_of "comment"
|
||||
json.type_of "comment"
|
||||
json.id_code comment.id_code_generated
|
||||
|
||||
json.id_code comment.id_code_generated
|
||||
json.body_html comment.processed_html
|
||||
|
||||
json.partial! "api/v0/shared/user", user: comment.user
|
||||
if comment.deleted?
|
||||
json.body_html "<p>#{Comment::TITLE_DELETED}</p>"
|
||||
json.set! :user, {}
|
||||
elsif comment.hidden_by_commentable_user?
|
||||
json.body_html "<p>#{Comment::TITLE_HIDDEN}</p>"
|
||||
json.set! :user, {}
|
||||
else
|
||||
json.body_html comment.processed_html
|
||||
json.partial! "api/v0/shared/user", user: comment.user
|
||||
end
|
||||
|
|
|
|||
376
docs/api_v0.yml
376
docs/api_v0.yml
|
|
@ -8,7 +8,7 @@ info:
|
|||
|
||||
Dates and date times, unless otherwise specified, must be in
|
||||
the [RFC 3339](https://tools.ietf.org/html/rfc3339) format.
|
||||
version: "0.6.2"
|
||||
version: "0.6.3"
|
||||
termsOfService: https://dev.to/terms
|
||||
contact:
|
||||
name: DEV Team
|
||||
|
|
@ -62,6 +62,7 @@ components:
|
|||
tokenUrl: https://dev.to/oauth/token
|
||||
refreshUrl: https://dev.to/oauth/token
|
||||
scopes: {}
|
||||
|
||||
schemas:
|
||||
APIError:
|
||||
type: object
|
||||
|
|
@ -484,6 +485,29 @@ components:
|
|||
description: Text color (hexadecimal)
|
||||
type: string
|
||||
|
||||
Comment:
|
||||
type: object
|
||||
required:
|
||||
- type_of
|
||||
- id_code
|
||||
- body_html
|
||||
- user
|
||||
- children
|
||||
properties:
|
||||
type_of:
|
||||
type: string
|
||||
id_code:
|
||||
type: string
|
||||
body_html:
|
||||
description: HTML formatted comment
|
||||
type: string
|
||||
user:
|
||||
$ref: "#/components/schemas/SharedUser"
|
||||
children:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/Comment"
|
||||
|
||||
Listing:
|
||||
type: object
|
||||
required:
|
||||
|
|
@ -587,6 +611,7 @@ components:
|
|||
description: Set it to "draft" to create an unpublished listing
|
||||
type: string
|
||||
enum: [draft]
|
||||
|
||||
ListingUpdate:
|
||||
type: object
|
||||
properties:
|
||||
|
|
@ -641,7 +666,6 @@ components:
|
|||
type: string
|
||||
enum: [bump, publish, unpublish]
|
||||
|
||||
|
||||
SharedUser:
|
||||
description: The resource creator
|
||||
type: object
|
||||
|
|
@ -706,6 +730,30 @@ components:
|
|||
items:
|
||||
type: string
|
||||
|
||||
WebhookIndex:
|
||||
description: Webhook
|
||||
type: object
|
||||
properties:
|
||||
type_of:
|
||||
type: string
|
||||
id:
|
||||
type: integer
|
||||
format: int64
|
||||
source:
|
||||
description: The name of the requester, eg. "DEV"
|
||||
type: string
|
||||
target_url:
|
||||
type: string
|
||||
format: url
|
||||
events:
|
||||
description: An array of events identifiers
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
created_at:
|
||||
type: string
|
||||
format: date-time
|
||||
|
||||
WebhookShow:
|
||||
description: Webhook
|
||||
type: object
|
||||
|
|
@ -732,30 +780,6 @@ components:
|
|||
user:
|
||||
$ref: "#/components/schemas/SharedUser"
|
||||
|
||||
WebhookIndex:
|
||||
description: Webhook
|
||||
type: object
|
||||
properties:
|
||||
type_of:
|
||||
type: string
|
||||
id:
|
||||
type: integer
|
||||
format: int64
|
||||
source:
|
||||
description: The name of the requester, eg. "DEV"
|
||||
type: string
|
||||
target_url:
|
||||
type: string
|
||||
format: url
|
||||
events:
|
||||
description: An array of events identifiers
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
created_at:
|
||||
type: string
|
||||
format: date-time
|
||||
|
||||
examples:
|
||||
ErrorBadRequest:
|
||||
value:
|
||||
|
|
@ -909,6 +933,202 @@ components:
|
|||
canonical_url: https://example.com/blog/hello
|
||||
organization_id: 1234
|
||||
|
||||
Comments:
|
||||
value:
|
||||
- type_of: comment
|
||||
id_code: m3m0
|
||||
body_html: |
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
|
||||
<html><body>
|
||||
<p>...</p>
|
||||
|
||||
</body></html>
|
||||
user:
|
||||
name: Heriberto Morissette
|
||||
username: heriberto_morissette
|
||||
twitter_username:
|
||||
github_username:
|
||||
website_url:
|
||||
profile_image: https://res.cloudinary.com/...jpeg
|
||||
profile_image_90: https://res.cloudinary.com/...jpeg
|
||||
children: []
|
||||
- type_of: comment
|
||||
id_code: m357
|
||||
body_html: |
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
|
||||
<html><body>
|
||||
<p>...</p>
|
||||
|
||||
<p>...</p>
|
||||
|
||||
</body></html>
|
||||
user:
|
||||
name: Dario Waelchi
|
||||
username: dario waelchi
|
||||
twitter_username:
|
||||
github_username:
|
||||
website_url:
|
||||
profile_image: https://res.cloudinary.com/...png
|
||||
profile_image_90: https://res.cloudinary.com/...png
|
||||
children:
|
||||
- type_of: comment
|
||||
id_code: m35m
|
||||
body_html: |
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
|
||||
<html><body>
|
||||
|
||||
<p>...</p>
|
||||
|
||||
</body></html>
|
||||
user:
|
||||
name: rhymes
|
||||
username: rhymes
|
||||
twitter_username:
|
||||
github_username:
|
||||
website_url:
|
||||
profile_image: https://res.cloudinary.com/...jpeg
|
||||
profile_image_90: https://res.cloudinary.com/practicaldev/image/fetch/s--SC90PuMi--/c_fill,f_auto,fl_progressive,h_90,q_auto,w_90/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/2693/146201.jpeg
|
||||
children: []
|
||||
CommentsDeleted:
|
||||
value:
|
||||
- type_of: comment
|
||||
id_code: m357
|
||||
body_html: <p>[deleted]</p>
|
||||
user: {}
|
||||
children:
|
||||
- type_of: comment
|
||||
id_code: m35m
|
||||
body_html: |
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
|
||||
<html><body>
|
||||
|
||||
<p>...</p>
|
||||
|
||||
</body></html>
|
||||
user:
|
||||
name: rhymes
|
||||
username: rhymes
|
||||
twitter_username:
|
||||
github_username:
|
||||
website_url:
|
||||
profile_image: https://res.cloudinary.com/...jpeg
|
||||
profile_image_90: https://res.cloudinary.com/practicaldev/image/fetch/s--SC90PuMi--/c_fill,f_auto,fl_progressive,h_90,q_auto,w_90/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/2693/146201.jpeg
|
||||
children: []
|
||||
CommentsHidden:
|
||||
value:
|
||||
- type_of: comment
|
||||
id_code: m357
|
||||
body_html: <p>[hidden by post author]</p>
|
||||
user: {}
|
||||
children:
|
||||
- type_of: comment
|
||||
id_code: m35m
|
||||
body_html: |
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
|
||||
<html><body>
|
||||
|
||||
<p>...</p>
|
||||
|
||||
</body></html>
|
||||
user:
|
||||
name: rhymes
|
||||
username: rhymes
|
||||
twitter_username:
|
||||
github_username:
|
||||
website_url:
|
||||
profile_image: https://res.cloudinary.com/...jpeg
|
||||
profile_image_90: https://res.cloudinary.com/practicaldev/image/fetch/s--SC90PuMi--/c_fill,f_auto,fl_progressive,h_90,q_auto,w_90/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/2693/146201.jpeg
|
||||
children: []
|
||||
Comment:
|
||||
value:
|
||||
type_of: comment
|
||||
id_code: m357
|
||||
body_html: |
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
|
||||
<html><body>
|
||||
<p>...</p>
|
||||
|
||||
<p>...</p>
|
||||
|
||||
</body></html>
|
||||
user:
|
||||
name: Dario Waelchi
|
||||
username: dario waelchi
|
||||
twitter_username:
|
||||
github_username:
|
||||
website_url:
|
||||
profile_image: https://res.cloudinary.com/...png
|
||||
profile_image_90: https://res.cloudinary.com/...png
|
||||
children:
|
||||
- type_of: comment
|
||||
id_code: m35m
|
||||
body_html: |
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
|
||||
<html><body>
|
||||
|
||||
<p>...</p>
|
||||
|
||||
</body></html>
|
||||
user:
|
||||
name: rhymes
|
||||
username: rhymes
|
||||
twitter_username:
|
||||
github_username:
|
||||
website_url:
|
||||
profile_image: https://res.cloudinary.com/...jpeg
|
||||
profile_image_90: https://res.cloudinary.com/....jpeg
|
||||
children: []
|
||||
CommentDeleted:
|
||||
value:
|
||||
type_of: comment
|
||||
id_code: m357
|
||||
body_html: <p>[deleted]</p>
|
||||
user: {}
|
||||
children:
|
||||
- type_of: comment
|
||||
id_code: m35m
|
||||
body_html: |
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
|
||||
<html><body>
|
||||
|
||||
<p>...</p>
|
||||
|
||||
</body></html>
|
||||
user:
|
||||
name: rhymes
|
||||
username: rhymes
|
||||
twitter_username:
|
||||
github_username:
|
||||
website_url:
|
||||
profile_image: https://res.cloudinary.com/...jpeg
|
||||
profile_image_90: https://res.cloudinary.com/...jpeg
|
||||
children: []
|
||||
CommentHidden:
|
||||
value:
|
||||
type_of: comment
|
||||
id_code: m357
|
||||
body_html: <p>[hidden by post author]</p>
|
||||
user: {}
|
||||
children:
|
||||
- type_of: comment
|
||||
id_code: m35m
|
||||
body_html: |
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
|
||||
<html><body>
|
||||
|
||||
<p>...</p>
|
||||
|
||||
</body></html>
|
||||
user:
|
||||
name: rhymes
|
||||
username: rhymes
|
||||
twitter_username:
|
||||
github_username:
|
||||
website_url:
|
||||
profile_image: https://res.cloudinary.com/...jpeg
|
||||
profile_image_90: https://res.cloudinary.com/...jpeg
|
||||
children: []
|
||||
|
||||
Listings:
|
||||
value:
|
||||
- type_of: classified_listing
|
||||
|
|
@ -1026,6 +1246,8 @@ components:
|
|||
tags:
|
||||
- name: articles
|
||||
description: Articles are all the posts users create on DEV
|
||||
- name: comments
|
||||
description: Users can leave comments to articles and podcasts episodes
|
||||
- name: listings
|
||||
description: Listings are classified ads
|
||||
- name: users
|
||||
|
|
@ -1595,6 +1817,108 @@ paths:
|
|||
source: |
|
||||
curl -H "api-key: API_KEY" https://dev.to/api/articles/me/all
|
||||
|
||||
/comments:
|
||||
get:
|
||||
operationId: getCommentsByArticleId
|
||||
summary: Comments
|
||||
description: |
|
||||
This endpoint allows the client to retrieve all comments belonging to an
|
||||
article as threaded conversations.
|
||||
|
||||
It will return the all top level comments with their nested comments as
|
||||
threads. See the format specification for further details.
|
||||
tags:
|
||||
- comments
|
||||
parameters:
|
||||
- name: a_id
|
||||
in: query
|
||||
description: Article identifier.
|
||||
schema:
|
||||
type: integer
|
||||
format: int32
|
||||
example: 270180
|
||||
responses:
|
||||
200:
|
||||
description: A list of threads of comments
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/Comment"
|
||||
examples:
|
||||
comments-success:
|
||||
$ref: "#/components/examples/Comments"
|
||||
comments-success-deleted:
|
||||
$ref: "#/components/examples/CommentsDeleted"
|
||||
comments-success-hidden:
|
||||
$ref: "#/components/examples/CommentsHidden"
|
||||
404:
|
||||
description: Resource not found
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/APIError"
|
||||
examples:
|
||||
article-not-found:
|
||||
$ref: "#/components/examples/ErrorNotFound"
|
||||
x-code-samples: # https://github.com/Redocly/redoc/blob/master/docs/redoc-vendor-extensions.md#x-code-samples
|
||||
- lang: Shell
|
||||
label: curl (all comments of an article)
|
||||
source: |
|
||||
curl https://dev.to/api/comments?a_id=270180
|
||||
|
||||
/comments/{id}:
|
||||
get:
|
||||
operationId: getCommentById
|
||||
summary: Comment
|
||||
description: |
|
||||
This endpoint allows the client to retrieve a comment as well as his
|
||||
descendants comments.
|
||||
|
||||
It will return the required comment (the root) with its nested
|
||||
descendants as a thread.
|
||||
|
||||
See the format specification for further details.
|
||||
tags:
|
||||
- comments
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
description: Comment identifier.
|
||||
schema:
|
||||
type: string
|
||||
example: m35m
|
||||
responses:
|
||||
200:
|
||||
description: A comment and its descendants
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Comment"
|
||||
examples:
|
||||
comment-success:
|
||||
$ref: "#/components/examples/Comment"
|
||||
comments-success-deleted:
|
||||
$ref: "#/components/examples/CommentDeleted"
|
||||
comments-success-hidden:
|
||||
$ref: "#/components/examples/CommentHidden"
|
||||
404:
|
||||
description: Resource not found
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/APIError"
|
||||
examples:
|
||||
article-not-found:
|
||||
$ref: "#/components/examples/ErrorNotFound"
|
||||
x-code-samples: # https://github.com/Redocly/redoc/blob/master/docs/redoc-vendor-extensions.md#x-code-samples
|
||||
- lang: Shell
|
||||
label: curl (a comment and its descendants)
|
||||
source: |
|
||||
curl https://dev.to/api/comments/m51e
|
||||
|
||||
/listings:
|
||||
get:
|
||||
operationId: getListings
|
||||
|
|
|
|||
|
|
@ -1,58 +1,93 @@
|
|||
require "rails_helper"
|
||||
|
||||
RSpec.describe "Api::V0::Comments", type: :request do
|
||||
def json_response
|
||||
JSON.parse(response.body)
|
||||
end
|
||||
|
||||
let_it_be(:article) { create(:article) }
|
||||
let_it_be(:root_comment) { create(:comment, commentable: article) }
|
||||
let_it_be(:child_comment) { create(:comment, commentable: article, parent: root_comment) }
|
||||
let_it_be(:grandchild_comment) { create(:comment, commentable: article, parent: child_comment) }
|
||||
let_it_be(:great_grandchild_comment) { create(:comment, commentable: article, parent: grandchild_comment) }
|
||||
let_it_be_changeable(:child_comment) do
|
||||
create(:comment, commentable: article, parent: root_comment)
|
||||
end
|
||||
let_it_be(:grandchild_comment) do
|
||||
create(:comment, commentable: article, parent: child_comment)
|
||||
end
|
||||
let_it_be(:great_grandchild_comment) do
|
||||
create(:comment, commentable: article, parent: grandchild_comment)
|
||||
end
|
||||
|
||||
def find_root_comment(response)
|
||||
response.parsed_body.detect do |cm|
|
||||
cm["id_code"] == root_comment.id_code_generated
|
||||
end
|
||||
end
|
||||
|
||||
def find_child_comment(response, action = :index)
|
||||
body = response.parsed_body
|
||||
|
||||
root_comment_json = if action == :index
|
||||
body.detect do |cm|
|
||||
cm["id_code"] == root_comment.id_code_generated
|
||||
end
|
||||
else
|
||||
body
|
||||
end
|
||||
|
||||
root_comment_json["children"].detect do |cm|
|
||||
cm["id_code"] == child_comment["id_code"]
|
||||
end
|
||||
end
|
||||
|
||||
describe "GET /api/comments" do
|
||||
it "returns not found if wrong article id" do
|
||||
get "/api/comments?a_id=gobbledygook"
|
||||
get api_comments_path(a_id: "gobbledygook")
|
||||
|
||||
expect(response).to have_http_status(:not_found)
|
||||
end
|
||||
|
||||
it "returns comments for article" do
|
||||
get "/api/comments?a_id=#{article.id}"
|
||||
expect(json_response.size).to eq(1)
|
||||
get api_comments_path(a_id: article.id)
|
||||
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(response.parsed_body.size).to eq(1)
|
||||
end
|
||||
|
||||
it "does not include children comments in the root list" do
|
||||
get "/api/comments?a_id=#{article.id}"
|
||||
get api_comments_path(a_id: article.id)
|
||||
|
||||
expected_ids = article.comments.roots.map(&:id_code_generated)
|
||||
expect(json_response.map { |cm| cm["id_code"] }).to match_array(expected_ids)
|
||||
response_ids = response.parsed_body.map { |cm| cm["id_code"] }
|
||||
expect(response_ids).to match_array(expected_ids)
|
||||
end
|
||||
|
||||
it "includes children comments in the children list" do
|
||||
get "/api/comments?a_id=#{article.id}"
|
||||
comment_with_children = json_response.detect { |cm| cm["id_code"] == root_comment.id_code_generated }
|
||||
expect(comment_with_children["children"][0]["id_code"]).to eq(child_comment.id_code_generated)
|
||||
get api_comments_path(a_id: article.id)
|
||||
|
||||
child_comment_json = find_child_comment(response)
|
||||
expect(child_comment_json["id_code"]).to eq(child_comment.id_code_generated)
|
||||
end
|
||||
|
||||
it "includes grandchildren comments in the children-children list" do
|
||||
get "/api/comments?a_id=#{article.id}"
|
||||
get api_comments_path(a_id: article.id)
|
||||
|
||||
comment_with_descendants = json_response.detect { |cm| cm["id_code"] == root_comment.id_code_generated }
|
||||
expect(comment_with_descendants["children"][0]["children"][0]["id_code"]).to eq(grandchild_comment.id_code_generated)
|
||||
root_comment_json = find_root_comment(response)
|
||||
grandchild_comment_json_id = root_comment_json.dig(
|
||||
"children", 0, "children", 0, "id_code"
|
||||
)
|
||||
expect(grandchild_comment_json_id).to eq(grandchild_comment.id_code_generated)
|
||||
end
|
||||
|
||||
it "includes great-grandchildren comments in the children-children-children list" do
|
||||
get "/api/comments?a_id=#{article.id}"
|
||||
get api_comments_path(a_id: article.id)
|
||||
|
||||
comment_with_descendants = json_response.detect { |cm| cm["id_code"] == root_comment.id_code_generated }
|
||||
json_great_grandchild_id_code = comment_with_descendants["children"][0]["children"][0]["children"][0]["id_code"]
|
||||
expect(json_great_grandchild_id_code).to eq(great_grandchild_comment.id_code_generated)
|
||||
root_comment_json = find_root_comment(response)
|
||||
great_grandchild_comment_json_id = root_comment_json.dig(
|
||||
"children", 0, "children", 0, "children", 0, "id_code"
|
||||
)
|
||||
expect(great_grandchild_comment_json_id).to eq(great_grandchild_comment.id_code_generated)
|
||||
end
|
||||
|
||||
it "sets the correct edge caching surrogate key for all the comments" do
|
||||
sibling_root_comment = create(:comment, commentable: article)
|
||||
|
||||
get "/api/comments?a_id=#{article.id}"
|
||||
get api_comments_path(a_id: article.id)
|
||||
|
||||
expected_key = [
|
||||
article.record_key, "comments", sibling_root_comment.record_key,
|
||||
|
|
@ -61,43 +96,108 @@ RSpec.describe "Api::V0::Comments", type: :request do
|
|||
].to_set
|
||||
expect(response.headers["surrogate-key"].split.to_set).to eq(expected_key)
|
||||
end
|
||||
|
||||
context "when a comment is deleted" do
|
||||
before do
|
||||
child_comment.update(deleted: true)
|
||||
end
|
||||
|
||||
it "appears in the thread" do
|
||||
get api_comments_path(a_id: article.id)
|
||||
|
||||
expect(find_child_comment(response)["id_code"]).to eq(child_comment.id_code_generated)
|
||||
end
|
||||
|
||||
it "replaces the body_html" do
|
||||
get api_comments_path(a_id: article.id)
|
||||
|
||||
expect(find_child_comment(response)["body_html"]).to eq("<p>#{Comment::TITLE_DELETED}</p>")
|
||||
end
|
||||
|
||||
it "does not render the user information" do
|
||||
get api_comments_path(a_id: article.id)
|
||||
|
||||
expect(find_child_comment(response)["user"]).to be_empty
|
||||
end
|
||||
|
||||
it "still has children comments" do
|
||||
get api_comments_path(a_id: article.id)
|
||||
|
||||
expect(find_child_comment(response)["children"]).not_to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
context "when a comment is hidden" do
|
||||
before do
|
||||
child_comment.update(hidden_by_commentable_user: true)
|
||||
end
|
||||
|
||||
it "appears in the thread" do
|
||||
get api_comments_path(a_id: article.id)
|
||||
|
||||
expect(find_child_comment(response)["id_code"]).to eq(child_comment.id_code_generated)
|
||||
end
|
||||
|
||||
it "replaces the body_html" do
|
||||
get api_comments_path(a_id: article.id)
|
||||
|
||||
expect(find_child_comment(response)["body_html"]).to eq("<p>#{Comment::TITLE_HIDDEN}</p>")
|
||||
end
|
||||
|
||||
it "does not render the user information" do
|
||||
get api_comments_path(a_id: article.id)
|
||||
|
||||
expect(find_child_comment(response)["user"]).to be_empty
|
||||
end
|
||||
|
||||
it "still has children comments" do
|
||||
get api_comments_path(a_id: article.id)
|
||||
|
||||
expect(find_child_comment(response)["children"]).not_to be_empty
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "GET /api/comments/:id" do
|
||||
it "returns not found if wrong comment id" do
|
||||
get "/api/comments/foobar"
|
||||
get api_comment_path("foobar")
|
||||
|
||||
expect(response).to have_http_status(:not_found)
|
||||
end
|
||||
|
||||
it "returns the comment" do
|
||||
get "/api/comments/#{root_comment.id_code_generated}"
|
||||
expect(json_response["id_code"]).to eq(root_comment.id_code_generated)
|
||||
get api_comment_path(root_comment.id_code_generated)
|
||||
expect(response).to have_http_status(:ok)
|
||||
|
||||
expect(response.parsed_body["id_code"]).to eq(root_comment.id_code_generated)
|
||||
end
|
||||
|
||||
it "includes children comments in the children list" do
|
||||
get "/api/comments/#{root_comment.id_code_generated}"
|
||||
get api_comment_path(root_comment.id_code_generated)
|
||||
|
||||
comment_with_children = json_response
|
||||
expect(comment_with_children["children"][0]["id_code"]).to eq(child_comment.id_code_generated)
|
||||
expect(find_child_comment(response, :show)["id_code"]).to eq(child_comment.id_code_generated)
|
||||
end
|
||||
|
||||
it "includes grandchildren comments in the children-children list" do
|
||||
get "/api/comments/#{root_comment.id_code_generated}"
|
||||
get api_comment_path(root_comment.id_code_generated)
|
||||
|
||||
comment_with_descendants = json_response
|
||||
expect(comment_with_descendants["children"][0]["children"][0]["id_code"]).to eq(grandchild_comment.id_code_generated)
|
||||
grandchild_comment_json_id = response.parsed_body.dig(
|
||||
"children", 0, "children", 0, "id_code"
|
||||
)
|
||||
expect(grandchild_comment_json_id).to eq(grandchild_comment.id_code_generated)
|
||||
end
|
||||
|
||||
it "includes great-grandchildren comments in the children-children-children list" do
|
||||
get "/api/comments/#{root_comment.id_code_generated}"
|
||||
get api_comment_path(root_comment.id_code_generated)
|
||||
|
||||
comment_with_descendants = json_response
|
||||
json_great_grandchild_id_code = comment_with_descendants["children"][0]["children"][0]["children"][0]["id_code"]
|
||||
expect(json_great_grandchild_id_code).to eq(great_grandchild_comment.id_code_generated)
|
||||
great_grandchild_comment_json_id = response.parsed_body.dig(
|
||||
"children", 0, "children", 0, "children", 0, "id_code"
|
||||
)
|
||||
expect(great_grandchild_comment_json_id).to eq(great_grandchild_comment.id_code_generated)
|
||||
end
|
||||
|
||||
it "sets the correct edge caching surrogate key for all the comments" do
|
||||
get "/api/comments/#{root_comment.id_code_generated}"
|
||||
get api_comment_path(root_comment.id_code_generated)
|
||||
|
||||
expected_key = [
|
||||
"comments", root_comment.record_key, child_comment.record_key,
|
||||
|
|
@ -105,5 +205,65 @@ RSpec.describe "Api::V0::Comments", type: :request do
|
|||
].to_set
|
||||
expect(response.headers["surrogate-key"].split.to_set).to eq(expected_key)
|
||||
end
|
||||
|
||||
context "when a comment is deleted" do
|
||||
before do
|
||||
child_comment.update(deleted: true)
|
||||
end
|
||||
|
||||
it "appears in the thread" do
|
||||
get api_comment_path(root_comment.id_code_generated)
|
||||
|
||||
expect(find_child_comment(response, :show)["id_code"]).to eq(child_comment.id_code_generated)
|
||||
end
|
||||
|
||||
it "replaces the body_html" do
|
||||
get api_comment_path(root_comment.id_code_generated)
|
||||
|
||||
expect(find_child_comment(response, :show)["body_html"]).to eq("<p>[deleted]</p>")
|
||||
end
|
||||
|
||||
it "does not render the user information" do
|
||||
get api_comment_path(root_comment.id_code_generated)
|
||||
|
||||
expect(find_child_comment(response, :show)["user"]).to be_empty
|
||||
end
|
||||
|
||||
it "still has children comments" do
|
||||
get api_comment_path(root_comment.id_code_generated)
|
||||
|
||||
expect(find_child_comment(response, :show)["children"]).not_to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
context "when a comment is hidden" do
|
||||
before do
|
||||
child_comment.update(hidden_by_commentable_user: true)
|
||||
end
|
||||
|
||||
it "appears in the thread" do
|
||||
get api_comment_path(root_comment.id_code_generated)
|
||||
|
||||
expect(find_child_comment(response, :show)["id_code"]).to eq(child_comment.id_code_generated)
|
||||
end
|
||||
|
||||
it "replaces the body_html" do
|
||||
get api_comment_path(root_comment.id_code_generated)
|
||||
|
||||
expect(find_child_comment(response, :show)["body_html"]).to eq("<p>[hidden by post author]</p>")
|
||||
end
|
||||
|
||||
it "does not render the user information" do
|
||||
get api_comment_path(root_comment.id_code_generated)
|
||||
|
||||
expect(find_child_comment(response, :show)["user"]).to be_empty
|
||||
end
|
||||
|
||||
it "still has children comments" do
|
||||
get api_comment_path(root_comment.id_code_generated)
|
||||
|
||||
expect(find_child_comment(response, :show)["children"]).not_to be_empty
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue