From df039253eefd10620c5df0501633ad2e8a725fa8 Mon Sep 17 00:00:00 2001 From: mydearxym Date: Wed, 2 Jun 2021 17:36:18 +0800 Subject: [PATCH 1/8] feat(post-qa): basic setup --- lib/groupher_server/accounts/helper/loader.ex | 8 +---- .../cms/delegates/article_comment.ex | 5 ++++ .../cms/embeds/article_comment_meta.ex | 29 ++++++++++--------- lib/groupher_server/cms/post.ex | 6 +++- ...0210602090617_add_qustion_mark_to_post.exs | 10 +++++++ .../cms/articles/post_test.exs | 28 ++++++++++++++++++ .../cms/comments/job_comment_test.exs | 1 - test/support/factory.ex | 4 ++- 8 files changed, 68 insertions(+), 23 deletions(-) create mode 100644 priv/repo/migrations/20210602090617_add_qustion_mark_to_post.exs diff --git a/lib/groupher_server/accounts/helper/loader.ex b/lib/groupher_server/accounts/helper/loader.ex index d371f4957..c6c93f34a 100644 --- a/lib/groupher_server/accounts/helper/loader.ex +++ b/lib/groupher_server/accounts/helper/loader.ex @@ -5,7 +5,7 @@ defmodule GroupherServer.Accounts.Helper.Loader do import Ecto.Query, warn: false alias Helper.QueryBuilder - alias GroupherServer.{Accounts, CMS, Repo} + alias GroupherServer.{CMS, Repo} def data, do: Dataloader.Ecto.new(Repo, query: &query/2) @@ -17,10 +17,4 @@ defmodule GroupherServer.Accounts.Helper.Loader do end def query(queryable, _args), do: queryable - - defp count_contents(queryable) do - queryable - |> group_by([f], f.user_id) - |> select([f], count(f.id)) - end end diff --git a/lib/groupher_server/cms/delegates/article_comment.ex b/lib/groupher_server/cms/delegates/article_comment.ex index ea1aa15fe..b4431100a 100644 --- a/lib/groupher_server/cms/delegates/article_comment.ex +++ b/lib/groupher_server/cms/delegates/article_comment.ex @@ -102,6 +102,11 @@ defmodule GroupherServer.CMS.Delegate.ArticleComment do |> Multi.run(:update_article_comments_count, fn _, %{create_article_comment: comment} -> update_article_comments_count(comment, :inc) end) + |> Multi.run(:set_question_flag_ifneed, fn _, _ -> + IO.inspect(article.meta, label: "hello now?") + # ORM.update(article, %{active_at: article.inserted_at}) + {:ok, :pass} + end) |> Multi.run(:add_participator, fn _, _ -> add_participator_to_article(article, user) end) diff --git a/lib/groupher_server/cms/embeds/article_comment_meta.ex b/lib/groupher_server/cms/embeds/article_comment_meta.ex index 3670d38d3..941f5e84e 100644 --- a/lib/groupher_server/cms/embeds/article_comment_meta.ex +++ b/lib/groupher_server/cms/embeds/article_comment_meta.ex @@ -7,24 +7,24 @@ defmodule GroupherServer.CMS.Embeds.ArticleCommentMeta do import Ecto.Changeset - @optional_fields ~w(is_article_author_upvoted is_solution report_count is_reply_to_others reported_count reported_user_ids)a - - @default_meta %{ - is_article_author_upvoted: false, - is_solution: false, - is_reply_to_others: false, - report_count: 0, - upvoted_user_ids: [], - reported_user_ids: [], - reported_count: 0 - } + @optional_fields ~w(is_article_author_upvoted report_count is_reply_to_others reported_count reported_user_ids is_solution is_for_question)a @doc "for test usage" - def default_meta(), do: @default_meta + def default_meta() do + %{ + is_article_author_upvoted: false, + is_reply_to_others: false, + report_count: 0, + upvoted_user_ids: [], + reported_user_ids: [], + reported_count: 0, + is_solution: false, + is_for_question: false + } + end embedded_schema do field(:is_article_author_upvoted, :boolean, default: false) - field(:is_solution, :boolean, default: false) # used in replies mode, for those reply to other user in replies box (for frontend) # 用于回复模式,指代这条回复是回复“回复列表其他人的” (方便前端展示) field(:is_reply_to_others, :boolean, default: false) @@ -33,6 +33,9 @@ defmodule GroupherServer.CMS.Embeds.ArticleCommentMeta do field(:upvoted_user_ids, {:array, :integer}, default: []) field(:reported_user_ids, {:array, :integer}, default: []) field(:reported_count, :integer, default: 0) + + field(:is_solution, :boolean, default: false) + field(:is_for_question, :boolean, default: false) end def changeset(struct, params) do diff --git a/lib/groupher_server/cms/post.ex b/lib/groupher_server/cms/post.ex index e23ab52b8..a257b1503 100644 --- a/lib/groupher_server/cms/post.ex +++ b/lib/groupher_server/cms/post.ex @@ -17,7 +17,8 @@ defmodule GroupherServer.CMS.Post do @required_fields ~w(title body digest length)a @article_cast_fields general_article_fields(:cast) - @optional_fields ~w(link_addr copy_right link_addr link_icon)a ++ @article_cast_fields + @optional_fields ~w(link_addr copy_right link_addr link_icon is_question is_solved)a ++ + @article_cast_fields @type t :: %Post{} schema "cms_posts" do @@ -29,6 +30,9 @@ defmodule GroupherServer.CMS.Post do field(:copy_right, :string) field(:length, :integer) + field(:is_question, :boolean, default: false) + field(:is_solved, :boolean, default: false) + # TODO: remove after legacy data migrated has_many(:comments, {"posts_comments", PostComment}) diff --git a/priv/repo/migrations/20210602090617_add_qustion_mark_to_post.exs b/priv/repo/migrations/20210602090617_add_qustion_mark_to_post.exs new file mode 100644 index 000000000..58811fa4d --- /dev/null +++ b/priv/repo/migrations/20210602090617_add_qustion_mark_to_post.exs @@ -0,0 +1,10 @@ +defmodule GroupherServer.Repo.Migrations.AddQustionMarkToPost do + use Ecto.Migration + + def change do + alter table(:cms_posts) do + add(:is_question, :boolean, default: false) + add(:is_solved, :boolean, default: false) + end + end +end diff --git a/test/groupher_server/cms/articles/post_test.exs b/test/groupher_server/cms/articles/post_test.exs index c88930b8a..28374f02b 100644 --- a/test/groupher_server/cms/articles/post_test.exs +++ b/test/groupher_server/cms/articles/post_test.exs @@ -146,4 +146,32 @@ defmodule GroupherServer.Test.CMS.Articles.Post do is_error?(reason, :undo_sink_old_article) end end + + describe "[cms post question]" do + @tag :wip + test "post have default question flags", ~m(user community post_attrs)a do + {:ok, post} = CMS.create_article(community, :post, post_attrs, user) + + assert not post.is_question + assert not post.is_solved + end + + @tag :wip + test "can create post with question", ~m(user community post_attrs)a do + post_attrs = Map.merge(post_attrs, %{is_question: true}) + {:ok, post} = CMS.create_article(community, :post, post_attrs, user) + + assert post.is_question + end + + @tag :wip + test "can update post with question", ~m(user community post_attrs)a do + post_attrs = Map.merge(post_attrs, %{is_question: true}) + {:ok, post} = CMS.create_article(community, :post, post_attrs, user) + assert post.is_question + + {:ok, post} = CMS.update_article(post, %{is_question: false}) + assert not post.is_question + end + end end diff --git a/test/groupher_server/cms/comments/job_comment_test.exs b/test/groupher_server/cms/comments/job_comment_test.exs index 4e5875df8..bd8f5f23a 100644 --- a/test/groupher_server/cms/comments/job_comment_test.exs +++ b/test/groupher_server/cms/comments/job_comment_test.exs @@ -65,7 +65,6 @@ defmodule GroupherServer.Test.CMS.Comments.JobComment do assert job.active_at == job.inserted_at end - @tag :wip test "old job will not update active after comment created", ~m(user)a do active_period_days = Map.get(@active_period, :job) diff --git a/test/support/factory.ex b/test/support/factory.ex index 866586ab5..71fbee81d 100644 --- a/test/support/factory.ex +++ b/test/support/factory.ex @@ -30,7 +30,9 @@ defmodule GroupherServer.Support.Factory do mock(:community) ], emotions: @default_emotions, - active_at: Timex.shift(Timex.now(), seconds: -1) + active_at: Timex.shift(Timex.now(), seconds: -1), + is_question: false, + is_solved: false } end From 711fe5740536abae5f611bae4e522405bacdcecd Mon Sep 17 00:00:00 2001 From: mydearxym Date: Wed, 2 Jun 2021 22:15:45 +0800 Subject: [PATCH 2/8] feat(post-qa): basic logic done --- lib/groupher_server/cms/article_comment.ex | 5 +- lib/groupher_server/cms/cms.ex | 2 + .../cms/delegates/article_comment.ex | 64 ++++++++-- .../cms/delegates/article_community.ex | 1 - .../cms/delegates/article_curd.ex | 11 +- .../cms/embeds/article_comment_meta.ex | 6 +- lib/groupher_server/cms/post.ex | 3 +- lib/helper/error_code.ex | 1 + ...15_add_qustion_mark_to_article_comment.exs | 9 ++ ...0602135346_add_solution_digest_to_post.exs | 9 ++ .../cms/articles/post_test.exs | 6 +- .../cms/comments/post_comment_test.exs | 112 ++++++++++++++++++ test/support/factory.ex | 1 + 13 files changed, 209 insertions(+), 21 deletions(-) create mode 100644 priv/repo/migrations/20210602111315_add_qustion_mark_to_article_comment.exs create mode 100644 priv/repo/migrations/20210602135346_add_solution_digest_to_post.exs diff --git a/lib/groupher_server/cms/article_comment.ex b/lib/groupher_server/cms/article_comment.ex index 121bfbcea..5999fc3b1 100644 --- a/lib/groupher_server/cms/article_comment.ex +++ b/lib/groupher_server/cms/article_comment.ex @@ -17,8 +17,8 @@ defmodule GroupherServer.CMS.ArticleComment do @article_threads get_config(:article, :threads) @required_fields ~w(body_html author_id)a - @optional_fields ~w(reply_to_id replies_count is_folded is_deleted floor is_article_author thread)a - @updatable_fields ~w(is_folded is_deleted floor upvotes_count is_pinned)a + @optional_fields ~w(reply_to_id replies_count is_folded is_deleted floor is_article_author thread is_for_question)a + @updatable_fields ~w(is_folded is_deleted floor upvotes_count is_pinned is_for_question)a @article_fields @article_threads |> Enum.map(&:"#{&1}_id") @@ -58,6 +58,7 @@ defmodule GroupherServer.CMS.ArticleComment do field(:is_deleted, :boolean, default: false) # 楼层 field(:floor, :integer, default: 0) + field(:is_for_question, :boolean, default: false) # 是否是评论文章的作者 field(:is_article_author, :boolean, default: false) diff --git a/lib/groupher_server/cms/cms.ex b/lib/groupher_server/cms/cms.ex index ca452f48b..4467a10ca 100644 --- a/lib/groupher_server/cms/cms.ex +++ b/lib/groupher_server/cms/cms.ex @@ -141,6 +141,8 @@ defmodule GroupherServer.CMS do defdelegate create_article_comment(thread, article_id, args, user), to: ArticleComment defdelegate update_article_comment(comment, content), to: ArticleComment defdelegate delete_article_comment(comment), to: ArticleComment + defdelegate mark_comment_solution(comment, user), to: ArticleComment + defdelegate undo_mark_comment_solution(comment, user), to: ArticleComment defdelegate upvote_article_comment(comment_id, user), to: ArticleCommentAction defdelegate undo_upvote_article_comment(comment_id, user), to: ArticleCommentAction diff --git a/lib/groupher_server/cms/delegates/article_comment.ex b/lib/groupher_server/cms/delegates/article_comment.ex index b4431100a..892e0558e 100644 --- a/lib/groupher_server/cms/delegates/article_comment.ex +++ b/lib/groupher_server/cms/delegates/article_comment.ex @@ -3,7 +3,7 @@ defmodule GroupherServer.CMS.Delegate.ArticleComment do CURD and operations for article comments """ import Ecto.Query, warn: false - import Helper.Utils, only: [done: 1, ensure: 2] + import Helper.Utils, only: [done: 1, ensure: 2, get_config: 2] import Helper.ErrorCode import GroupherServer.CMS.Delegate.Helper, only: [mark_viewer_emotion_states: 3] @@ -18,6 +18,8 @@ defmodule GroupherServer.CMS.Delegate.ArticleComment do alias CMS.{ArticleComment, ArticlePinnedComment, Embeds} alias Ecto.Multi + @article_threads get_config(:article, :threads) + @max_participator_count ArticleComment.max_participator_count() @default_emotions Embeds.ArticleCommentEmotion.default_emotions() @delete_hint ArticleComment.delete_hint() @@ -102,10 +104,8 @@ defmodule GroupherServer.CMS.Delegate.ArticleComment do |> Multi.run(:update_article_comments_count, fn _, %{create_article_comment: comment} -> update_article_comments_count(comment, :inc) end) - |> Multi.run(:set_question_flag_ifneed, fn _, _ -> - IO.inspect(article.meta, label: "hello now?") - # ORM.update(article, %{active_at: article.inserted_at}) - {:ok, :pass} + |> Multi.run(:set_question_flag_ifneed, fn _, %{create_article_comment: comment} -> + set_question_flag_ifneed(article, comment) end) |> Multi.run(:add_participator, fn _, _ -> add_participator_to_article(article, user) @@ -139,6 +139,50 @@ defmodule GroupherServer.CMS.Delegate.ArticleComment do article_comment |> ORM.update(%{body_html: content}) end + def mark_comment_solution(article_comment_id, user) do + do_mark_comment_solution(article_comment_id, user, true) + end + + def undo_mark_comment_solution(article_comment_id, user) do + do_mark_comment_solution(article_comment_id, user, false) + end + + defp do_mark_comment_solution(article_comment_id, user, is_solution) do + with {:ok, article_comment} <- ORM.find(ArticleComment, article_comment_id), + {:ok, post} <- ORM.find(CMS.Post, article_comment.post_id, preload: [author: :user]), + # check if user is questioner + true <- user.id == post.author.user.id do + Multi.new() + |> Multi.run(:mark_solution, fn _, _ -> + meta = article_comment.meta |> Map.merge(%{is_solution: is_solution}) + ORM.update_meta(article_comment, meta) + end) + |> Multi.run(:update_post_state, fn _, _ -> + ORM.update(post, %{is_solved: is_solution, solution_digest: article_comment.body_html}) + end) + |> Repo.transaction() + |> result() + else + false -> raise_error(:require_questioner, "oops, questioner only") + {:error, error} -> {:error, error} + end + end + + @doc """ + batch update is_question flag for post-only article + """ + def batch_update_question_flag(%CMS.Post{is_question: is_question} = post) do + from(c in ArticleComment, + where: c.post_id == ^post.id, + update: [set: [is_for_question: ^is_question]] + ) + |> Repo.update_all([]) + + {:ok, :pass} + end + + def batch_update_question_flag(_), do: {:ok, :pass} + @doc "delete article comment" def delete_article_comment(%ArticleComment{} = comment) do Multi.new() @@ -230,7 +274,6 @@ defmodule GroupherServer.CMS.Delegate.ArticleComment do query |> where(^thread_query) |> where(^where_query) - # |> QueryBuilder.filter_pack(Map.merge(filters, %{sort: :asc_inserted})) |> QueryBuilder.filter_pack(Map.merge(filters, %{sort: sort})) |> ORM.paginater(~m(page size)a) |> add_pined_comments_ifneed(thread, article_id, filters) @@ -299,8 +342,15 @@ defmodule GroupherServer.CMS.Delegate.ArticleComment do Map.merge(paged_comments, %{entries: entries}) end - defp result({:ok, %{create_article_comment: result}}), do: {:ok, result} + defp set_question_flag_ifneed(%{is_question: true} = article, %ArticleComment{} = comment) do + ORM.update(comment, %{is_for_question: true}) + end + + defp set_question_flag_ifneed(_, comment), do: ORM.update(comment, %{is_for_question: false}) + + defp result({:ok, %{set_question_flag_ifneed: result}}), do: {:ok, result} defp result({:ok, %{delete_article_comment: result}}), do: {:ok, result} + defp result({:ok, %{mark_solution: result}}), do: {:ok, result} defp result({:error, :create_article_comment, result, _steps}) do raise_error(:create_comment, result) diff --git a/lib/groupher_server/cms/delegates/article_community.ex b/lib/groupher_server/cms/delegates/article_community.ex index 0cd8f9ab1..69dd1c0e7 100644 --- a/lib/groupher_server/cms/delegates/article_community.ex +++ b/lib/groupher_server/cms/delegates/article_community.ex @@ -17,7 +17,6 @@ defmodule GroupherServer.CMS.Delegate.ArticleCommunity do alias Ecto.Multi - @default_article_meta Embeds.ArticleMeta.default_meta() @max_pinned_article_count_per_thread Community.max_pinned_article_count_per_thread() @spec pin_article(T.article_thread(), Integer.t(), Integer.t()) :: {:ok, PinnedArticle.t()} diff --git a/lib/groupher_server/cms/delegates/article_curd.ex b/lib/groupher_server/cms/delegates/article_curd.ex index 71f791ddf..8beb17152 100644 --- a/lib/groupher_server/cms/delegates/article_curd.ex +++ b/lib/groupher_server/cms/delegates/article_curd.ex @@ -27,7 +27,7 @@ defmodule GroupherServer.CMS.Delegate.ArticleCURD do alias Accounts.User alias CMS.{Author, Community, PinnedArticle, Embeds, Delegate} - alias Delegate.{ArticleCommunity, ArticleTag, CommunityCURD} + alias Delegate.{ArticleCommunity, ArticleComment, ArticleTag, CommunityCURD} alias Ecto.Multi @@ -214,8 +214,13 @@ defmodule GroupherServer.CMS.Delegate.ArticleCURD do """ def update_article(article, args) do Multi.new() - |> Multi.run(:update_article, fn _, _ -> - ORM.update(article, args) + |> Multi.run(:update_article, fn _, _ -> ORM.update(article, args) end) + |> Multi.run(:update_comment_question_flag_if_need, fn _, %{update_article: update_article} -> + # 如果帖子的类型变了,那么 update 所有的 flag + case Map.has_key?(args, :is_question) do + true -> ArticleComment.batch_update_question_flag(update_article) + false -> {:ok, :pass} + end end) |> Multi.run(:update_edit_status, fn _, %{update_article: update_article} -> ArticleCommunity.update_edit_status(update_article) diff --git a/lib/groupher_server/cms/embeds/article_comment_meta.ex b/lib/groupher_server/cms/embeds/article_comment_meta.ex index 941f5e84e..eaf20c3f8 100644 --- a/lib/groupher_server/cms/embeds/article_comment_meta.ex +++ b/lib/groupher_server/cms/embeds/article_comment_meta.ex @@ -7,7 +7,7 @@ defmodule GroupherServer.CMS.Embeds.ArticleCommentMeta do import Ecto.Changeset - @optional_fields ~w(is_article_author_upvoted report_count is_reply_to_others reported_count reported_user_ids is_solution is_for_question)a + @optional_fields ~w(is_article_author_upvoted report_count is_reply_to_others reported_count reported_user_ids is_solution)a @doc "for test usage" def default_meta() do @@ -18,8 +18,7 @@ defmodule GroupherServer.CMS.Embeds.ArticleCommentMeta do upvoted_user_ids: [], reported_user_ids: [], reported_count: 0, - is_solution: false, - is_for_question: false + is_solution: false } end @@ -35,7 +34,6 @@ defmodule GroupherServer.CMS.Embeds.ArticleCommentMeta do field(:reported_count, :integer, default: 0) field(:is_solution, :boolean, default: false) - field(:is_for_question, :boolean, default: false) end def changeset(struct, params) do diff --git a/lib/groupher_server/cms/post.ex b/lib/groupher_server/cms/post.ex index a257b1503..ea9125a2d 100644 --- a/lib/groupher_server/cms/post.ex +++ b/lib/groupher_server/cms/post.ex @@ -17,7 +17,7 @@ defmodule GroupherServer.CMS.Post do @required_fields ~w(title body digest length)a @article_cast_fields general_article_fields(:cast) - @optional_fields ~w(link_addr copy_right link_addr link_icon is_question is_solved)a ++ + @optional_fields ~w(link_addr copy_right link_addr link_icon is_question is_solved solution_digest)a ++ @article_cast_fields @type t :: %Post{} @@ -32,6 +32,7 @@ defmodule GroupherServer.CMS.Post do field(:is_question, :boolean, default: false) field(:is_solved, :boolean, default: false) + field(:solution_digest, :string) # TODO: remove after legacy data migrated has_many(:comments, {"posts_comments", PostComment}) diff --git a/lib/helper/error_code.ex b/lib/helper/error_code.ex index 6e15d28c5..897cf819f 100644 --- a/lib/helper/error_code.ex +++ b/lib/helper/error_code.ex @@ -50,6 +50,7 @@ defmodule Helper.ErrorCode do def ecode(:invalid_domain_tag), do: @article_base + 6 def ecode(:undo_sink_old_article), do: @article_base + 7 def ecode(:article_comment_locked), do: @article_base + 8 + def ecode(:require_questioner), do: @article_base + 9 def ecode, do: @default_base # def ecode(_), do: @default_base diff --git a/priv/repo/migrations/20210602111315_add_qustion_mark_to_article_comment.exs b/priv/repo/migrations/20210602111315_add_qustion_mark_to_article_comment.exs new file mode 100644 index 000000000..af3bd96c7 --- /dev/null +++ b/priv/repo/migrations/20210602111315_add_qustion_mark_to_article_comment.exs @@ -0,0 +1,9 @@ +defmodule GroupherServer.Repo.Migrations.AddQustionMarkToArticleComment do + use Ecto.Migration + + def change do + alter table(:articles_comments) do + add(:is_for_question, :boolean, default: false) + end + end +end diff --git a/priv/repo/migrations/20210602135346_add_solution_digest_to_post.exs b/priv/repo/migrations/20210602135346_add_solution_digest_to_post.exs new file mode 100644 index 000000000..c59ec48bf --- /dev/null +++ b/priv/repo/migrations/20210602135346_add_solution_digest_to_post.exs @@ -0,0 +1,9 @@ +defmodule GroupherServer.Repo.Migrations.AddSolutionDigestToPost do + use Ecto.Migration + + def change do + alter table(:cms_posts) do + add(:solution_digest, :string) + end + end +end diff --git a/test/groupher_server/cms/articles/post_test.exs b/test/groupher_server/cms/articles/post_test.exs index 28374f02b..978b687df 100644 --- a/test/groupher_server/cms/articles/post_test.exs +++ b/test/groupher_server/cms/articles/post_test.exs @@ -148,7 +148,7 @@ defmodule GroupherServer.Test.CMS.Articles.Post do end describe "[cms post question]" do - @tag :wip + @tag :wip2 test "post have default question flags", ~m(user community post_attrs)a do {:ok, post} = CMS.create_article(community, :post, post_attrs, user) @@ -156,7 +156,7 @@ defmodule GroupherServer.Test.CMS.Articles.Post do assert not post.is_solved end - @tag :wip + @tag :wip2 test "can create post with question", ~m(user community post_attrs)a do post_attrs = Map.merge(post_attrs, %{is_question: true}) {:ok, post} = CMS.create_article(community, :post, post_attrs, user) @@ -164,7 +164,7 @@ defmodule GroupherServer.Test.CMS.Articles.Post do assert post.is_question end - @tag :wip + @tag :wip2 test "can update post with question", ~m(user community post_attrs)a do post_attrs = Map.merge(post_attrs, %{is_question: true}) {:ok, post} = CMS.create_article(community, :post, post_attrs, user) diff --git a/test/groupher_server/cms/comments/post_comment_test.exs b/test/groupher_server/cms/comments/post_comment_test.exs index 4335e5c4b..63d0aa06a 100644 --- a/test/groupher_server/cms/comments/post_comment_test.exs +++ b/test/groupher_server/cms/comments/post_comment_test.exs @@ -668,4 +668,116 @@ defmodule GroupherServer.Test.CMS.Comments.PostComment do {:ok, _} = CMS.reply_article_comment(parent_comment.id, "reply_content", user) end end + + describe "[article comment qa type]" do + @tag :wip2 + test "create comment for normal post should have default qa flags", ~m(user community)a do + post_attrs = mock_attrs(:post, %{community_id: community.id}) + {:ok, post} = CMS.create_article(community, :post, post_attrs, user) + {:ok, post_comment} = CMS.create_article_comment(:post, post.id, "comment", user) + + assert not post_comment.is_for_question + assert not post_comment.meta.is_solution + end + + @tag :wip2 + test "create comment for question post should have flags", ~m(user community)a do + post_attrs = mock_attrs(:post, %{community_id: community.id, is_question: true}) + {:ok, post} = CMS.create_article(community, :post, post_attrs, user) + + {:ok, post_comment} = CMS.create_article_comment(:post, post.id, "comment", user) + + assert post_comment.is_for_question + end + + @tag :wip2 + test "update comment with is_question should batch update exsit comments is_for_question field", + ~m(user community)a do + post_attrs = mock_attrs(:post, %{community_id: community.id, is_question: true}) + {:ok, post} = CMS.create_article(community, :post, post_attrs, user) + {:ok, comment1} = CMS.create_article_comment(:post, post.id, "comment", user) + {:ok, comment2} = CMS.create_article_comment(:post, post.id, "comment", user) + {:ok, comment3} = CMS.create_article_comment(:post, post.id, "comment", user) + + assert comment1.is_for_question + assert comment2.is_for_question + assert comment3.is_for_question + + {:ok, _} = CMS.update_article(post, %{is_question: false}) + + {:ok, comment1} = ORM.find(ArticleComment, comment1.id) + {:ok, comment2} = ORM.find(ArticleComment, comment2.id) + {:ok, comment3} = ORM.find(ArticleComment, comment3.id) + + assert not comment1.is_for_question + assert not comment2.is_for_question + assert not comment3.is_for_question + end + + @tag :wip + test "can mark a comment as solution", ~m(user community)a do + post_attrs = mock_attrs(:post, %{community_id: community.id, is_question: true}) + {:ok, post} = CMS.create_article(community, :post, post_attrs, user) + + {:ok, post} = ORM.find(Post, post.id, preload: [author: :user]) + post_author = post.author.user + + {:ok, comment} = CMS.create_article_comment(:post, post.id, "comment", post_author) + {:ok, comment} = CMS.mark_comment_solution(comment.id, post_author) + + assert comment.meta.is_solution + + {:ok, post} = ORM.find(Post, post.id) + assert post.is_solved + assert post.solution_digest == comment.body_html + end + + @tag :wip + test "non-post-author can not mark a comment as solution", ~m(user community)a do + post_attrs = mock_attrs(:post, %{community_id: community.id, is_question: true}) + {:ok, post} = CMS.create_article(community, :post, post_attrs, user) + + {:ok, post} = ORM.find(Post, post.id, preload: [author: :user]) + post_author = post.author.user + {:ok, random_user} = db_insert(:user) + + {:ok, comment} = CMS.create_article_comment(:post, post.id, "comment", post_author) + {:error, reason} = CMS.mark_comment_solution(comment.id, random_user) + + reason |> is_error?(:require_questioner) + end + + @tag :wip + test "can undo mark a comment as solution", ~m(user community)a do + post_attrs = mock_attrs(:post, %{community_id: community.id, is_question: true}) + {:ok, post} = CMS.create_article(community, :post, post_attrs, user) + {:ok, post} = ORM.find(Post, post.id, preload: [author: :user]) + post_author = post.author.user + + {:ok, comment} = CMS.create_article_comment(:post, post.id, "comment", post_author) + {:ok, comment} = CMS.mark_comment_solution(comment.id, post_author) + + {:ok, comment} = CMS.undo_mark_comment_solution(comment.id, post_author) + + assert not comment.meta.is_solution + + {:ok, post} = ORM.find(Post, post.id) + assert not post.is_solved + end + + @tag :wip + test "non-post-author can not undo mark a comment as solution", ~m(user community)a do + post_attrs = mock_attrs(:post, %{community_id: community.id, is_question: true}) + {:ok, post} = CMS.create_article(community, :post, post_attrs, user) + + {:ok, post} = ORM.find(Post, post.id, preload: [author: :user]) + post_author = post.author.user + {:ok, random_user} = db_insert(:user) + + {:ok, comment} = CMS.create_article_comment(:post, post.id, "comment", post_author) + {:error, reason} = CMS.undo_mark_comment_solution(comment.id, random_user) + + reason |> is_error?(:require_questioner) + end + end end diff --git a/test/support/factory.ex b/test/support/factory.ex index 71fbee81d..2014004cc 100644 --- a/test/support/factory.ex +++ b/test/support/factory.ex @@ -21,6 +21,7 @@ defmodule GroupherServer.Support.Factory do title: String.slice(body, 1, 49), body: body, digest: String.slice(body, 1, 150), + solution_digest: String.slice(body, 1, 150), length: String.length(body), author: mock(:author), views: Enum.random(0..2000), From 9f9910940bece7c30903643f3c7fb07748ed42a3 Mon Sep 17 00:00:00 2001 From: mydearxym Date: Wed, 2 Jun 2021 22:55:19 +0800 Subject: [PATCH 3/8] feat(post-qa): only allows one best solution --- lib/groupher_server/cms/article_comment.ex | 6 ++-- .../cms/delegates/article_comment.ex | 34 ++++++++++++++----- .../cms/embeds/article_comment_meta.ex | 7 ++-- lib/helper/error_code.ex | 1 + ...2144450_add_solution_digest_to_comment.exs | 9 +++++ .../cms/comments/post_comment_test.exs | 30 +++++++++++++--- 6 files changed, 67 insertions(+), 20 deletions(-) create mode 100644 priv/repo/migrations/20210602144450_add_solution_digest_to_comment.exs diff --git a/lib/groupher_server/cms/article_comment.ex b/lib/groupher_server/cms/article_comment.ex index 5999fc3b1..5b68d6528 100644 --- a/lib/groupher_server/cms/article_comment.ex +++ b/lib/groupher_server/cms/article_comment.ex @@ -17,8 +17,8 @@ defmodule GroupherServer.CMS.ArticleComment do @article_threads get_config(:article, :threads) @required_fields ~w(body_html author_id)a - @optional_fields ~w(reply_to_id replies_count is_folded is_deleted floor is_article_author thread is_for_question)a - @updatable_fields ~w(is_folded is_deleted floor upvotes_count is_pinned is_for_question)a + @optional_fields ~w(reply_to_id replies_count is_folded is_deleted floor is_article_author thread is_for_question is_solution)a + @updatable_fields ~w(is_folded is_deleted floor upvotes_count is_pinned is_for_question is_solution)a @article_fields @article_threads |> Enum.map(&:"#{&1}_id") @@ -58,7 +58,9 @@ defmodule GroupherServer.CMS.ArticleComment do field(:is_deleted, :boolean, default: false) # 楼层 field(:floor, :integer, default: 0) + field(:is_for_question, :boolean, default: false) + field(:is_solution, :boolean, default: false) # 是否是评论文章的作者 field(:is_article_author, :boolean, default: false) diff --git a/lib/groupher_server/cms/delegates/article_comment.ex b/lib/groupher_server/cms/delegates/article_comment.ex index 892e0558e..c8dfb402d 100644 --- a/lib/groupher_server/cms/delegates/article_comment.ex +++ b/lib/groupher_server/cms/delegates/article_comment.ex @@ -140,22 +140,27 @@ defmodule GroupherServer.CMS.Delegate.ArticleComment do end def mark_comment_solution(article_comment_id, user) do - do_mark_comment_solution(article_comment_id, user, true) + with {:ok, article_comment} <- ORM.find(ArticleComment, article_comment_id), + {:ok, post} <- ORM.find(CMS.Post, article_comment.post_id, preload: [author: :user]) do + # 确保只有一个最佳答案 + batch_update_solution_flag(post, false) + do_mark_comment_solution(post, article_comment, user, true) + end end def undo_mark_comment_solution(article_comment_id, user) do - do_mark_comment_solution(article_comment_id, user, false) + with {:ok, article_comment} <- ORM.find(ArticleComment, article_comment_id), + {:ok, post} <- ORM.find(CMS.Post, article_comment.post_id, preload: [author: :user]) do + do_mark_comment_solution(post, article_comment, user, false) + end end - defp do_mark_comment_solution(article_comment_id, user, is_solution) do - with {:ok, article_comment} <- ORM.find(ArticleComment, article_comment_id), - {:ok, post} <- ORM.find(CMS.Post, article_comment.post_id, preload: [author: :user]), - # check if user is questioner - true <- user.id == post.author.user.id do + defp do_mark_comment_solution(post, %ArticleComment{} = article_comment, user, is_solution) do + # check if user is questioner + with true <- user.id == post.author.user.id do Multi.new() |> Multi.run(:mark_solution, fn _, _ -> - meta = article_comment.meta |> Map.merge(%{is_solution: is_solution}) - ORM.update_meta(article_comment, meta) + ORM.update(article_comment, %{is_solution: is_solution}) end) |> Multi.run(:update_post_state, fn _, _ -> ORM.update(post, %{is_solved: is_solution, solution_digest: article_comment.body_html}) @@ -348,6 +353,17 @@ defmodule GroupherServer.CMS.Delegate.ArticleComment do defp set_question_flag_ifneed(_, comment), do: ORM.update(comment, %{is_for_question: false}) + # batch update is_solution flag for artilce comment + defp batch_update_solution_flag(%CMS.Post{} = post, is_question) do + from(c in ArticleComment, + where: c.post_id == ^post.id, + update: [set: [is_solution: ^is_question]] + ) + |> Repo.update_all([]) + + {:ok, :pass} + end + defp result({:ok, %{set_question_flag_ifneed: result}}), do: {:ok, result} defp result({:ok, %{delete_article_comment: result}}), do: {:ok, result} defp result({:ok, %{mark_solution: result}}), do: {:ok, result} diff --git a/lib/groupher_server/cms/embeds/article_comment_meta.ex b/lib/groupher_server/cms/embeds/article_comment_meta.ex index eaf20c3f8..b05eb591f 100644 --- a/lib/groupher_server/cms/embeds/article_comment_meta.ex +++ b/lib/groupher_server/cms/embeds/article_comment_meta.ex @@ -7,7 +7,7 @@ defmodule GroupherServer.CMS.Embeds.ArticleCommentMeta do import Ecto.Changeset - @optional_fields ~w(is_article_author_upvoted report_count is_reply_to_others reported_count reported_user_ids is_solution)a + @optional_fields ~w(is_article_author_upvoted report_count is_reply_to_others reported_count reported_user_ids)a @doc "for test usage" def default_meta() do @@ -17,8 +17,7 @@ defmodule GroupherServer.CMS.Embeds.ArticleCommentMeta do report_count: 0, upvoted_user_ids: [], reported_user_ids: [], - reported_count: 0, - is_solution: false + reported_count: 0 } end @@ -32,8 +31,6 @@ defmodule GroupherServer.CMS.Embeds.ArticleCommentMeta do field(:upvoted_user_ids, {:array, :integer}, default: []) field(:reported_user_ids, {:array, :integer}, default: []) field(:reported_count, :integer, default: 0) - - field(:is_solution, :boolean, default: false) end def changeset(struct, params) do diff --git a/lib/helper/error_code.ex b/lib/helper/error_code.ex index 897cf819f..64660bae0 100644 --- a/lib/helper/error_code.ex +++ b/lib/helper/error_code.ex @@ -51,6 +51,7 @@ defmodule Helper.ErrorCode do def ecode(:undo_sink_old_article), do: @article_base + 7 def ecode(:article_comment_locked), do: @article_base + 8 def ecode(:require_questioner), do: @article_base + 9 + # def ecode(:already_solved), do: @article_base + 10 def ecode, do: @default_base # def ecode(_), do: @default_base diff --git a/priv/repo/migrations/20210602144450_add_solution_digest_to_comment.exs b/priv/repo/migrations/20210602144450_add_solution_digest_to_comment.exs new file mode 100644 index 000000000..a30f2aa07 --- /dev/null +++ b/priv/repo/migrations/20210602144450_add_solution_digest_to_comment.exs @@ -0,0 +1,9 @@ +defmodule GroupherServer.Repo.Migrations.AddSolutionDigestToComment do + use Ecto.Migration + + def change do + alter table(:articles_comments) do + add(:is_solution, :boolean, default: false) + end + end +end diff --git a/test/groupher_server/cms/comments/post_comment_test.exs b/test/groupher_server/cms/comments/post_comment_test.exs index 63d0aa06a..cd6e7937d 100644 --- a/test/groupher_server/cms/comments/post_comment_test.exs +++ b/test/groupher_server/cms/comments/post_comment_test.exs @@ -5,7 +5,7 @@ defmodule GroupherServer.Test.CMS.Comments.PostComment do import Helper.Utils, only: [get_config: 2] alias Helper.ORM - alias GroupherServer.{Accounts, CMS} + alias GroupherServer.{Accounts, CMS, Repo} alias CMS.{ArticleComment, ArticlePinnedComment, Embeds, Post} @@ -677,7 +677,7 @@ defmodule GroupherServer.Test.CMS.Comments.PostComment do {:ok, post_comment} = CMS.create_article_comment(:post, post.id, "comment", user) assert not post_comment.is_for_question - assert not post_comment.meta.is_solution + assert not post_comment.is_solution end @tag :wip2 @@ -725,7 +725,7 @@ defmodule GroupherServer.Test.CMS.Comments.PostComment do {:ok, comment} = CMS.create_article_comment(:post, post.id, "comment", post_author) {:ok, comment} = CMS.mark_comment_solution(comment.id, post_author) - assert comment.meta.is_solution + assert comment.is_solution {:ok, post} = ORM.find(Post, post.id) assert post.is_solved @@ -759,7 +759,7 @@ defmodule GroupherServer.Test.CMS.Comments.PostComment do {:ok, comment} = CMS.undo_mark_comment_solution(comment.id, post_author) - assert not comment.meta.is_solution + assert not comment.is_solution {:ok, post} = ORM.find(Post, post.id) assert not post.is_solved @@ -779,5 +779,27 @@ defmodule GroupherServer.Test.CMS.Comments.PostComment do reason |> is_error?(:require_questioner) end + + @tag :wip + test "can only mark one best comment as solution", ~m(user community)a do + post_attrs = mock_attrs(:post, %{community_id: community.id, is_question: true}) + {:ok, post} = CMS.create_article(community, :post, post_attrs, user) + + {:ok, post} = ORM.find(Post, post.id, preload: [author: :user]) + post_author = post.author.user + + {:ok, comment1} = CMS.create_article_comment(:post, post.id, "comment", post_author) + {:ok, comment2} = CMS.create_article_comment(:post, post.id, "comment", post_author) + + {:ok, _comment} = CMS.mark_comment_solution(comment1.id, post_author) + {:ok, comment2} = CMS.mark_comment_solution(comment2.id, post_author) + + answers = + from(c in ArticleComment, where: c.post_id == ^post.id and c.is_solution == true) + |> Repo.all() + + assert answers |> length == 1 + assert answers |> List.first() |> Map.get(:id) == comment2.id + end end end From cea7bcd816b0a1710714b437448fde7f66f6a3a7 Mon Sep 17 00:00:00 2001 From: mydearxym Date: Thu, 3 Jun 2021 00:50:03 +0800 Subject: [PATCH 4/8] feat(post-qa): pin solution logic --- .../cms/delegates/article_comment.ex | 24 +++++++-- .../cms/comments/post_comment_test.exs | 10 ++-- .../query/cms/comments/job_comment_test.exs | 7 ++- .../query/cms/comments/post_comment_test.exs | 49 +++++++++++++++++-- .../query/cms/comments/repo_comment_test.exs | 7 ++- 5 files changed, 81 insertions(+), 16 deletions(-) diff --git a/lib/groupher_server/cms/delegates/article_comment.ex b/lib/groupher_server/cms/delegates/article_comment.ex index c8dfb402d..603e883db 100644 --- a/lib/groupher_server/cms/delegates/article_comment.ex +++ b/lib/groupher_server/cms/delegates/article_comment.ex @@ -107,9 +107,7 @@ defmodule GroupherServer.CMS.Delegate.ArticleComment do |> Multi.run(:set_question_flag_ifneed, fn _, %{create_article_comment: comment} -> set_question_flag_ifneed(article, comment) end) - |> Multi.run(:add_participator, fn _, _ -> - add_participator_to_article(article, user) - end) + |> Multi.run(:add_participator, fn _, _ -> add_participator_to_article(article, user) end) |> Multi.run(:update_article_active_timestamp, fn _, %{create_article_comment: comment} -> case comment.author_id == article.author.user.id do true -> {:ok, :pass} @@ -144,6 +142,7 @@ defmodule GroupherServer.CMS.Delegate.ArticleComment do {:ok, post} <- ORM.find(CMS.Post, article_comment.post_id, preload: [author: :user]) do # 确保只有一个最佳答案 batch_update_solution_flag(post, false) + CMS.pin_article_comment(article_comment.id) do_mark_comment_solution(post, article_comment, user, true) end end @@ -209,7 +208,9 @@ defmodule GroupherServer.CMS.Delegate.ArticleComment do %{article_comments_participators: article_comments_participators} = article, %User{} = user ) do - total_participators = article_comments_participators |> List.insert_at(0, user) |> Enum.uniq() + total_participators = + article_comments_participators |> List.insert_at(0, user) |> Enum.uniq_by(& &1.id) + new_comment_participators = total_participators |> Enum.slice(0, @max_participator_count) total_participators_count = length(total_participators) @@ -312,6 +313,7 @@ defmodule GroupherServer.CMS.Delegate.ArticleComment do join: c in ArticleComment, on: p.article_comment_id == c.id, where: field(p, ^info.foreign_key) == ^article_id, + order_by: [desc: p.inserted_at], select: c ), {:ok, pined_comments} <- Repo.all(query) |> done() do @@ -323,6 +325,7 @@ defmodule GroupherServer.CMS.Delegate.ArticleComment do preloaded_pined_comments = Enum.slice(pined_comments, 0, @pinned_comment_limit) |> Repo.preload(reply_to: :author) + |> sort_solution_to_front entries = Enum.concat(preloaded_pined_comments, entries) pined_comment_count = length(pined_comments) @@ -335,6 +338,19 @@ defmodule GroupherServer.CMS.Delegate.ArticleComment do defp add_pined_comments_ifneed(paged_comments, _thread, _article_id, _), do: paged_comments + defp sort_solution_to_front(pined_comments) do + solution_index = Enum.find_index(pined_comments, & &1.is_solution) + + case is_nil(solution_index) do + true -> + pined_comments + + false -> + {solution_comment, rest_comments} = List.pop_at(pined_comments, solution_index) + [solution_comment] ++ rest_comments + end + end + defp mark_viewer_has_upvoted(paged_comments, nil), do: paged_comments defp mark_viewer_has_upvoted(%{entries: entries} = paged_comments, %User{} = user) do diff --git a/test/groupher_server/cms/comments/post_comment_test.exs b/test/groupher_server/cms/comments/post_comment_test.exs index cd6e7937d..44cd3cd84 100644 --- a/test/groupher_server/cms/comments/post_comment_test.exs +++ b/test/groupher_server/cms/comments/post_comment_test.exs @@ -714,7 +714,7 @@ defmodule GroupherServer.Test.CMS.Comments.PostComment do assert not comment3.is_for_question end - @tag :wip + @tag :wip2 test "can mark a comment as solution", ~m(user community)a do post_attrs = mock_attrs(:post, %{community_id: community.id, is_question: true}) {:ok, post} = CMS.create_article(community, :post, post_attrs, user) @@ -732,7 +732,7 @@ defmodule GroupherServer.Test.CMS.Comments.PostComment do assert post.solution_digest == comment.body_html end - @tag :wip + @tag :wip2 test "non-post-author can not mark a comment as solution", ~m(user community)a do post_attrs = mock_attrs(:post, %{community_id: community.id, is_question: true}) {:ok, post} = CMS.create_article(community, :post, post_attrs, user) @@ -747,7 +747,7 @@ defmodule GroupherServer.Test.CMS.Comments.PostComment do reason |> is_error?(:require_questioner) end - @tag :wip + @tag :wip2 test "can undo mark a comment as solution", ~m(user community)a do post_attrs = mock_attrs(:post, %{community_id: community.id, is_question: true}) {:ok, post} = CMS.create_article(community, :post, post_attrs, user) @@ -765,7 +765,7 @@ defmodule GroupherServer.Test.CMS.Comments.PostComment do assert not post.is_solved end - @tag :wip + @tag :wip2 test "non-post-author can not undo mark a comment as solution", ~m(user community)a do post_attrs = mock_attrs(:post, %{community_id: community.id, is_question: true}) {:ok, post} = CMS.create_article(community, :post, post_attrs, user) @@ -780,7 +780,7 @@ defmodule GroupherServer.Test.CMS.Comments.PostComment do reason |> is_error?(:require_questioner) end - @tag :wip + @tag :wip2 test "can only mark one best comment as solution", ~m(user community)a do post_attrs = mock_attrs(:post, %{community_id: community.id, is_question: true}) {:ok, post} = CMS.create_article(community, :post, post_attrs, user) diff --git a/test/groupher_server_web/query/cms/comments/job_comment_test.exs b/test/groupher_server_web/query/cms/comments/job_comment_test.exs index 97b06913d..9bf57971b 100644 --- a/test/groupher_server_web/query/cms/comments/job_comment_test.exs +++ b/test/groupher_server_web/query/cms/comments/job_comment_test.exs @@ -240,6 +240,7 @@ defmodule GroupherServer.Test.Query.Comments.JobComment do assert results["totalCount"] == total_count end + @tag :wip test "guest user can get paged comment with pinned comment in it", ~m(guest_conn job user)a do total_count = 20 @@ -254,14 +255,16 @@ defmodule GroupherServer.Test.Query.Comments.JobComment do {:ok, comment} = CMS.create_article_comment(thread, job.id, "pinned comment", user) {:ok, pinned_comment} = CMS.pin_article_comment(comment.id) + Process.sleep(1000) + {:ok, comment} = CMS.create_article_comment(thread, job.id, "pinned comment 2", user) {:ok, pinned_comment2} = CMS.pin_article_comment(comment.id) variables = %{id: job.id, thread: "JOB", filter: %{page: 1, size: 10}} results = guest_conn |> query_result(@query, variables, "pagedArticleComments") - assert results["entries"] |> List.first() |> Map.get("id") == to_string(pinned_comment.id) - assert results["entries"] |> Enum.at(1) |> Map.get("id") == to_string(pinned_comment2.id) + assert results["entries"] |> List.first() |> Map.get("id") == to_string(pinned_comment2.id) + assert results["entries"] |> Enum.at(1) |> Map.get("id") == to_string(pinned_comment.id) assert results["totalCount"] == total_count + 2 end diff --git a/test/groupher_server_web/query/cms/comments/post_comment_test.exs b/test/groupher_server_web/query/cms/comments/post_comment_test.exs index 076f3157e..7856805f3 100644 --- a/test/groupher_server_web/query/cms/comments/post_comment_test.exs +++ b/test/groupher_server_web/query/cms/comments/post_comment_test.exs @@ -4,16 +4,18 @@ defmodule GroupherServer.Test.Query.Comments.PostComment do use GroupherServer.TestTools alias GroupherServer.CMS + alias Helper.ORM setup do {:ok, post} = db_insert(:post) {:ok, user} = db_insert(:user) {:ok, user2} = db_insert(:user) + {:ok, community} = db_insert(:community) guest_conn = simu_conn(:guest) user_conn = simu_conn(:user, user) - {:ok, ~m(user_conn guest_conn post user user2)a} + {:ok, ~m(user_conn guest_conn community post user user2)a} end describe "[baisc article post comment]" do @@ -240,6 +242,7 @@ defmodule GroupherServer.Test.Query.Comments.PostComment do assert results["totalCount"] == total_count end + @tag :wip test "guest user can get paged comment with pinned comment in it", ~m(guest_conn post user)a do total_count = 20 @@ -254,18 +257,58 @@ defmodule GroupherServer.Test.Query.Comments.PostComment do {:ok, comment} = CMS.create_article_comment(thread, post.id, "pinned comment", user) {:ok, pinned_comment} = CMS.pin_article_comment(comment.id) + Process.sleep(1000) + {:ok, comment} = CMS.create_article_comment(thread, post.id, "pinned comment 2", user) {:ok, pinned_comment2} = CMS.pin_article_comment(comment.id) variables = %{id: post.id, thread: "POST", filter: %{page: 1, size: 10}} results = guest_conn |> query_result(@query, variables, "pagedArticleComments") - assert results["entries"] |> List.first() |> Map.get("id") == to_string(pinned_comment.id) - assert results["entries"] |> Enum.at(1) |> Map.get("id") == to_string(pinned_comment2.id) + assert results["entries"] |> List.first() |> Map.get("id") == to_string(pinned_comment2.id) + assert results["entries"] |> Enum.at(1) |> Map.get("id") == to_string(pinned_comment.id) assert results["totalCount"] == total_count + 2 end + # post only + @tag :wip + test "if solution in pinned comments, solution should always on top", + ~m(guest_conn community user)a do + post_attrs = mock_attrs(:post, %{community_id: community.id, is_question: true}) + {:ok, post} = CMS.create_article(community, :post, post_attrs, user) + + total_count = 20 + thread = :post + + Enum.reduce(1..total_count, [], fn _, acc -> + {:ok, comment} = CMS.create_article_comment(thread, post.id, "test comment", user) + + acc ++ [comment] + end) + + {:ok, post} = ORM.find(CMS.Post, post.id, preload: [author: :user]) + post_author = post.author.user + + {:ok, comment} = CMS.create_article_comment(thread, post.id, "pinned comment", user) + {:ok, pinned_comment} = CMS.pin_article_comment(comment.id) + + Process.sleep(1000) + + {:ok, comment} = CMS.create_article_comment(thread, post.id, "solution", post_author) + {:ok, solution_comment} = CMS.mark_comment_solution(comment.id, post_author) + + Process.sleep(1000) + {:ok, comment} = CMS.create_article_comment(thread, post.id, "pinned comment 2", user) + {:ok, pinned_comment2} = CMS.pin_article_comment(comment.id) + + variables = %{id: post.id, thread: "POST", filter: %{page: 1, size: 10}} + results = guest_conn |> query_result(@query, variables, "pagedArticleComments") + + assert results["entries"] |> List.first() |> Map.get("id") == to_string(solution_comment.id) + assert results["totalCount"] == total_count + 3 + end + test "guest user can get paged comment with floor it", ~m(guest_conn post user)a do total_count = 5 thread = :post diff --git a/test/groupher_server_web/query/cms/comments/repo_comment_test.exs b/test/groupher_server_web/query/cms/comments/repo_comment_test.exs index e0fa1099a..d8cdcd893 100644 --- a/test/groupher_server_web/query/cms/comments/repo_comment_test.exs +++ b/test/groupher_server_web/query/cms/comments/repo_comment_test.exs @@ -238,6 +238,7 @@ defmodule GroupherServer.Test.Query.Comments.RepoComment do assert results["totalCount"] == total_count end + @tag :wip test "guest user can get paged comment with pinned comment in it", ~m(guest_conn repo user)a do total_count = 20 @@ -252,14 +253,16 @@ defmodule GroupherServer.Test.Query.Comments.RepoComment do {:ok, comment} = CMS.create_article_comment(thread, repo.id, "pinned comment", user) {:ok, pinned_comment} = CMS.pin_article_comment(comment.id) + Process.sleep(1000) + {:ok, comment} = CMS.create_article_comment(thread, repo.id, "pinned comment 2", user) {:ok, pinned_comment2} = CMS.pin_article_comment(comment.id) variables = %{id: repo.id, thread: "REPO", filter: %{page: 1, size: 10}} results = guest_conn |> query_result(@query, variables, "pagedArticleComments") - assert results["entries"] |> List.first() |> Map.get("id") == to_string(pinned_comment.id) - assert results["entries"] |> Enum.at(1) |> Map.get("id") == to_string(pinned_comment2.id) + assert results["entries"] |> List.first() |> Map.get("id") == to_string(pinned_comment2.id) + assert results["entries"] |> Enum.at(1) |> Map.get("id") == to_string(pinned_comment.id) assert results["totalCount"] == total_count + 2 end From a100e251d2816f43a97897e85a10dc3768c12b79 Mon Sep 17 00:00:00 2001 From: mydearxym Date: Thu, 3 Jun 2021 08:42:48 +0800 Subject: [PATCH 5/8] feat(post-qa): improve pinned logic readabily --- .../cms/delegates/article_comment.ex | 52 ++++++++++--------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/lib/groupher_server/cms/delegates/article_comment.ex b/lib/groupher_server/cms/delegates/article_comment.ex index 603e883db..d6ba8243b 100644 --- a/lib/groupher_server/cms/delegates/article_comment.ex +++ b/lib/groupher_server/cms/delegates/article_comment.ex @@ -282,7 +282,7 @@ defmodule GroupherServer.CMS.Delegate.ArticleComment do |> where(^where_query) |> QueryBuilder.filter_pack(Map.merge(filters, %{sort: sort})) |> ORM.paginater(~m(page size)a) - |> add_pined_comments_ifneed(thread, article_id, filters) + |> add_pinned_comments_ifneed(thread, article_id, filters) |> mark_viewer_emotion_states(user, :comment) |> mark_viewer_has_upvoted(user) |> done() @@ -304,49 +304,51 @@ defmodule GroupherServer.CMS.Delegate.ArticleComment do |> done() end - defp add_pined_comments_ifneed(%{entries: entries} = paged_comments, thread, article_id, %{ - page: 1 - }) do + defp add_pinned_comments_ifneed(paged_comments, thread, article_id, %{page: 1}) do with {:ok, info} <- match(thread), - query <- - from(p in ArticlePinnedComment, - join: c in ArticleComment, - on: p.article_comment_id == c.id, - where: field(p, ^info.foreign_key) == ^article_id, - order_by: [desc: p.inserted_at], - select: c - ), - {:ok, pined_comments} <- Repo.all(query) |> done() do - case pined_comments do + {:ok, pinned_comments} <- paged_pinned_comments(info, article_id) do + case pinned_comments do [] -> paged_comments _ -> - preloaded_pined_comments = - Enum.slice(pined_comments, 0, @pinned_comment_limit) + pinned_comments = + sort_solution_to_front(pinned_comments) + |> Enum.slice(0, @pinned_comment_limit) |> Repo.preload(reply_to: :author) - |> sort_solution_to_front - entries = Enum.concat(preloaded_pined_comments, entries) - pined_comment_count = length(pined_comments) + entries = pinned_comments ++ paged_comments.entries + pinned_comment_count = length(pinned_comments) - total_count = paged_comments.total_count + pined_comment_count + total_count = paged_comments.total_count + pinned_comment_count paged_comments |> Map.merge(%{entries: entries, total_count: total_count}) end end end - defp add_pined_comments_ifneed(paged_comments, _thread, _article_id, _), do: paged_comments + defp add_pinned_comments_ifneed(paged_comments, _thread, _article_id, _), do: paged_comments - defp sort_solution_to_front(pined_comments) do - solution_index = Enum.find_index(pined_comments, & &1.is_solution) + defp paged_pinned_comments(info, article_id) do + from(p in ArticlePinnedComment, + join: c in ArticleComment, + on: p.article_comment_id == c.id, + where: field(p, ^info.foreign_key) == ^article_id, + order_by: [desc: p.inserted_at], + select: c + ) + |> Repo.all() + |> done + end + + defp sort_solution_to_front(pinned_comments) do + solution_index = Enum.find_index(pinned_comments, & &1.is_solution) case is_nil(solution_index) do true -> - pined_comments + pinned_comments false -> - {solution_comment, rest_comments} = List.pop_at(pined_comments, solution_index) + {solution_comment, rest_comments} = List.pop_at(pinned_comments, solution_index) [solution_comment] ++ rest_comments end end From ddb876f842fa62b3e4cd69289a08ae74b79215a2 Mon Sep 17 00:00:00 2001 From: mydearxym Date: Thu, 3 Jun 2021 10:53:37 +0800 Subject: [PATCH 6/8] feat(post-qa): improve nameing func matching --- .../cms/delegates/article_comment.ex | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/lib/groupher_server/cms/delegates/article_comment.ex b/lib/groupher_server/cms/delegates/article_comment.ex index d6ba8243b..281aa635a 100644 --- a/lib/groupher_server/cms/delegates/article_comment.ex +++ b/lib/groupher_server/cms/delegates/article_comment.ex @@ -13,6 +13,7 @@ defmodule GroupherServer.CMS.Delegate.ArticleComment do alias Helper.Types, as: T alias Helper.{ORM, QueryBuilder} alias GroupherServer.{Accounts, CMS, Repo} + alias CMS.Post alias Accounts.User alias CMS.{ArticleComment, ArticlePinnedComment, Embeds} @@ -139,7 +140,7 @@ defmodule GroupherServer.CMS.Delegate.ArticleComment do def mark_comment_solution(article_comment_id, user) do with {:ok, article_comment} <- ORM.find(ArticleComment, article_comment_id), - {:ok, post} <- ORM.find(CMS.Post, article_comment.post_id, preload: [author: :user]) do + {:ok, post} <- ORM.find(Post, article_comment.post_id, preload: [author: :user]) do # 确保只有一个最佳答案 batch_update_solution_flag(post, false) CMS.pin_article_comment(article_comment.id) @@ -149,7 +150,7 @@ defmodule GroupherServer.CMS.Delegate.ArticleComment do def undo_mark_comment_solution(article_comment_id, user) do with {:ok, article_comment} <- ORM.find(ArticleComment, article_comment_id), - {:ok, post} <- ORM.find(CMS.Post, article_comment.post_id, preload: [author: :user]) do + {:ok, post} <- ORM.find(Post, article_comment.post_id, preload: [author: :user]) do do_mark_comment_solution(post, article_comment, user, false) end end @@ -175,7 +176,7 @@ defmodule GroupherServer.CMS.Delegate.ArticleComment do @doc """ batch update is_question flag for post-only article """ - def batch_update_question_flag(%CMS.Post{is_question: is_question} = post) do + def batch_update_question_flag(%Post{is_question: is_question} = post) do from(c in ArticleComment, where: c.post_id == ^post.id, update: [set: [is_for_question: ^is_question]] @@ -306,14 +307,14 @@ defmodule GroupherServer.CMS.Delegate.ArticleComment do defp add_pinned_comments_ifneed(paged_comments, thread, article_id, %{page: 1}) do with {:ok, info} <- match(thread), - {:ok, pinned_comments} <- paged_pinned_comments(info, article_id) do + {:ok, pinned_comments} <- list_pinned_comments(info, article_id) do case pinned_comments do [] -> paged_comments _ -> pinned_comments = - sort_solution_to_front(pinned_comments) + sort_solution_to_front(thread, pinned_comments) |> Enum.slice(0, @pinned_comment_limit) |> Repo.preload(reply_to: :author) @@ -328,11 +329,11 @@ defmodule GroupherServer.CMS.Delegate.ArticleComment do defp add_pinned_comments_ifneed(paged_comments, _thread, _article_id, _), do: paged_comments - defp paged_pinned_comments(info, article_id) do + defp list_pinned_comments(%{foreign_key: foreign_key}, article_id) do from(p in ArticlePinnedComment, join: c in ArticleComment, on: p.article_comment_id == c.id, - where: field(p, ^info.foreign_key) == ^article_id, + where: field(p, ^foreign_key) == ^article_id, order_by: [desc: p.inserted_at], select: c ) @@ -340,7 +341,8 @@ defmodule GroupherServer.CMS.Delegate.ArticleComment do |> done end - defp sort_solution_to_front(pinned_comments) do + # only support post + defp sort_solution_to_front(:post, pinned_comments) do solution_index = Enum.find_index(pinned_comments, & &1.is_solution) case is_nil(solution_index) do @@ -353,6 +355,8 @@ defmodule GroupherServer.CMS.Delegate.ArticleComment do end end + defp sort_solution_to_front(_, pinned_comments), do: pinned_comments + defp mark_viewer_has_upvoted(paged_comments, nil), do: paged_comments defp mark_viewer_has_upvoted(%{entries: entries} = paged_comments, %User{} = user) do @@ -372,7 +376,7 @@ defmodule GroupherServer.CMS.Delegate.ArticleComment do defp set_question_flag_ifneed(_, comment), do: ORM.update(comment, %{is_for_question: false}) # batch update is_solution flag for artilce comment - defp batch_update_solution_flag(%CMS.Post{} = post, is_question) do + defp batch_update_solution_flag(%Post{} = post, is_question) do from(c in ArticleComment, where: c.post_id == ^post.id, update: [set: [is_solution: ^is_question]] From bdc9257a582da03aa98bec1ecbfda51c5093dbe5 Mon Sep 17 00:00:00 2001 From: mydearxym Date: Thu, 3 Jun 2021 11:56:09 +0800 Subject: [PATCH 7/8] feat(post-qa): solution digest edge case --- .../cms/delegates/article_comment.ex | 8 ++++++ .../cms/articles/post_test.exs | 3 --- .../cms/comments/post_comment_test.exs | 26 +++++++++++++------ .../query/cms/comments/job_comment_test.exs | 1 - .../query/cms/comments/post_comment_test.exs | 3 +-- .../query/cms/comments/repo_comment_test.exs | 1 - 6 files changed, 27 insertions(+), 15 deletions(-) diff --git a/lib/groupher_server/cms/delegates/article_comment.ex b/lib/groupher_server/cms/delegates/article_comment.ex index 281aa635a..1e15e20f4 100644 --- a/lib/groupher_server/cms/delegates/article_comment.ex +++ b/lib/groupher_server/cms/delegates/article_comment.ex @@ -134,6 +134,14 @@ defmodule GroupherServer.CMS.Delegate.ArticleComment do @doc """ update a comment for article like psot, job ... """ + # 如果是 solution, 那么要更新对应的 post 的 solution_digest + def update_article_comment(%ArticleComment{is_solution: true} = article_comment, content) do + with {:ok, post} <- ORM.find(Post, article_comment.post_id) do + post |> ORM.update(%{solution_digest: content}) + article_comment |> ORM.update(%{body_html: content}) + end + end + def update_article_comment(%ArticleComment{} = article_comment, content) do article_comment |> ORM.update(%{body_html: content}) end diff --git a/test/groupher_server/cms/articles/post_test.exs b/test/groupher_server/cms/articles/post_test.exs index 978b687df..119993686 100644 --- a/test/groupher_server/cms/articles/post_test.exs +++ b/test/groupher_server/cms/articles/post_test.exs @@ -148,7 +148,6 @@ defmodule GroupherServer.Test.CMS.Articles.Post do end describe "[cms post question]" do - @tag :wip2 test "post have default question flags", ~m(user community post_attrs)a do {:ok, post} = CMS.create_article(community, :post, post_attrs, user) @@ -156,7 +155,6 @@ defmodule GroupherServer.Test.CMS.Articles.Post do assert not post.is_solved end - @tag :wip2 test "can create post with question", ~m(user community post_attrs)a do post_attrs = Map.merge(post_attrs, %{is_question: true}) {:ok, post} = CMS.create_article(community, :post, post_attrs, user) @@ -164,7 +162,6 @@ defmodule GroupherServer.Test.CMS.Articles.Post do assert post.is_question end - @tag :wip2 test "can update post with question", ~m(user community post_attrs)a do post_attrs = Map.merge(post_attrs, %{is_question: true}) {:ok, post} = CMS.create_article(community, :post, post_attrs, user) diff --git a/test/groupher_server/cms/comments/post_comment_test.exs b/test/groupher_server/cms/comments/post_comment_test.exs index 44cd3cd84..b9b096119 100644 --- a/test/groupher_server/cms/comments/post_comment_test.exs +++ b/test/groupher_server/cms/comments/post_comment_test.exs @@ -670,7 +670,6 @@ defmodule GroupherServer.Test.CMS.Comments.PostComment do end describe "[article comment qa type]" do - @tag :wip2 test "create comment for normal post should have default qa flags", ~m(user community)a do post_attrs = mock_attrs(:post, %{community_id: community.id}) {:ok, post} = CMS.create_article(community, :post, post_attrs, user) @@ -680,7 +679,6 @@ defmodule GroupherServer.Test.CMS.Comments.PostComment do assert not post_comment.is_solution end - @tag :wip2 test "create comment for question post should have flags", ~m(user community)a do post_attrs = mock_attrs(:post, %{community_id: community.id, is_question: true}) {:ok, post} = CMS.create_article(community, :post, post_attrs, user) @@ -690,7 +688,6 @@ defmodule GroupherServer.Test.CMS.Comments.PostComment do assert post_comment.is_for_question end - @tag :wip2 test "update comment with is_question should batch update exsit comments is_for_question field", ~m(user community)a do post_attrs = mock_attrs(:post, %{community_id: community.id, is_question: true}) @@ -714,7 +711,6 @@ defmodule GroupherServer.Test.CMS.Comments.PostComment do assert not comment3.is_for_question end - @tag :wip2 test "can mark a comment as solution", ~m(user community)a do post_attrs = mock_attrs(:post, %{community_id: community.id, is_question: true}) {:ok, post} = CMS.create_article(community, :post, post_attrs, user) @@ -732,7 +728,6 @@ defmodule GroupherServer.Test.CMS.Comments.PostComment do assert post.solution_digest == comment.body_html end - @tag :wip2 test "non-post-author can not mark a comment as solution", ~m(user community)a do post_attrs = mock_attrs(:post, %{community_id: community.id, is_question: true}) {:ok, post} = CMS.create_article(community, :post, post_attrs, user) @@ -747,7 +742,6 @@ defmodule GroupherServer.Test.CMS.Comments.PostComment do reason |> is_error?(:require_questioner) end - @tag :wip2 test "can undo mark a comment as solution", ~m(user community)a do post_attrs = mock_attrs(:post, %{community_id: community.id, is_question: true}) {:ok, post} = CMS.create_article(community, :post, post_attrs, user) @@ -765,7 +759,6 @@ defmodule GroupherServer.Test.CMS.Comments.PostComment do assert not post.is_solved end - @tag :wip2 test "non-post-author can not undo mark a comment as solution", ~m(user community)a do post_attrs = mock_attrs(:post, %{community_id: community.id, is_question: true}) {:ok, post} = CMS.create_article(community, :post, post_attrs, user) @@ -780,7 +773,6 @@ defmodule GroupherServer.Test.CMS.Comments.PostComment do reason |> is_error?(:require_questioner) end - @tag :wip2 test "can only mark one best comment as solution", ~m(user community)a do post_attrs = mock_attrs(:post, %{community_id: community.id, is_question: true}) {:ok, post} = CMS.create_article(community, :post, post_attrs, user) @@ -801,5 +793,23 @@ defmodule GroupherServer.Test.CMS.Comments.PostComment do assert answers |> length == 1 assert answers |> List.first() |> Map.get(:id) == comment2.id end + + test "update a solution should also update post's solution digest", ~m(user community)a do + post_attrs = mock_attrs(:post, %{community_id: community.id, is_question: true}) + {:ok, post} = CMS.create_article(community, :post, post_attrs, user) + + {:ok, post} = ORM.find(Post, post.id, preload: [author: :user]) + post_author = post.author.user + + {:ok, comment} = CMS.create_article_comment(:post, post.id, "solution", post_author) + {:ok, comment} = CMS.mark_comment_solution(comment.id, post_author) + + {:ok, post} = ORM.find(Post, post.id, preload: [author: :user]) + assert post.solution_digest == "solution" + + {:ok, _comment} = CMS.update_article_comment(comment, "new solution") + {:ok, post} = ORM.find(Post, post.id, preload: [author: :user]) + assert post.solution_digest == "new solution" + end end end diff --git a/test/groupher_server_web/query/cms/comments/job_comment_test.exs b/test/groupher_server_web/query/cms/comments/job_comment_test.exs index 9bf57971b..b4d763d86 100644 --- a/test/groupher_server_web/query/cms/comments/job_comment_test.exs +++ b/test/groupher_server_web/query/cms/comments/job_comment_test.exs @@ -240,7 +240,6 @@ defmodule GroupherServer.Test.Query.Comments.JobComment do assert results["totalCount"] == total_count end - @tag :wip test "guest user can get paged comment with pinned comment in it", ~m(guest_conn job user)a do total_count = 20 diff --git a/test/groupher_server_web/query/cms/comments/post_comment_test.exs b/test/groupher_server_web/query/cms/comments/post_comment_test.exs index 7856805f3..8baf1289d 100644 --- a/test/groupher_server_web/query/cms/comments/post_comment_test.exs +++ b/test/groupher_server_web/query/cms/comments/post_comment_test.exs @@ -242,7 +242,6 @@ defmodule GroupherServer.Test.Query.Comments.PostComment do assert results["totalCount"] == total_count end - @tag :wip test "guest user can get paged comment with pinned comment in it", ~m(guest_conn post user)a do total_count = 20 @@ -272,7 +271,7 @@ defmodule GroupherServer.Test.Query.Comments.PostComment do end # post only - @tag :wip + test "if solution in pinned comments, solution should always on top", ~m(guest_conn community user)a do post_attrs = mock_attrs(:post, %{community_id: community.id, is_question: true}) diff --git a/test/groupher_server_web/query/cms/comments/repo_comment_test.exs b/test/groupher_server_web/query/cms/comments/repo_comment_test.exs index d8cdcd893..83e6abf4d 100644 --- a/test/groupher_server_web/query/cms/comments/repo_comment_test.exs +++ b/test/groupher_server_web/query/cms/comments/repo_comment_test.exs @@ -238,7 +238,6 @@ defmodule GroupherServer.Test.Query.Comments.RepoComment do assert results["totalCount"] == total_count end - @tag :wip test "guest user can get paged comment with pinned comment in it", ~m(guest_conn repo user)a do total_count = 20 From 83fffc4fd633d1619b1b93257c761b0cedbaf13f Mon Sep 17 00:00:00 2001 From: mydearxym Date: Thu, 3 Jun 2021 12:57:39 +0800 Subject: [PATCH 8/8] feat(post-qa): gq workflow --- .../cms/delegates/article_comment.ex | 16 ++-- .../resolvers/cms_resolver.ex | 8 ++ .../schema/cms/cms_types.ex | 3 + .../schema/cms/mutations/comment.ex | 16 ++++ .../cms/comments/post_comment_test.exs | 75 +++++++++++++++++++ .../query/cms/comments/post_comment_test.exs | 4 +- 6 files changed, 115 insertions(+), 7 deletions(-) diff --git a/lib/groupher_server/cms/delegates/article_comment.ex b/lib/groupher_server/cms/delegates/article_comment.ex index 1e15e20f4..c61d63282 100644 --- a/lib/groupher_server/cms/delegates/article_comment.ex +++ b/lib/groupher_server/cms/delegates/article_comment.ex @@ -146,8 +146,11 @@ defmodule GroupherServer.CMS.Delegate.ArticleComment do article_comment |> ORM.update(%{body_html: content}) end - def mark_comment_solution(article_comment_id, user) do - with {:ok, article_comment} <- ORM.find(ArticleComment, article_comment_id), + @doc """ + mark a comment as question post's best solution + """ + def mark_comment_solution(comment_id, user) do + with {:ok, article_comment} <- ORM.find(ArticleComment, comment_id), {:ok, post} <- ORM.find(Post, article_comment.post_id, preload: [author: :user]) do # 确保只有一个最佳答案 batch_update_solution_flag(post, false) @@ -156,8 +159,11 @@ defmodule GroupherServer.CMS.Delegate.ArticleComment do end end - def undo_mark_comment_solution(article_comment_id, user) do - with {:ok, article_comment} <- ORM.find(ArticleComment, article_comment_id), + @doc """ + undo mark a comment as question post's best solution + """ + def undo_mark_comment_solution(comment_id, user) do + with {:ok, article_comment} <- ORM.find(ArticleComment, comment_id), {:ok, post} <- ORM.find(Post, article_comment.post_id, preload: [author: :user]) do do_mark_comment_solution(post, article_comment, user, false) end @@ -168,7 +174,7 @@ defmodule GroupherServer.CMS.Delegate.ArticleComment do with true <- user.id == post.author.user.id do Multi.new() |> Multi.run(:mark_solution, fn _, _ -> - ORM.update(article_comment, %{is_solution: is_solution}) + ORM.update(article_comment, %{is_solution: is_solution, is_for_question: true}) end) |> Multi.run(:update_post_state, fn _, _ -> ORM.update(post, %{is_solved: is_solution, solution_digest: article_comment.body_html}) diff --git a/lib/groupher_server_web/resolvers/cms_resolver.ex b/lib/groupher_server_web/resolvers/cms_resolver.ex index 8491b30c1..1261ef0a3 100644 --- a/lib/groupher_server_web/resolvers/cms_resolver.ex +++ b/lib/groupher_server_web/resolvers/cms_resolver.ex @@ -320,6 +320,14 @@ defmodule GroupherServerWeb.Resolvers.CMS do CMS.undo_emotion_to_comment(id, emotion, user) end + def mark_comment_solution(_root, ~m(id)a, %{context: %{cur_user: user}}) do + CMS.mark_comment_solution(id, user) + end + + def undo_mark_comment_solution(_root, ~m(id)a, %{context: %{cur_user: user}}) do + CMS.undo_mark_comment_solution(id, user) + end + ############ ############ ############ diff --git a/lib/groupher_server_web/schema/cms/cms_types.ex b/lib/groupher_server_web/schema/cms/cms_types.ex index dbb6c4931..9c5a52610 100644 --- a/lib/groupher_server_web/schema/cms/cms_types.ex +++ b/lib/groupher_server_web/schema/cms/cms_types.ex @@ -344,6 +344,9 @@ defmodule GroupherServerWeb.Schema.CMS.Types do field(:is_deleted, :boolean) field(:viewer_has_upvoted, :boolean) + field(:is_for_question, :boolean) + field(:is_solution, :boolean) + timestamp_fields() end diff --git a/lib/groupher_server_web/schema/cms/mutations/comment.ex b/lib/groupher_server_web/schema/cms/mutations/comment.ex index 3cced5c2f..3a98080c7 100644 --- a/lib/groupher_server_web/schema/cms/mutations/comment.ex +++ b/lib/groupher_server_web/schema/cms/mutations/comment.ex @@ -73,6 +73,22 @@ defmodule GroupherServerWeb.Schema.CMS.Mutations.Comment do resolve(&R.CMS.undo_emotion_to_comment/3) end + @desc "mark a comment as question post's best solution" + field :mark_comment_solution, :article_comment do + arg(:id, non_null(:id)) + + middleware(M.Authorize, :login) + resolve(&R.CMS.mark_comment_solution/3) + end + + @desc "mark a comment as question post's best solution" + field :undo_mark_comment_solution, :article_comment do + arg(:id, non_null(:id)) + + middleware(M.Authorize, :login) + resolve(&R.CMS.undo_mark_comment_solution/3) + end + ############################ ############################ ############################ diff --git a/test/groupher_server_web/mutation/cms/comments/post_comment_test.exs b/test/groupher_server_web/mutation/cms/comments/post_comment_test.exs index 77f9e8d8c..eb390df57 100644 --- a/test/groupher_server_web/mutation/cms/comments/post_comment_test.exs +++ b/test/groupher_server_web/mutation/cms/comments/post_comment_test.exs @@ -219,4 +219,79 @@ defmodule GroupherServer.Test.Mutation.Comments.PostComment do assert guest_conn |> mutation_get_error?(@query, variables, ecode(:account_login)) end end + + describe "[post only: article comment solution]" do + @query """ + mutation($id: ID!) { + markCommentSolution(id: $id) { + id + isForQuestion + isSolution + } + } + """ + @tag :wip + test "questioner can mark a post comment as solution", ~m(post)a do + {:ok, post} = ORM.find(Post, post.id, preload: [author: :user]) + post_author = post.author.user + {:ok, comment} = CMS.create_article_comment(:post, post.id, "solution", post_author) + + questioner_conn = simu_conn(:user, post_author) + + variables = %{id: comment.id} + + result = questioner_conn |> mutation_result(@query, variables, "markCommentSolution") + + assert result["isForQuestion"] + assert result["isSolution"] + end + + @tag :wip + test "other user can not mark a post comment as solution", ~m(guest_conn user_conn post)a do + {:ok, post} = ORM.find(Post, post.id, preload: [author: :user]) + post_author = post.author.user + {:ok, comment} = CMS.create_article_comment(:post, post.id, "solution", post_author) + + variables = %{id: comment.id} + assert user_conn |> mutation_get_error?(@query, variables, ecode(:require_questioner)) + assert guest_conn |> mutation_get_error?(@query, variables, ecode(:account_login)) + end + + @query """ + mutation($id: ID!) { + undoMarkCommentSolution(id: $id) { + id + isForQuestion + isSolution + } + } + """ + @tag :wip + test "questioner can undo mark a post comment as solution", ~m(post)a do + {:ok, post} = ORM.find(Post, post.id, preload: [author: :user]) + post_author = post.author.user + {:ok, comment} = CMS.create_article_comment(:post, post.id, "solution", post_author) + {:ok, comment} = CMS.mark_comment_solution(comment.id, post_author) + + questioner_conn = simu_conn(:user, post_author) + + variables = %{id: comment.id} + result = questioner_conn |> mutation_result(@query, variables, "undoMarkCommentSolution") + + assert result["isForQuestion"] + assert not result["isSolution"] + end + + @tag :wip + test "other user can not undo mark a post comment as solution", + ~m(guest_conn user_conn post)a do + {:ok, post} = ORM.find(Post, post.id, preload: [author: :user]) + post_author = post.author.user + {:ok, comment} = CMS.create_article_comment(:post, post.id, "solution", post_author) + + variables = %{id: comment.id} + assert user_conn |> mutation_get_error?(@query, variables, ecode(:require_questioner)) + assert guest_conn |> mutation_get_error?(@query, variables, ecode(:account_login)) + end + end end diff --git a/test/groupher_server_web/query/cms/comments/post_comment_test.exs b/test/groupher_server_web/query/cms/comments/post_comment_test.exs index 8baf1289d..fd10b01e2 100644 --- a/test/groupher_server_web/query/cms/comments/post_comment_test.exs +++ b/test/groupher_server_web/query/cms/comments/post_comment_test.exs @@ -290,7 +290,7 @@ defmodule GroupherServer.Test.Query.Comments.PostComment do post_author = post.author.user {:ok, comment} = CMS.create_article_comment(thread, post.id, "pinned comment", user) - {:ok, pinned_comment} = CMS.pin_article_comment(comment.id) + {:ok, _pinned_comment} = CMS.pin_article_comment(comment.id) Process.sleep(1000) @@ -299,7 +299,7 @@ defmodule GroupherServer.Test.Query.Comments.PostComment do Process.sleep(1000) {:ok, comment} = CMS.create_article_comment(thread, post.id, "pinned comment 2", user) - {:ok, pinned_comment2} = CMS.pin_article_comment(comment.id) + {:ok, _pinned_comment2} = CMS.pin_article_comment(comment.id) variables = %{id: post.id, thread: "POST", filter: %{page: 1, size: 10}} results = guest_conn |> query_result(@query, variables, "pagedArticleComments")