Skip to content

Commit

Permalink
Add the ability to create article comments
Browse files Browse the repository at this point in the history
  • Loading branch information
ellmetha committed Nov 19, 2023
1 parent b8db9dd commit 1288f0d
Show file tree
Hide file tree
Showing 9 changed files with 428 additions and 26 deletions.
119 changes: 119 additions & 0 deletions spec/apps/blogging/handlers/article_detail_handler_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -101,5 +101,124 @@ describe Blogging::ArticleDetailHandler do

handler.context["favorited"].should be_false
end

it "inserts an empty page of comments if the article has no comments" do
user = create_user(username: "test", email: "[email protected]", password: "insecure")

article = Blogging::Article.create!(
title: "My article",
slug: "my-article",
description: "My article description",
body: "# Hello World",
author: user.profile!,
)

request = Marten::HTTP::Request.new(method: "GET", resource: "/test/xyz")
request.session = Marten::HTTP::Session::Store::Cookie.new("sessionkey")
MartenAuth.sign_in(request, user)

handler = Blogging::ArticleDetailHandler.new(
request,
Marten::Routing::MatchParameters{"slug" => article.slug!}
)

handler.context["comments"].empty?.should be_true
end

it "inserts the first page of comments if the article has comments and no page parameter is set" do
user = create_user(username: "test", email: "[email protected]", password: "insecure")
other_user = create_user(username: "test2", email: "[email protected]", password: "insecure")

article = Blogging::Article.create!(
title: "My article",
slug: "my-article",
description: "My article description",
body: "# Hello World",
author: user.profile!,
)
other_article = Blogging::Article.create!(
title: "My other article",
slug: "my-other-article",
description: "My other-article description",
body: "# Hello World",
author: user.profile!,
)

15.times do |i|
Blogging::Comment.create!(
article: article,
body: "# Hello World #{i}",
author: other_user.profile!,
)

Blogging::Comment.create!(
article: other_article,
body: "# Hello World",
author: other_user.profile!,
)
end

request = Marten::HTTP::Request.new(method: "GET", resource: "/test/xyz")
request.session = Marten::HTTP::Session::Store::Cookie.new("sessionkey")
MartenAuth.sign_in(request, user)

handler = Blogging::ArticleDetailHandler.new(
request,
Marten::Routing::MatchParameters{"slug" => article.slug!}
)

handler.context["comments"].empty?.should be_false
handler.context["comments"].size.should eq 10
handler.context["comments"].to_a[0].raw.should be_a Blogging::Comment
handler.context["comments"].to_a.all? { |c| c.raw.as(Blogging::Comment).article == article }.should be_true
end

it "inserts the right page of comments if the article has comments and no page parameter is set" do
user = create_user(username: "test", email: "[email protected]", password: "insecure")
other_user = create_user(username: "test2", email: "[email protected]", password: "insecure")

article = Blogging::Article.create!(
title: "My article",
slug: "my-article",
description: "My article description",
body: "# Hello World",
author: user.profile!,
)
other_article = Blogging::Article.create!(
title: "My other article",
slug: "my-other-article",
description: "My other-article description",
body: "# Hello World",
author: user.profile!,
)

15.times do |i|
Blogging::Comment.create!(
article: article,
body: "# Hello World #{i}",
author: other_user.profile!,
)

Blogging::Comment.create!(
article: other_article,
body: "# Hello World",
author: other_user.profile!,
)
end

request = Marten::HTTP::Request.new(method: "GET", resource: "/test/xyz?comment_page=2")
request.session = Marten::HTTP::Session::Store::Cookie.new("sessionkey")
MartenAuth.sign_in(request, user)

handler = Blogging::ArticleDetailHandler.new(
request,
Marten::Routing::MatchParameters{"slug" => article.slug!}
)

handler.context["comments"].empty?.should be_false
handler.context["comments"].size.should eq 5
handler.context["comments"].to_a[0].raw.should be_a Blogging::Comment
handler.context["comments"].to_a.all? { |c| c.raw.as(Blogging::Comment).article == article }.should be_true
end
end
end
66 changes: 66 additions & 0 deletions spec/apps/blogging/handlers/comment_create_handler_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
require "./spec_helper"

describe Blogging::CommentCreateHandler do
describe "#post" do
it "redirects to the sign in page if the user is not authenticated" do
user = create_user(username: "test", email: "[email protected]", password: "insecure")

article = Blogging::Article.create!(
title: "My article",
slug: "my-article",
description: "My article description",
body: "# Hello World",
author: user.profile!,
)

response = Marten::Spec.client.post(
Marten.routes.reverse("blogging:comment_create", slug: article.slug!),
data: {"body" => "My super comment"}
)

response.status.should eq 302
response.headers["Location"].should eq Marten.routes.reverse("auth:sign_in")

article.reload.comments.size.should eq 0
end

it "creates a new article comment if the user is authenticated" do
user = create_user(username: "test1", email: "[email protected]", password: "insecure")
Marten::Spec.client.sign_in(user)

other_user = create_user(username: "test2", email: "[email protected]", password: "insecure")

article = Blogging::Article.create!(
title: "My article",
slug: "my-article",
description: "My article description",
body: "# Hello World",
author: other_user.profile!,
)

response = Marten::Spec.client.post(
Marten.routes.reverse("blogging:comment_create", slug: article.slug!),
data: {"body" => "My super comment"}
)

response.status.should eq 302
response.headers["Location"].should eq Marten.routes.reverse("blogging:article_detail", slug: article.slug!)

article.reload.comments.size.should eq 1
article.comments.first!.body.should eq "My super comment"
article.comments.first!.author.should eq user.profile!
end

it "raises a not found error if the article is not found" do
user = create_user(username: "test1", email: "[email protected]", password: "insecure")
Marten::Spec.client.sign_in(user)

expect_raises(Marten::DB::Errors::RecordNotFound) do
Marten::Spec.client.post(
Marten.routes.reverse("blogging:comment_create", slug: "not-found"),
data: {"body" => "My super comment"}
)
end
end
end
end
118 changes: 118 additions & 0 deletions spec/apps/blogging/handlers/comment_delete_handler_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
require "./spec_helper"

describe Blogging::CommentDeleteHandler do
describe "#post" do
it "redirects to the sign in page if the user is not authenticated" do
user = create_user(username: "test", email: "[email protected]", password: "insecure")

article = Blogging::Article.create!(
title: "My article",
slug: "my-article",
description: "My article description",
body: "# Hello World",
author: user.profile!,
)
comment = article.comments.create!(article: article, body: "My super comment", author: user.profile!)

response = Marten::Spec.client.post(
Marten.routes.reverse("blogging:comment_delete", slug: article.slug!, comment_id: comment.id!),
)

response.status.should eq 302
response.headers["Location"].should eq Marten.routes.reverse("auth:sign_in")

comment.reload.persisted?.should be_true
end

it "deletes the targeted comment and redirects to the article page" do
user = create_user(username: "test", email: "[email protected]", password: "insecure")
Marten::Spec.client.sign_in(user)

article = Blogging::Article.create!(
title: "My article",
slug: "my-article",
description: "My article description",
body: "# Hello World",
author: user.profile!,
)
comment = article.comments.create!(article: article, body: "My super comment", author: user.profile!)

response = Marten::Spec.client.post(
Marten.routes.reverse("blogging:comment_delete", slug: article.slug!, comment_id: comment.id!),
)

response.status.should eq 302
response.headers["Location"].should eq Marten.routes.reverse("blogging:article_detail", slug: article.slug!)

expect_raises(Marten::DB::Errors::RecordNotFound) do
comment.reload
end
end

it "raises a not found error if the article is not found" do
user = create_user(username: "test", email: "[email protected]", password: "insecure")

article = Blogging::Article.create!(
title: "My article",
slug: "my-article",
description: "My article description",
body: "# Hello World",
author: user.profile!,
)
comment = Blogging::Comment.create!(article: article, body: "My super comment", author: user.profile!)

Marten::Spec.client.sign_in(user)

expect_raises(Marten::DB::Errors::RecordNotFound) do
Marten::Spec.client.post(
Marten.routes.reverse("blogging:comment_delete", slug: "not-found", comment_id: comment.id!),
data: {"body" => "My super comment"}
)
end
end

it "raises a not found error if the comment is not found" do
user = create_user(username: "test", email: "[email protected]", password: "insecure")

article = Blogging::Article.create!(
title: "My article",
slug: "my-article",
description: "My article description",
body: "# Hello World",
author: user.profile!,
)

Marten::Spec.client.sign_in(user)

expect_raises(Marten::DB::Errors::RecordNotFound) do
Marten::Spec.client.post(
Marten.routes.reverse("blogging:comment_delete", slug: article.slug!, comment_id: 0),
data: {"body" => "My super comment"}
)
end
end

it "raises a permission denied error if the comment was created by another user" do
user = create_user(username: "test1", email: "[email protected]", password: "insecure")
other_user = create_user(username: "test2", email: "[email protected]", password: "insecure")

article = Blogging::Article.create!(
title: "My article",
slug: "my-article",
description: "My article description",
body: "# Hello World",
author: user.profile!,
)
comment = Blogging::Comment.create!(article: article, body: "My super comment", author: other_user.profile!)

Marten::Spec.client.sign_in(user)

expect_raises(Marten::HTTP::Errors::PermissionDenied) do
Marten::Spec.client.post(
Marten.routes.reverse("blogging:comment_delete", slug: article.slug!, comment_id: comment.id!),
data: {"body" => "My super comment"}
)
end
end
end
end
16 changes: 16 additions & 0 deletions src/apps/blogging/handlers/article_detail_handler.cr
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,23 @@ module Blogging
ctx[:favorited] = request.user!.profile!.favorite_articles.exists?(pk: record.pk)
end

ctx[:comments] = paginated_comments

ctx
end

private COMMENT_PAGE_PARAM = "comment_page"
private COMMENT_PAGE_SIZE = 10

private def comment_page_number
request.query_params[COMMENT_PAGE_PARAM]?.try(&.to_i) || 1
rescue ArgumentError
1
end

private def paginated_comments
paginator = record.comments.order("-created_at").paginator(COMMENT_PAGE_SIZE)
paginator.page(comment_page_number)
end
end
end
37 changes: 37 additions & 0 deletions src/apps/blogging/handlers/comment_create_handler.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
module Blogging
class CommentCreateHandler < Marten::Handlers::Schema
include Auth::RequireSignedInUser

@article : Blogging::Article?

schema CommentSchema
http_method_names :post

before_dispatch :retrieve_article

after_failed_schema_validation :redirect_to_article
after_successful_schema_validation :create_article_comment

private def article!
@article.not_nil!
end

private def create_article_comment
Blogging::Comment.create!(
article: article!,
author: request.user!.profile!,
body: schema.body!,
)

redirect_to_article
end

private def redirect_to_article
redirect reverse("blogging:article_detail", slug: article!.not_nil!.slug!)
end

private def retrieve_article
@article = Blogging::Article.get!(slug: params[:slug]?)
end
end
end
Loading

0 comments on commit 1288f0d

Please sign in to comment.