diff --git a/config/config.exs b/config/config.exs index 754ddedbb..607196a39 100644 --- a/config/config.exs +++ b/config/config.exs @@ -43,9 +43,9 @@ config :groupher_server, :general, # membership senior_amount_threshold: 51.2, # user achievements - user_achieve_star_weight: 1, + user_achieve_upvote_weight: 1, user_achieve_watch_weight: 1, - user_achieve_favorite_weight: 2, + user_achieve_collect_weight: 2, user_achieve_follow_weight: 3 config :groupher_server, :customization, diff --git a/lib/groupher_server/accounts/accounts.ex b/lib/groupher_server/accounts/accounts.ex index a3ab1db75..dae4a4e7a 100644 --- a/lib/groupher_server/accounts/accounts.ex +++ b/lib/groupher_server/accounts/accounts.ex @@ -5,12 +5,13 @@ defmodule GroupherServer.Accounts do Achievements, Customization, Fans, - FavoriteCategory, + CollectFolder, Publish, Mails, Profile, - ReactedContents, - Search + UpvotedArticles, + Search, + Utils } # profile @@ -20,13 +21,17 @@ defmodule GroupherServer.Accounts do defdelegate default_subscribed_communities(filter), to: Profile defdelegate subscribed_communities(user, filter), to: Profile - # favorite category - defdelegate list_favorite_categories(user, opt, filter), to: FavoriteCategory - defdelegate create_favorite_category(user, attrs), to: FavoriteCategory - defdelegate update_favorite_category(user, attrs), to: FavoriteCategory - defdelegate delete_favorite_category(user, id), to: FavoriteCategory - defdelegate set_favorites(user, thread, content_id, category_id), to: FavoriteCategory - defdelegate unset_favorites(user, thread, content_id, category_id), to: FavoriteCategory + # collect folder + defdelegate list_collect_folders(user_id, filter), to: CollectFolder + defdelegate list_collect_folders(user_id, filter, owner), to: CollectFolder + defdelegate list_collect_folder_articles(folder_id, filter, user), to: CollectFolder + defdelegate list_collect_folder_articles(folder_id, filter), to: CollectFolder + + defdelegate create_collect_folder(attrs, user), to: CollectFolder + defdelegate update_collect_folder(id, attrs), to: CollectFolder + defdelegate delete_collect_folder(id), to: CollectFolder + defdelegate add_to_collect(thread, article_id, folder_id, user), to: CollectFolder + defdelegate remove_from_collect(thread, article_id, folder_id, user), to: CollectFolder # achievement defdelegate achieve(user, operation, key), to: Achievements @@ -44,9 +49,8 @@ defmodule GroupherServer.Accounts do defdelegate fetch_followers(user, filter), to: Fans defdelegate fetch_followings(user, filter), to: Fans - # reacted contents - defdelegate reacted_contents(thread, react, filter, user), to: ReactedContents - defdelegate reacted_contents(thread, react, category_id, filter, user), to: ReactedContents + # upvoted articles + defdelegate list_upvoted_articles(user_id, filter), to: UpvotedArticles # mentions defdelegate fetch_mentions(user, filter), to: Mails @@ -67,4 +71,6 @@ defmodule GroupherServer.Accounts do defdelegate upgrade_by_plan(user, plan), to: Customization defdelegate search_users(args), to: Search + + defdelegate get_userid_and_cache(login), to: Utils end diff --git a/lib/groupher_server/accounts/achievement.ex b/lib/groupher_server/accounts/achievement.ex index 8c7838e3f..47fa4014f 100644 --- a/lib/groupher_server/accounts/achievement.ex +++ b/lib/groupher_server/accounts/achievement.ex @@ -8,14 +8,14 @@ defmodule GroupherServer.Accounts.Achievement do alias GroupherServer.Accounts.{User, SourceContribute} @required_fields ~w(user_id)a - @optional_fields ~w(contents_stared_count contents_favorited_count contents_watched_count followers_count reputation donate_member senior_member sponsor_member)a + @optional_fields ~w(articles_upvotes_count articles_collects_count contents_watched_count followers_count reputation donate_member senior_member sponsor_member)a @type t :: %Achievement{} schema "user_achievements" do belongs_to(:user, User) - field(:contents_stared_count, :integer, default: 0) - field(:contents_favorited_count, :integer, default: 0) + field(:articles_upvotes_count, :integer, default: 0) + field(:articles_collects_count, :integer, default: 0) field(:contents_watched_count, :integer, default: 0) field(:followers_count, :integer, default: 0) field(:reputation, :integer, default: 0) diff --git a/lib/groupher_server/accounts/collect_folder.ex b/lib/groupher_server/accounts/collect_folder.ex new file mode 100644 index 000000000..34efc1b7b --- /dev/null +++ b/lib/groupher_server/accounts/collect_folder.ex @@ -0,0 +1,59 @@ +defmodule GroupherServer.Accounts.CollectFolder do + @moduledoc false + alias __MODULE__ + + use Ecto.Schema + import Ecto.Changeset + + alias GroupherServer.{Accounts, CMS} + alias Accounts.{User, Embeds} + alias CMS.ArticleCollect + + @required_fields ~w(user_id title)a + @optional_fields ~w(index total_count private desc last_updated)a + + @supported_threads [:post, :job, :repo] + + def supported_threads, do: @supported_threads + + @type t :: %CollectFolder{} + schema "collect_folders" do + belongs_to(:user, User, foreign_key: :user_id) + # has_many(:posts, ...) + + field(:title, :string) + field(:desc, :string) + field(:index, :integer) + field(:total_count, :integer, default: 0) + field(:private, :boolean, default: false) + # last time when add/delete items in category + field(:last_updated, :utc_datetime) + + # 可以参照 fragment 查询语法啊 + # 2. article truple [{:post, 1}, [:job, 2]] ... 便于在计算 "成就" 的时候对比 + embeds_one(:meta, Embeds.CollectFolderMeta, on_replace: :delete) + embeds_many(:collects, ArticleCollect, on_replace: :delete) + + timestamps(type: :utc_datetime) + end + + @doc false + def changeset(%CollectFolder{} = collect_folder, attrs) do + collect_folder + |> cast(attrs, @optional_fields ++ @required_fields) + |> validate_required(@required_fields) + |> cast_embed(:meta, required: true, with: &Embeds.CollectFolderMeta.changeset/2) + |> validate_length(:title, min: 1) + |> foreign_key_constraint(:user_id) + end + + @doc false + def update_changeset(%CollectFolder{} = collect_folder, attrs) do + collect_folder + |> cast(attrs, @optional_fields ++ @required_fields) + |> cast_embed(:collects, with: &ArticleCollect.changeset/2) + |> cast_embed(:meta, with: &Embeds.CollectFolderMeta.changeset/2) + |> validate_length(:title, min: 1) + |> foreign_key_constraint(:user_id) + end +end diff --git a/lib/groupher_server/accounts/delegates/achievements.ex b/lib/groupher_server/accounts/delegates/achievements.ex index 35a5f0240..60f52979f 100644 --- a/lib/groupher_server/accounts/delegates/achievements.ex +++ b/lib/groupher_server/accounts/delegates/achievements.ex @@ -2,9 +2,9 @@ defmodule GroupherServer.Accounts.Delegate.Achievements do @moduledoc """ user achievements related acheiveements formula: - 1. create content been stared by other user + 1 + 1. create content been upvoteed by other user + 1 2. create content been watched by other user + 1 - 3. create content been favorited by other user + 2 + 3. create content been colleced by other user + 2 4. followed by other user + 3 """ import Ecto.Query, warn: false @@ -14,16 +14,16 @@ defmodule GroupherServer.Accounts.Delegate.Achievements do alias Helper.{ORM, SpecType} alias GroupherServer.Accounts.{Achievement, User} - @favorite_weight get_config(:general, :user_achieve_favorite_weight) - @star_weight get_config(:general, :user_achieve_star_weight) + @collect_weight get_config(:general, :user_achieve_collect_weight) + @upvote_weight get_config(:general, :user_achieve_upvote_weight) # @watch_weight get_config(:general, :user_achieve_watch_weight) @follow_weight get_config(:general, :user_achieve_follow_weight) @doc """ - add user's achievement by add followers_count of favorite_weight + inc user's achievement by inc followers_count of collect_weight """ @spec achieve(User.t(), atom, atom) :: SpecType.done() - def achieve(%User{id: user_id}, :add, :follow) do + def achieve(%User{id: user_id}, :inc, :follow) do with {:ok, achievement} <- ORM.findby_or_insert(Achievement, ~m(user_id)a, ~m(user_id)a) do followers_count = achievement.followers_count + 1 reputation = achievement.reputation + @follow_weight @@ -34,9 +34,9 @@ defmodule GroupherServer.Accounts.Delegate.Achievements do end @doc """ - minus user's achievement by add followers_count of favorite_weight + dec user's achievement by inc followers_count of collect_weight """ - def achieve(%User{id: user_id}, :minus, :follow) do + def achieve(%User{id: user_id}, :dec, :follow) do with {:ok, achievement} <- ORM.findby_or_insert(Achievement, ~m(user_id)a, ~m(user_id)a) do followers_count = max(achievement.followers_count - 1, 0) reputation = max(achievement.reputation - @follow_weight, 0) @@ -47,55 +47,55 @@ defmodule GroupherServer.Accounts.Delegate.Achievements do end @doc """ - add user's achievement by contents_stared_count of star_weight + inc user's achievement by articles_upvotes_count of upvote_weight """ - def achieve(%User{id: user_id} = _user, :add, :star) do + def achieve(%User{id: user_id} = _user, :inc, :upvote) do with {:ok, achievement} <- ORM.findby_or_insert(Achievement, ~m(user_id)a, ~m(user_id)a) do - contents_stared_count = achievement.contents_stared_count + 1 - reputation = achievement.reputation + @star_weight + articles_upvotes_count = achievement.articles_upvotes_count + 1 + reputation = achievement.reputation + @upvote_weight achievement - |> ORM.update(~m(contents_stared_count reputation)a) + |> ORM.update(~m(articles_upvotes_count reputation)a) end end @doc """ - minus user's achievement by contents_stared_count of star_weight + dec user's achievement by articles_upvotes_count of upvote_weight """ - def achieve(%User{id: user_id} = _user, :minus, :star) do + def achieve(%User{id: user_id} = _user, :dec, :upvote) do with {:ok, achievement} <- ORM.findby_or_insert(Achievement, ~m(user_id)a, ~m(user_id)a) do - contents_stared_count = max(achievement.contents_stared_count - 1, 0) - reputation = max(achievement.reputation - @star_weight, 0) + articles_upvotes_count = max(achievement.articles_upvotes_count - 1, 0) + reputation = max(achievement.reputation - @upvote_weight, 0) achievement - |> ORM.update(~m(contents_stared_count reputation)a) + |> ORM.update(~m(articles_upvotes_count reputation)a) end end @doc """ - minus user's achievement by contents_favorited_count of favorite_weight + dec user's achievement by articles_collects_count of collect_weight """ - def achieve(%User{id: user_id} = _user, :add, :favorite) do + def achieve(%User{id: user_id} = _user, :inc, :collect) do with {:ok, achievement} <- ORM.findby_or_insert(Achievement, ~m(user_id)a, ~m(user_id)a) do - contents_favorited_count = achievement.contents_favorited_count + 1 - reputation = achievement.reputation + @favorite_weight + articles_collects_count = achievement.articles_collects_count + 1 + reputation = achievement.reputation + @collect_weight achievement - |> ORM.update(~m(contents_favorited_count reputation)a) + |> ORM.update(~m(articles_collects_count reputation)a) end end @doc """ - add user's achievement by contents_favorited_count of favorite_weight + inc user's achievement by articles_collects_count of collect_weight """ - def achieve(%User{id: user_id} = _user, :minus, :favorite) do + def achieve(%User{id: user_id} = _user, :dec, :collect) do with {:ok, achievement} <- ORM.findby_or_insert(Achievement, ~m(user_id)a, ~m(user_id)a) do - contents_favorited_count = max(achievement.contents_favorited_count - 1, 0) + articles_collects_count = max(achievement.articles_collects_count - 1, 0) - reputation = max(achievement.reputation - @favorite_weight, 0) + reputation = max(achievement.reputation - @collect_weight, 0) achievement - |> ORM.update(~m(contents_favorited_count reputation)a) + |> ORM.update(~m(articles_collects_count reputation)a) end end @@ -113,27 +113,15 @@ defmodule GroupherServer.Accounts.Delegate.Achievements do @doc """ only used for user delete the farorited category, other case is auto """ - def downgrade_achievement(%User{id: user_id}, :favorite, count) do + def downgrade_achievement(%User{id: user_id}, :collect, count) do with {:ok, achievement} <- ORM.find_by(Achievement, user_id: user_id) do - contents_favorited_count = max(achievement.contents_favorited_count - count, 0) - reputation = max(achievement.reputation - count * @favorite_weight, 0) + articles_collects_count = max(achievement.articles_collects_count - count, 0) + reputation = max(achievement.reputation - count * @collect_weight, 0) - achievement - |> ORM.update(~m(contents_favorited_count reputation)a) + achievement |> ORM.update(~m(articles_collects_count reputation)a) end end - # @spec safe_minus(non_neg_integer(), non_neg_integer()) :: non_neg_integer() - # defp safe_minus(count, unit) when is_integer(count) and is_integer(unit) and unit > 0 do - # case count <= 0 do - # true -> - # 0 - - # false -> - # count - unit - # end - # end - @doc """ list communities which the user is editor in it """ diff --git a/lib/groupher_server/accounts/delegates/collect_folder.ex b/lib/groupher_server/accounts/delegates/collect_folder.ex new file mode 100644 index 000000000..c130213c2 --- /dev/null +++ b/lib/groupher_server/accounts/delegates/collect_folder.ex @@ -0,0 +1,262 @@ +defmodule GroupherServer.Accounts.Delegate.CollectFolder do + @moduledoc """ + user collect folder related + """ + import Ecto.Query, warn: false + import GroupherServer.CMS.Utils.Matcher2 + + alias Helper.Types, as: T + alias Helper.QueryBuilder + + import Helper.ErrorCode + import Helper.Utils, only: [done: 1] + + import ShortMaps + + alias Helper.ORM + alias GroupherServer.{Accounts, CMS, Repo} + + alias Accounts.{CollectFolder, Embeds, User} + alias CMS.{ArticleCollect} + + alias Ecto.Multi + + # @max_article_count_per_collect_folder 300 + + @default_meta Embeds.CollectFolderMeta.default_meta() + @supported_collect_threads [:post, :job] + + @doc """ + list a user's not-private collect folders + """ + def list_collect_folders(user_id, filter) do + query = CollectFolder |> where([c], c.user_id == ^user_id and not c.private) + + do_list_collect_folders(filter, query) + end + + @doc """ + list a owner's collect folders + """ + def list_collect_folders(user_id, filter, %User{id: cur_user_id}) do + query = + if cur_user_id == user_id, + do: CollectFolder |> where([c], c.user_id == ^user_id), + else: CollectFolder |> where([c], c.user_id == ^user_id and not c.private) + + do_list_collect_folders(filter, query) + end + + @doc """ + list article inside a collect folder + """ + def list_collect_folder_articles(folder_id, filter) do + with {:ok, folder} <- ORM.find(CollectFolder, folder_id) do + case folder.private do + true -> raise_error(:private_collect_folder, "#{folder.title} is private") + false -> do_list_collect_folder_articles(folder, filter) + end + end + end + + def list_collect_folder_articles(folder_id, filter, %User{id: cur_user_id}) do + with {:ok, folder} <- ORM.find(CollectFolder, folder_id) do + is_valid_request = + case folder.private do + true -> folder.user_id == cur_user_id + false -> true + end + + case is_valid_request do + false -> raise_error(:private_collect_folder, "#{folder.title} is private") + true -> do_list_collect_folder_articles(folder, filter) + end + end + end + + defp do_list_collect_folder_articles(folder, filter) do + Repo.preload(folder.collects, @supported_collect_threads) + |> ORM.embeds_paginater(filter) + |> ORM.extract_articles(@supported_collect_threads) + |> done() + end + + @doc """ + create a collect folder for articles + """ + def create_collect_folder(%{title: title} = attrs, %User{id: user_id}) do + with {:error, _} <- ORM.find_by(CollectFolder, ~m(user_id title)a) do + last_updated = Timex.today() |> Timex.to_datetime() + + args = + Map.merge( + %{ + user_id: user_id, + last_updated: last_updated, + meta: @default_meta + }, + attrs + ) + + CollectFolder |> ORM.create(args) + else + {:ok, folder} -> raise_error(:already_exsit, "#{folder.title} already exsits") + end + end + + def update_collect_folder(folder_id, attrs) do + with {:ok, folder} <- ORM.find(CollectFolder, folder_id) do + last_updated = Timex.today() |> Timex.to_datetime() + folder |> ORM.update(Map.merge(~m(last_updated)a, attrs)) + end + end + + @doc """ + delete empty collect folder + """ + @spec delete_collect_folder(T.id()) :: {:ok, CollectFolder.t()} + def delete_collect_folder(id) do + with {:ok, folder} <- ORM.find(CollectFolder, id) do + is_folder_empty = Enum.empty?(folder.collects) + + case is_folder_empty do + true -> CollectFolder |> ORM.find_delete!(id) + false -> raise_error(:delete_no_empty_collect_folder, "#{folder.title} is not empty") + end + end + end + + @doc """ + add article from collect folder + """ + @spec add_to_collect(T.article_thread(), T.id(), T.id(), User.t()) :: {:ok, CollectFolder.t()} + def add_to_collect(thread, article_id, folder_id, %User{id: cur_user_id} = user) do + with {:ok, folder} <- ORM.find(CollectFolder, folder_id), + {:ok, _} <- article_not_collect_in_folder(thread, article_id, folder.collects), + # 是否是该 folder 的 owner ? + true <- cur_user_id == folder.user_id do + Multi.new() + |> Multi.run(:add_article_collect, fn _, _ -> + CMS.collect_article_ifneed(thread, article_id, user) + end) + |> Multi.run(:add_to_collect_folder, fn _, %{add_article_collect: article_collect} -> + collects = [article_collect] ++ folder.collects + update_folder_meta(thread, collects, folder) + end) + # order is important, otherwise the foler in article_collect is not the latest + |> Multi.run(:set_article_collect_folder, fn _, + %{ + add_article_collect: article_collect, + add_to_collect_folder: folder + } -> + CMS.set_collect_folder(article_collect, folder) + end) + |> Repo.transaction() + |> upsert_collect_folder_result() + end + end + + @doc """ + remove article from collect folder + """ + @spec remove_from_collect(T.article_thread(), T.id(), T.id(), User.t()) :: + {:ok, CollectFolder.t()} + def remove_from_collect(thread, article_id, folder_id, %User{id: cur_user_id} = user) do + with {:ok, folder} <- ORM.find(CollectFolder, folder_id), + # 是否是该 folder 的 owner ? + true <- cur_user_id == folder.user_id do + Multi.new() + |> Multi.run(:del_article_collect, fn _, _ -> + CMS.undo_collect_article_ifneed(thread, article_id, user) + end) + |> Multi.run(:rm_from_collect_folder, fn _, %{del_article_collect: article_collect} -> + # 不能用 -- 语法,因为两个结构体的 meta 信息不同,摔。 + collects = Enum.reject(folder.collects, &(&1.id == article_collect.id)) + update_folder_meta(thread, collects, folder) + end) + # order is important, otherwise the foler in article_collect is not the latest + |> Multi.run(:unset_article_collect_folder, fn _, + %{ + del_article_collect: article_collect, + rm_from_collect_folder: folder + } -> + CMS.undo_set_collect_folder(article_collect, folder) + end) + |> Repo.transaction() + |> upsert_collect_folder_result() + end + end + + @spec update_folder_meta(T.article_thread(), [ArticleCollect.t()], CollectFolder.t()) :: + CollectFolder.t() + defp update_folder_meta(thread, collects, folder) do + total_count = length(collects) + last_updated = Timex.today() |> Timex.to_datetime() + + thread_count = Enum.filter(collects, &(not is_nil(Map.get(&1, :"#{thread}_id")))) |> length + + threads_flag_map = %{"has_#{thread}": thread_count > 0} + thread_count_map = %{"#{thread}_count": thread_count} + + meta = + folder.meta + |> Map.merge(threads_flag_map) + |> Map.merge(thread_count_map) + |> Map.from_struct() + |> Map.delete(:id) + + folder + |> Ecto.Changeset.change(%{total_count: total_count, last_updated: last_updated}) + |> Ecto.Changeset.put_embed(:collects, collects) + |> Ecto.Changeset.put_embed(:meta, meta) + |> Repo.update() + end + + # check if the article is already in this folder + @spec article_not_collect_in_folder(T.article_thread(), T.id(), [ArticleCollect.t()]) :: + T.done() + defp article_not_collect_in_folder(thread, article_id, collects) do + with {:ok, info} <- match(thread) do + already_collected = + Enum.any?(collects, fn c -> + article_id == Map.get(c, info.foreign_key) + end) + + case already_collected do + true -> raise_error(:already_collected_in_folder, "already collected in this folder") + false -> {:ok, :pass} + end + end + end + + defp do_list_collect_folders(filter, query) do + %{page: page, size: size} = filter + + query + |> filter_thread_ifneed(filter) + |> QueryBuilder.filter_pack(filter) + |> ORM.paginater(page: page, size: size) + |> done() + end + + defp filter_thread_ifneed(query, %{thread: thread}) do + field_name = "has_#{thread}" + field_value = true + + # see https://stackoverflow.com/a/55922528/4050784 + query + |> where([f], fragment("(?->>?)::boolean = ?", f.meta, ^field_name, ^field_value)) + end + + defp filter_thread_ifneed(query, _), do: query + + defp upsert_collect_folder_result({:ok, %{add_to_collect_folder: result}}), do: {:ok, result} + + defp upsert_collect_folder_result({:ok, %{rm_from_collect_folder: result}}) do + {:ok, result} + end + + defp upsert_collect_folder_result({:error, _, result, _steps}) do + {:error, result} + end +end diff --git a/lib/groupher_server/accounts/delegates/fans.ex b/lib/groupher_server/accounts/delegates/fans.ex index beb5266c4..9e2d1714a 100644 --- a/lib/groupher_server/accounts/delegates/fans.ex +++ b/lib/groupher_server/accounts/delegates/fans.ex @@ -32,7 +32,7 @@ defmodule GroupherServer.Accounts.Delegate.Fans do UserFollowing.changeset(%UserFollowing{}, %{user_id: user_id, following_id: follower_id}) ) |> Multi.run(:add_achievement, fn _, _ -> - Accounts.achieve(%User{id: follower_id}, :add, :follow) + Accounts.achieve(%User{id: follower_id}, :inc, :follow) end) |> Repo.transaction() |> follow_result() @@ -81,7 +81,7 @@ defmodule GroupherServer.Accounts.Delegate.Fans do ORM.findby_delete!(UserFollowing, %{user_id: user_id, following_id: follower_id}) end) |> Multi.run(:minus_achievement, fn _, _ -> - Accounts.achieve(%User{id: follower_id}, :minus, :follow) + Accounts.achieve(%User{id: follower_id}, :dec, :follow) end) |> Repo.transaction() |> undo_follow_result() diff --git a/lib/groupher_server/accounts/delegates/favorite_category.ex b/lib/groupher_server/accounts/delegates/favorite_category.ex deleted file mode 100644 index 3cb79d395..000000000 --- a/lib/groupher_server/accounts/delegates/favorite_category.ex +++ /dev/null @@ -1,249 +0,0 @@ -defmodule GroupherServer.Accounts.Delegate.FavoriteCategory do - @moduledoc """ - user FavoriteCategory related - """ - import Ecto.Query, warn: false - - alias Helper.QueryBuilder - - import Helper.ErrorCode - import Helper.Utils, only: [done: 1, count_words: 1] - - import ShortMaps - - alias Helper.ORM - alias GroupherServer.Accounts - alias GroupherServer.Accounts.{FavoriteCategory, User} - alias GroupherServer.{CMS, Repo} - - alias CMS.{PostFavorite, JobFavorite, VideoFavorite, RepoFavorite} - - alias Ecto.Multi - - def create_favorite_category(%User{id: user_id}, %{title: title} = attrs) do - with {:error, _} <- FavoriteCategory |> ORM.find_by(~m(user_id title)a) do - last_updated = Timex.today() |> Timex.to_datetime() - FavoriteCategory |> ORM.create(Map.merge(~m(user_id last_updated)a, attrs)) - else - {:ok, category} -> - {:error, [message: "#{category.title} already exsits", code: ecode(:already_exsit)]} - end - end - - def update_favorite_category(%User{id: user_id}, %{id: id} = attrs) do - with {:ok, category} <- FavoriteCategory |> ORM.find_by(~m(id user_id)a) do - last_updated = Timex.today() |> Timex.to_datetime() - category |> ORM.update(Map.merge(~m(last_updated)a, attrs)) - end - end - - def delete_favorite_category(%User{id: user_id}, id) do - with {:ok, category} <- FavoriteCategory |> ORM.find_by(~m(id user_id)a) do - Multi.new() - |> Multi.run(:downgrade_achievement, fn _, _ -> - # find user favvoried-contents(posts & jobs) 's author, - # and downgrade their's acieveents - # NOTE: this is too fucking violent and should be refactor later - # we find favroted posts/jobs author_ids then doengrade their achievement - # this implentment is limited, if the user have lots contents in a favoreted-category - # ant those contents have diffenert author each, it may be fucked - # should be in a queue job or sth - {:ok, post_author_ids} = affected_author_ids(:post, CMS.PostFavorite, category) - {:ok, job_author_ids} = affected_author_ids(:job, CMS.JobFavorite, category) - {:ok, repo_author_ids} = affected_author_ids(:repo, CMS.RepoFavorite, category) - - # author_ids_list = count_words(total_author_ids) |> Map.to_list - author_ids_list = - (post_author_ids ++ job_author_ids ++ repo_author_ids) - |> count_words - |> Map.to_list() - - # NOTE: if the contents have too many unique authors, it may be crash the server - # so limit size to 20 unique authors - Enum.each(author_ids_list |> Enum.slice(0, 20), fn {author_id, count} -> - Accounts.downgrade_achievement(%User{id: author_id}, :favorite, count) - end) - - {:ok, %{done: true}} - end) - |> Multi.run(:delete_category, fn _, _ -> - category |> ORM.delete() - end) - |> Repo.transaction() - |> delete_favorites_result() - end - end - - # NOTE: this is too fucking violent and should be refactor later - # we find favroted posts/jobs author_ids then doengrade their achievement - # this implentment is limited, if the user have lots contents in a favoreted-category - # ant those contents have diffenert author each, it may be fucked - defp affected_author_ids(thread, queryable, category) do - query = - from( - fc in queryable, - join: content in assoc(fc, ^thread), - join: author in assoc(content, :author), - where: fc.category_id == ^category.id, - select: author.user_id - ) - - case ORM.find_all(query, %{page: 1, size: 50}) do - {:ok, paged_contents} -> - {:ok, paged_contents |> Map.get(:entries)} - - {:error, _} -> - {:ok, []} - end - end - - defp delete_favorites_result({:ok, %{downgrade_achievement: result}}), do: {:ok, result} - - defp delete_favorites_result({:error, :delete_category, %Ecto.Changeset{} = result, _steps}) do - {:error, result} - end - - defp delete_favorites_result({:error, :delete_category, _result, _steps}) do - {:error, [message: "delete category fails", code: ecode(:delete_fails)]} - end - - defp delete_favorites_result({:error, :delete_favorite_record, _result, _steps}) do - {:error, [message: "delete delete_favorite_record fails", code: ecode(:delete_fails)]} - end - - def list_favorite_categories( - %User{id: user_id}, - %{private: private}, - %{page: page, size: size} = filter - ) do - query = - case private do - true -> - FavoriteCategory - |> where([c], c.user_id == ^user_id) - - false -> - FavoriteCategory - |> where([c], c.user_id == ^user_id) - |> where([c], c.private == false) - end - - query - |> QueryBuilder.filter_pack(filter) - |> ORM.paginater(page: page, size: size) - |> done() - end - - @doc """ - set category for favorited content (post, job ...) - """ - def set_favorites(%User{} = user, thread, content_id, category_id) do - with {:ok, favorite_category} <- - FavoriteCategory |> ORM.find_by(%{user_id: user.id, id: category_id}) do - Multi.new() - |> Multi.run(:favorite_content, fn _, _ -> - with {:ok, content_favorite} <- find_content_favorite(thread, content_id, user.id) do - check_dup_category(content_favorite, favorite_category) - else - {:error, _} -> - case CMS.reaction(thread, :favorite, content_id, user) do - {:ok, _} -> find_content_favorite(thread, content_id, user.id) - {:error, reason} -> {:error, reason} - end - end - end) - |> Multi.run(:dec_old_category_count, fn _, %{favorite_content: content_favorite} -> - with false <- is_nil(content_favorite.category_id), - {:ok, old_category} <- FavoriteCategory |> ORM.find(content_favorite.category_id) do - old_category - |> ORM.update(%{total_count: max(old_category.total_count - 1, 0)}) - else - true -> {:ok, ""} - reason -> {:error, reason} - end - end) - |> Multi.run(:update_content_category_id, fn _, %{favorite_content: content_favorite} -> - content_favorite |> ORM.update(%{category_id: favorite_category.id}) - end) - |> Multi.run(:update_category_info, fn _, _ -> - last_updated = Timex.today() |> Timex.to_datetime() - - favorite_category - |> ORM.update(%{ - last_updated: last_updated, - total_count: favorite_category.total_count + 1 - }) - end) - |> Repo.transaction() - |> set_favorites_result() - end - end - - defp set_favorites_result({:ok, %{update_category_info: result}}), do: {:ok, result} - - defp set_favorites_result({:error, :favorite_content, result, _steps}) do - # {:error, [message: "favorite content fails", code: ecode(:react_fails)]} - {:error, result} - end - - defp set_favorites_result({:error, :dec_old_category_count, _result, _steps}) do - {:error, [message: "update old category count fails", code: ecode(:update_fails)]} - end - - defp set_favorites_result({:error, :update_content_category_id, _result, _steps}) do - {:error, [message: "update category content fails", code: ecode(:update_fails)]} - end - - defp set_favorites_result({:error, :update_count, _result, _steps}) do - {:error, [message: "inc total count fails", code: ecode(:update_fails)]} - end - - def unset_favorites(%User{} = user, thread, content_id, category_id) do - with {:ok, favorite_category} <- - FavoriteCategory |> ORM.find_by(%{user_id: user.id, id: category_id}) do - Multi.new() - |> Multi.run(:undo_favorite_action, fn _, _ -> - CMS.undo_reaction(thread, :favorite, content_id, user) - end) - |> Multi.run(:update_category_info, fn _, _ -> - last_updated = Timex.today() |> Timex.to_datetime() - - favorite_category - |> ORM.update(%{ - last_updated: last_updated, - total_count: max(favorite_category.total_count - 1, 0) - }) - end) - |> Repo.transaction() - |> unset_favorites_result() - end - end - - # @spec unset_favorites_result({:ok, map()}) :: {:ok, FavoriteCategory.t() } - defp unset_favorites_result({:ok, %{update_category_info: result}}), do: {:ok, result} - - defp unset_favorites_result({:error, :undo_favorite_action, result, _steps}) do - # {:error, [message: "favorite content fails", code: ecode(:react_fails)]} - {:error, result} - end - - defp unset_favorites_result({:error, :dec_count, result, _steps}) do - {:error, result} - end - - defp find_content_favorite(:post, content_id, user_id), - do: PostFavorite |> ORM.find_by(%{post_id: content_id, user_id: user_id}) - - defp find_content_favorite(:job, content_id, user_id), - do: JobFavorite |> ORM.find_by(%{job_id: content_id, user_id: user_id}) - - defp find_content_favorite(:repo, content_id, user_id), - do: RepoFavorite |> ORM.find_by(%{repo_id: content_id, user_id: user_id}) - - defp check_dup_category(content, category) do - case content.category_id !== category.id do - true -> {:ok, content} - false -> {:error, [message: "viewer has already categoried", code: ecode(:already_did)]} - end - end -end diff --git a/lib/groupher_server/accounts/delegates/reacted_contents.ex b/lib/groupher_server/accounts/delegates/reacted_contents.ex deleted file mode 100644 index b9f09478e..000000000 --- a/lib/groupher_server/accounts/delegates/reacted_contents.ex +++ /dev/null @@ -1,53 +0,0 @@ -defmodule GroupherServer.Accounts.Delegate.ReactedContents do - @moduledoc """ - get contents(posts, jobs ...) that user reacted (star, favorite ..) - """ - import GroupherServer.CMS.Utils.Matcher - import Ecto.Query, warn: false - import Helper.Utils, only: [done: 1] - import ShortMaps - - alias Helper.{ORM, QueryBuilder} - alias GroupherServer.Accounts.User - - @doc """ - paged favorite contents of a spec category - """ - def reacted_contents(thread, :favorite, category_id, ~m(page size)a = filter, %User{id: user_id}) do - with {:ok, action} <- match_action(thread, :favorite) do - action.reactor - |> where([f], f.user_id == ^user_id) - |> join(:inner, [f], p in assoc(f, ^thread)) - |> join(:inner, [f], c in assoc(f, :category)) - |> where([f, p, c], c.id == ^category_id) - |> select([f, p], p) - |> QueryBuilder.filter_pack(filter) - |> ORM.paginater(~m(page size)a) - |> done() - end - end - - @doc """ - paged favorited/stared contents - """ - def reacted_contents(thread, react, ~m(page size)a = filter, %User{id: user_id}) do - with {:ok, action} <- match_action(thread, react) do - action.reactor - |> where([f], f.user_id == ^user_id) - |> join(:inner, [f], p in assoc(f, ^thread)) - |> select([f, p], p) - |> QueryBuilder.filter_pack(filter) - |> ORM.paginater(~m(page size)a) - |> done() - end - end - - # def reacted_count(thread, react, %User{id: user_id}) do - # with {:ok, action} <- match_action(thread, react) do - # action.reactor - # |> where([f], f.user_id == ^user_id) - # |> group_by([f], f.post_id) - # |> select([f], count(f.id)) - # end - # end -end diff --git a/lib/groupher_server/accounts/delegates/upvoted_articles.ex b/lib/groupher_server/accounts/delegates/upvoted_articles.ex new file mode 100644 index 000000000..bca1a728e --- /dev/null +++ b/lib/groupher_server/accounts/delegates/upvoted_articles.ex @@ -0,0 +1,44 @@ +defmodule GroupherServer.Accounts.Delegate.UpvotedArticles do + @moduledoc """ + get contents(posts, jobs ...) that user upvotes + """ + # import GroupherServer.CMS.Utils.Matcher + import Ecto.Query, warn: false + import Helper.Utils, only: [done: 1] + import ShortMaps + + alias Helper.{ORM, QueryBuilder} + + alias GroupherServer.CMS + alias CMS.{ArticleUpvote} + + # TODO: move to Model + @supported_uovoted_threads [:post, :job] + + @doc """ + get paged upvoted articles + """ + def list_upvoted_articles(user_id, %{thread: thread} = filter) do + thread_upcase = thread |> to_string |> String.upcase() + where_query = dynamic([a], a.user_id == ^user_id and a.thread == ^thread_upcase) + + load_upvoted_articles(where_query, filter) + end + + def list_upvoted_articles(user_id, filter) do + where_query = dynamic([a], a.user_id == ^user_id) + + load_upvoted_articles(where_query, filter) + end + + defp load_upvoted_articles(where_query, %{page: page, size: size} = filter) do + query = from(a in ArticleUpvote, preload: ^@supported_uovoted_threads) + + query + |> where(^where_query) + |> QueryBuilder.filter_pack(filter) + |> ORM.paginater(~m(page size)a) + |> ORM.extract_articles(@supported_uovoted_threads) + |> done() + end +end diff --git a/lib/groupher_server/accounts/delegates/utils.ex b/lib/groupher_server/accounts/delegates/utils.ex new file mode 100644 index 000000000..cab38639c --- /dev/null +++ b/lib/groupher_server/accounts/delegates/utils.ex @@ -0,0 +1,28 @@ +defmodule GroupherServer.Accounts.Delegate.Utils do + @moduledoc """ + utils for Accounts + """ + alias GroupherServer.Accounts + alias Accounts.User + alias Helper.{Cache, ORM} + + @cache_pool :user_login + + @doc """ + get and cache user'id by user's login + """ + @spec get_userid_and_cache(String.t()) :: {:ok, Integer.t()} | {:error, any} + def get_userid_and_cache(login) do + case Cache.get(@cache_pool, login) do + {:ok, user_id} -> {:ok, user_id} + {:error, _} -> get_and_cache(login) + end + end + + defp get_and_cache(login) do + with {:ok, user} <- ORM.find_by(User, %{login: login}) do + Cache.put(@cache_pool, login, user.id) + {:ok, user.id} + end + end +end diff --git a/lib/groupher_server/accounts/embeds/collect_folder_meta.ex b/lib/groupher_server/accounts/embeds/collect_folder_meta.ex new file mode 100644 index 000000000..76ca25b11 --- /dev/null +++ b/lib/groupher_server/accounts/embeds/collect_folder_meta.ex @@ -0,0 +1,56 @@ +defmodule GroupherServer.CMS.Embeds.CollectFolderMeta.Macros do + @moduledoc """ + general fields for each folder meta + + e.g: + field(:has_post, :boolean, default: false) + field(:post_count, :integer, default: 0) + field(:has_job, :boolean, default: false) + field(:job_count, :integer, default: 0) + field(:has_repo, :boolean, default: false) + field(:repo_count, :integer, default: 0) + """ + alias GroupherServer.Accounts.CollectFolder + + @supported_threads CollectFolder.supported_threads() + + defmacro threads_fields() do + @supported_threads + |> Enum.map(fn thread -> + quote do + field(unquote(:"has_#{thread}"), :boolean, default: false) + field(unquote(:"#{thread}_count"), :integer, default: 0) + end + end) + end +end + +defmodule GroupherServer.Accounts.Embeds.CollectFolderMeta do + @moduledoc """ + general article meta info for article-like content, like @supported_threads + """ + use Ecto.Schema + import Ecto.Changeset + import GroupherServer.CMS.Embeds.CollectFolderMeta.Macros + + alias GroupherServer.Accounts.CollectFolder + + @supported_threads CollectFolder.supported_threads() + + @optional_fields Enum.map(@supported_threads, &:"#{&1}_count") ++ + Enum.map(@supported_threads, &:"has_#{&1}") + + def default_meta() do + @supported_threads + |> Enum.reduce([], fn thread, acc -> acc ++ ["#{thread}_count": 0, "has_#{thread}": false] end) + |> Enum.into(%{}) + end + + embedded_schema do + threads_fields() + end + + def changeset(struct, params) do + struct |> cast(params, @optional_fields) + end +end diff --git a/lib/groupher_server/accounts/favorite_category.ex b/lib/groupher_server/accounts/favorite_category.ex deleted file mode 100644 index 0eb4a16d1..000000000 --- a/lib/groupher_server/accounts/favorite_category.ex +++ /dev/null @@ -1,44 +0,0 @@ -defmodule GroupherServer.Accounts.FavoriteCategory do - @moduledoc false - alias __MODULE__ - - use Ecto.Schema - import Ecto.Changeset - alias GroupherServer.Accounts.User - - @required_fields ~w(user_id title)a - @optional_fields ~w(index total_count private desc last_updated)a - - @type t :: %FavoriteCategory{} - schema "favorite_categories" do - belongs_to(:user, User, foreign_key: :user_id) - # has_many(:posts, ...) - - field(:title, :string) - field(:desc, :string) - field(:index, :integer) - field(:total_count, :integer, default: 0) - field(:private, :boolean, default: false) - # last time when add/delete items in category - field(:last_updated, :utc_datetime) - - timestamps(type: :utc_datetime) - end - - @doc false - def changeset(%FavoriteCategory{} = favorite_category, attrs) do - favorite_category - |> cast(attrs, @optional_fields ++ @required_fields) - |> validate_required(@required_fields) - |> validate_length(:title, min: 1) - |> foreign_key_constraint(:user_id) - end - - @doc false - def update_changeset(%FavoriteCategory{} = favorite_category, attrs) do - favorite_category - |> cast(attrs, @optional_fields ++ @required_fields) - |> validate_length(:title, min: 1) - |> foreign_key_constraint(:user_id) - end -end diff --git a/lib/groupher_server/accounts/user.ex b/lib/groupher_server/accounts/user.ex index 8fb588071..7d62a0713 100644 --- a/lib/groupher_server/accounts/user.ex +++ b/lib/groupher_server/accounts/user.ex @@ -11,7 +11,7 @@ defmodule GroupherServer.Accounts.User do Achievement, Customization, EducationBackground, - FavoriteCategory, + CollectFolder, GithubUser, Purchase, UserFollower, @@ -54,16 +54,7 @@ defmodule GroupherServer.Accounts.User do has_many(:subscribed_communities, {"communities_subscribers", CMS.CommunitySubscriber}) - # stared contents - has_many(:stared_posts, {"posts_stars", CMS.PostStar}) - has_many(:stared_jobs, {"jobs_stars", CMS.JobStar}) - - # favorited contents - has_many(:favorited_posts, {"posts_favorites", CMS.PostFavorite}) - has_many(:favorited_jobs, {"jobs_favorites", CMS.JobFavorite}) - has_many(:favorited_repos, {"repos_favorites", CMS.RepoFavorite}) - - has_many(:favorite_categories, {"favorite_categories", FavoriteCategory}) + has_many(:collect_folder, {"collect_folders", CollectFolder}) # field(:sponsor_member, :boolean) # field(:paid_member, :boolean) diff --git a/lib/groupher_server/accounts/utils/loader.ex b/lib/groupher_server/accounts/utils/loader.ex index acbabf207..01f2c9346 100644 --- a/lib/groupher_server/accounts/utils/loader.ex +++ b/lib/groupher_server/accounts/utils/loader.ex @@ -41,28 +41,6 @@ defmodule GroupherServer.Accounts.Utils.Loader do UserFollower |> where([f], f.follower_id == ^cur_user.id) end - # stared contents count - def query({"posts_stars", CMS.PostStar}, %{count: _}) do - CMS.PostStar |> count_contents - end - - def query({"jobs_stars", CMS.JobStar}, %{count: _}) do - CMS.JobStar |> count_contents - end - - # favorited contents count - def query({"posts_favorites", CMS.PostFavorite}, %{count: _}) do - CMS.PostFavorite |> count_contents - end - - def query({"jobs_favorites", CMS.JobFavorite}, %{count: _}) do - CMS.JobFavorite |> count_contents - end - - def query({"repos_favorites", CMS.RepoFavorite}, %{count: _}) do - CMS.RepoFavorite |> count_contents - end - def query(queryable, _args), do: queryable defp count_contents(queryable) do diff --git a/lib/groupher_server/application.ex b/lib/groupher_server/application.ex index 53d9f590b..b7fccf212 100644 --- a/lib/groupher_server/application.ex +++ b/lib/groupher_server/application.ex @@ -9,6 +9,8 @@ defmodule GroupherServer.Application do import Supervisor.Spec import Cachex.Spec + alias Helper.Cache + # Define workers and child supervisors to be supervised children = [ # Start the PubSub system @@ -19,22 +21,9 @@ defmodule GroupherServer.Application do supervisor(GroupherServerWeb.Endpoint, []), # Start your own worker by calling: GroupherServer.Worker.start_link(arg1, arg2, arg3) # worker(GroupherServer.Worker, [arg1, arg2, arg3]), - worker(Cachex, [ - :site_cache, - [ - limit: - limit( - # the limit provided - size: 5000, - # the policy to use for eviction - policy: Cachex.Policy.LRW, - # how much to reclaim on bound expiration - reclaim: 0.1, - # options to pass to the policy - options: [] - ) - ] - ]), + worker(Cachex, [:common, Cache.config(:common)], id: :common), + worker(Cachex, [:user_login, Cache.config(:user_login)], id: :user_login), + # worker(Helper.Scheduler, []), {Rihanna.Supervisor, [postgrex: GroupherServer.Repo.config()]} ] diff --git a/lib/groupher_server/cms/article_collect.ex b/lib/groupher_server/cms/article_collect.ex new file mode 100644 index 000000000..5f5c2dc07 --- /dev/null +++ b/lib/groupher_server/cms/article_collect.ex @@ -0,0 +1,41 @@ +defmodule GroupherServer.CMS.ArticleCollect do + @moduledoc false + alias __MODULE__ + + use Ecto.Schema + import Ecto.Changeset + + alias GroupherServer.{Accounts, CMS} + + alias Accounts.{User, CollectFolder} + alias CMS.{Post, Job, Repo} + + @required_fields ~w(user_id)a + @optional_fields ~w(thread post_id job_id repo_id)a + + @type t :: %ArticleCollect{} + schema "article_collects" do + field(:thread, :string) + + belongs_to(:user, User, foreign_key: :user_id) + belongs_to(:post, Post, foreign_key: :post_id) + belongs_to(:job, Job, foreign_key: :job_id) + belongs_to(:repo, Repo, foreign_key: :repo_id) + + embeds_many(:collect_folders, CollectFolder, on_replace: :delete) + + timestamps(type: :utc_datetime) + end + + @doc false + def changeset(%ArticleCollect{} = article_collect, attrs) do + article_collect + |> cast(attrs, @optional_fields ++ @required_fields) + |> validate_required(@required_fields) + |> cast_embed(:collect_folders, with: &CollectFolder.changeset/2) + |> foreign_key_constraint(:user_id) + |> foreign_key_constraint(:post_id) + |> foreign_key_constraint(:job_id) + |> foreign_key_constraint(:repo_id) + end +end diff --git a/lib/groupher_server/cms/article_upvote.ex b/lib/groupher_server/cms/article_upvote.ex new file mode 100644 index 000000000..8a93d1a47 --- /dev/null +++ b/lib/groupher_server/cms/article_upvote.ex @@ -0,0 +1,39 @@ +defmodule GroupherServer.CMS.ArticleUpvote do + @moduledoc false + alias __MODULE__ + + use Ecto.Schema + import Ecto.Changeset + + alias GroupherServer.{Accounts, CMS} + + alias Accounts.User + alias CMS.{Post, Job, Repo} + + @required_fields ~w(user_id)a + @optional_fields ~w(thread post_id job_id repo_id)a + + @type t :: %ArticleUpvote{} + schema "article_upvotes" do + # for user-center to filter + field(:thread, :string) + + belongs_to(:user, User, foreign_key: :user_id) + belongs_to(:post, Post, foreign_key: :post_id) + belongs_to(:job, Job, foreign_key: :job_id) + belongs_to(:repo, Repo, foreign_key: :repo_id) + + timestamps(type: :utc_datetime) + end + + @doc false + def changeset(%ArticleUpvote{} = article_upvote, attrs) do + article_upvote + |> cast(attrs, @optional_fields ++ @required_fields) + |> validate_required(@required_fields) + |> foreign_key_constraint(:user_id) + |> foreign_key_constraint(:post_id) + |> foreign_key_constraint(:job_id) + |> foreign_key_constraint(:repo_id) + end +end diff --git a/lib/groupher_server/cms/cms.ex b/lib/groupher_server/cms/cms.ex index 1b7b1218f..a063bac7c 100644 --- a/lib/groupher_server/cms/cms.ex +++ b/lib/groupher_server/cms/cms.ex @@ -12,7 +12,6 @@ defmodule GroupherServer.CMS do ArticleCURD, ArticleOperation, ArticleReaction, - FavoritedContents, ArticleComment, CommentCURD, CommunitySync, @@ -82,7 +81,21 @@ defmodule GroupherServer.CMS do defdelegate reaction(thread, react, content_id, user), to: ArticleReaction defdelegate undo_reaction(thread, react, content_id, user), to: ArticleReaction - defdelegate favorited_category(thread, content_id, user), to: FavoritedContents + defdelegate upvote_article(thread, article_id, user), to: ArticleReaction + defdelegate undo_upvote_article(thread, article_id, user), to: ArticleReaction + + defdelegate upvoted_users(thread, article_id, filter), to: ArticleReaction + + defdelegate collect_article(thread, article_id, user), to: ArticleReaction + defdelegate collect_article_ifneed(thread, article_id, user), to: ArticleReaction + + defdelegate undo_collect_article(thread, article_id, user), to: ArticleReaction + defdelegate undo_collect_article_ifneed(thread, article_id, user), to: ArticleReaction + defdelegate collected_users(thread, article_id, filter), to: ArticleReaction + + defdelegate set_collect_folder(collect, folder), to: ArticleReaction + defdelegate undo_set_collect_folder(collect, folder), to: ArticleReaction + # ArticleOperation # >> set flag on article, like: pin / unpin article defdelegate set_community_flags(community_info, queryable, attrs), to: ArticleOperation @@ -94,8 +107,6 @@ defmodule GroupherServer.CMS do # >> tag: set / unset defdelegate set_tag(thread, tag, content_id), to: ArticleOperation defdelegate unset_tag(thread, tag, content_id), to: ArticleOperation - defdelegate set_refined_tag(community, thread, content_id), to: ArticleOperation - defdelegate unset_refined_tag(community, thread, content_id), to: ArticleOperation # >> community: set / unset defdelegate set_community(community, thread, content_id), to: ArticleOperation defdelegate unset_community(community, thread, content_id), to: ArticleOperation @@ -142,11 +153,6 @@ defmodule GroupherServer.CMS do # report defdelegate create_report(type, content_id, args, user), to: AbuseReport - # Comment Reaction - # >> like / undo like - defdelegate like_comment(thread, comment, user), to: CommentReaction - defdelegate undo_like_comment(thread, comment, user), to: CommentReaction - # Passport CURD defdelegate stamp_passport(rules, user), to: PassportCURD defdelegate erase_passport(rules, user), to: PassportCURD diff --git a/lib/groupher_server/cms/community.ex b/lib/groupher_server/cms/community.ex index 4ee423521..f7a830b87 100644 --- a/lib/groupher_server/cms/community.ex +++ b/lib/groupher_server/cms/community.ex @@ -99,7 +99,6 @@ defmodule GroupherServer.CMS.Community do |> unique_constraint(:aka, name: :communities_aka_index) # |> foreign_key_constraint(:communities_author_fkey) - # |> unique_constraint(:user_id, name: :posts_favorites_user_id_post_id_index) end @doc false @@ -114,6 +113,5 @@ defmodule GroupherServer.CMS.Community do |> unique_constraint(:aka, name: :communities_aka_index) # |> foreign_key_constraint(:communities_author_fkey) - # |> unique_constraint(:user_id, name: :posts_favorites_user_id_post_id_index) end end diff --git a/lib/groupher_server/cms/delegates/article_comment.ex b/lib/groupher_server/cms/delegates/article_comment.ex index 7d4fe6534..7aec3f525 100644 --- a/lib/groupher_server/cms/delegates/article_comment.ex +++ b/lib/groupher_server/cms/delegates/article_comment.ex @@ -496,12 +496,11 @@ defmodule GroupherServer.CMS.Delegate.ArticleComment do cur_count = Repo.aggregate(count_query, :count) + # dec 是 comment 还没有删除的时候的操作,和 inc 不同 + # 因为 dec 操作如果放在 delete 后面,那么 update 会失败 case opt do - :inc -> - ORM.update(article, %{article_comments_count: cur_count - 1 + 1}) - - :dec -> - ORM.update(article, %{article_comments_count: Enum.max([0, cur_count - 1])}) + :inc -> ORM.update(article, %{article_comments_count: cur_count}) + :dec -> ORM.update(article, %{article_comments_count: Enum.max([1, cur_count]) - 1}) end end end diff --git a/lib/groupher_server/cms/delegates/article_curd.ex b/lib/groupher_server/cms/delegates/article_curd.ex index 01b272890..f36ad79f5 100644 --- a/lib/groupher_server/cms/delegates/article_curd.ex +++ b/lib/groupher_server/cms/delegates/article_curd.ex @@ -140,26 +140,6 @@ defmodule GroupherServer.CMS.Delegate.ArticleCURD do |> update_content_result() end - @doc """ - get CMS contents - post's favorites/stars/comments ... - ... - jobs's favorites/stars/comments ... - - with or without page info - """ - def reaction_users(thread, react, id, %{page: page, size: size} = filters) do - with {:ok, action} <- match_action(thread, react), - {:ok, where} <- dynamic_where(thread, id) do - # common_filter(action.reactor) - action.reactor - |> where(^where) - |> QueryBuilder.load_inner_users(filters) - |> ORM.paginater(~m(page size)a) - |> done() - end - end - @spec ensure_author_exists(User.t()) :: {:ok, User.t()} def ensure_author_exists(%User{} = user) do # unique_constraint: avoid race conditions, make sure user_id unique diff --git a/lib/groupher_server/cms/delegates/article_reaction.ex b/lib/groupher_server/cms/delegates/article_reaction.ex index fe9f02325..190dc1234 100644 --- a/lib/groupher_server/cms/delegates/article_reaction.ex +++ b/lib/groupher_server/cms/delegates/article_reaction.ex @@ -1,114 +1,211 @@ defmodule GroupherServer.CMS.Delegate.ArticleReaction do @moduledoc """ - reaction[favorite, star, watch ...] on article [post, job...] + reaction[upvote, collect, watch ...] on article [post, job...] """ import Helper.Utils, only: [done: 1, done: 2] - import GroupherServer.CMS.Utils.Matcher + + import GroupherServer.CMS.Utils.Matcher2 + import GroupherServer.CMS.Utils.Matcher, only: [match_action: 2] import Ecto.Query, warn: false import Helper.ErrorCode + import ShortMaps - alias Helper.ORM - alias GroupherServer.{Accounts, Repo} + alias Helper.{ORM, QueryBuilder} + alias GroupherServer.{Accounts, CMS, Repo} alias Accounts.User + alias CMS.{ArticleUpvote, ArticleCollect} + alias Ecto.Multi - @doc """ - favorite / star / watch CMS contents like post / tuts ... - """ - def reaction(thread, react, content_id, %User{id: user_id}) do - with {:ok, action} <- match_action(thread, react), - {:ok, content} <- ORM.find(action.target, content_id, preload: [author: :user]), - {:ok, user} <- ORM.find(Accounts.User, user_id) do + def upvoted_users(thread, article_id, filter) do + load_reaction_users(ArticleUpvote, thread, article_id, filter) + end + + def collected_users(thread, article_id, filter) do + load_reaction_users(ArticleCollect, thread, article_id, filter) + end + + defp load_reaction_users(schema, thread, article_id, %{page: page, size: size} = filter) do + with {:ok, info} <- match(thread) do + schema + |> where([u], field(u, ^info.foreign_key) == ^article_id) + |> QueryBuilder.load_inner_users(filter) + |> ORM.paginater(~m(page size)a) + |> done() + end + end + + def collect_article(thread, article_id, %User{id: user_id}) do + with {:ok, info} <- match(thread), + {:ok, article} <- ORM.find(info.model, article_id, preload: [author: :user]) do Multi.new() - |> Multi.run(:create_reaction_record, fn _, _ -> - create_reaction_record(action, user, thread, content) + |> Multi.run(:inc_author_achieve, fn _, _ -> + Accounts.achieve(article.author.user, :inc, :collect) end) - |> Multi.run(:add_achievement, fn _, _ -> - achiever_id = content.author.user_id - Accounts.achieve(%User{id: achiever_id}, :add, react) + |> Multi.run(:inc_article_collects_count, fn _, _ -> + update_article_upvotes_count(info, article, :collects_count, :inc) + end) + |> Multi.run(:create_collect, fn _, _ -> + thread_upcase = thread |> to_string |> String.upcase() + args = Map.put(%{user_id: user_id, thread: thread_upcase}, info.foreign_key, article.id) + + ORM.create(ArticleCollect, args) end) |> Repo.transaction() |> reaction_result() end end - defp reaction_result({:ok, %{create_reaction_record: result}}), do: result |> done() + # 用于在收藏时,用户添加文章到不同的收藏夹中的情况 + # 如果是同一篇文章,只创建一次,collect_article 不创建记录,只是后续设置不同的收藏夹即可 + # 如果是第一次收藏,那么才创建文章收藏记录 + # 避免因为同一篇文章在不同收藏夹内造成的统计和用户成就系统的混乱 + def collect_article_ifneed(thread, article_id, %User{id: user_id} = user) do + with findby_args <- collection_findby_args(thread, article_id, user_id) do + already_collected = ORM.find_by(ArticleCollect, findby_args) + + case already_collected do + {:ok, article_collect} -> {:ok, article_collect} + {:error, _} -> collect_article(thread, article_id, user) + end + end + end - defp reaction_result({:error, :create_reaction_record, %Ecto.Changeset{} = result, _steps}) do - {:error, result} + def undo_collect_article(thread, article_id, %User{id: user_id}) do + with {:ok, info} <- match(thread), + {:ok, article} <- ORM.find(info.model, article_id, preload: [author: :user]) do + Multi.new() + |> Multi.run(:dec_author_achieve, fn _, _ -> + Accounts.achieve(article.author.user, :dec, :collect) + end) + |> Multi.run(:inc_article_collects_count, fn _, _ -> + update_article_upvotes_count(info, article, :collects_count, :dec) + end) + |> Multi.run(:undo_collect, fn _, _ -> + args = Map.put(%{user_id: user_id}, info.foreign_key, article.id) + + ORM.findby_delete(ArticleCollect, args) + end) + |> Repo.transaction() + |> reaction_result() + end end - defp reaction_result({:error, :create_reaction_record, _result, _steps}) do - {:error, [message: "create reaction fails", code: ecode(:react_fails)]} + def undo_collect_article_ifneed(thread, article_id, %User{id: user_id} = user) do + with findby_args <- collection_findby_args(thread, article_id, user_id), + {:ok, article_collect} = ORM.find_by(ArticleCollect, findby_args) do + case article_collect.collect_folders |> length <= 1 do + true -> undo_collect_article(thread, article_id, user) + false -> {:ok, article_collect} + end + end end - defp reaction_result({:error, :add_achievement, _result, _steps}), - do: {:error, [message: "achieve fails", code: ecode(:react_fails)]} + defp collection_findby_args(thread, article_id, user_id) do + with {:ok, info} <- match(thread) do + thread_upcase = thread |> to_string |> String.upcase() + %{thread: thread_upcase, user_id: user_id} |> Map.put(info.foreign_key, article_id) + end + end - defp create_reaction_record(action, %User{id: user_id}, thread, content) do - attrs = %{} |> Map.put("user_id", user_id) |> Map.put("#{thread}_id", content.id) + def set_collect_folder(%ArticleCollect{} = collect, folder) do + collect_folders = (collect.collect_folders ++ [folder]) |> Enum.uniq() - action.reactor - |> ORM.create(attrs) - |> done(with: content) + collect + |> Ecto.Changeset.change() + |> Ecto.Changeset.put_embed(:collect_folders, collect_folders) + |> Repo.update() end - # ------ - @doc """ - unfavorite / unstar / unwatch CMS contents like post / tuts ... - """ - def undo_reaction(thread, react, content_id, %User{id: user_id}) do - with {:ok, action} <- match_action(thread, react), - {:ok, content} <- ORM.find(action.target, content_id, preload: [author: :user]), - {:ok, user} <- ORM.find(Accounts.User, user_id) do + def undo_set_collect_folder(%ArticleCollect{} = collect, folder) do + collect_folders = Enum.reject(collect.collect_folders, &(&1.id == folder.id)) + + case collect_folders do + # means collect already delete + [] -> + {:ok, :pass} + + _ -> + collect + |> Ecto.Changeset.change() + |> Ecto.Changeset.put_embed(:collect_folders, collect_folders) + |> Repo.update() + end + end + + @doc "upvote to a article-like content" + def upvote_article(thread, article_id, %User{id: user_id}) do + with {:ok, info} <- match(thread), + {:ok, article} <- ORM.find(info.model, article_id, preload: [author: :user]) do Multi.new() - |> Multi.run(:delete_reaction_record, fn _, _ -> - delete_reaction_record(action, user, thread, content) + |> Multi.run(:inc_article_upvotes_count, fn _, _ -> + update_article_upvotes_count(info, article, :upvotes_count, :inc) end) - |> Multi.run(:minus_achievement, fn _, _ -> - achiever_id = content.author.user_id - Accounts.achieve(%User{id: achiever_id}, :minus, react) + |> Multi.run(:add_achievement, fn _, _ -> + achiever_id = article.author.user_id + Accounts.achieve(%User{id: achiever_id}, :inc, :upvote) + end) + |> Multi.run(:create_upvote, fn _, _ -> + thread_upcase = thread |> to_string |> String.upcase() + args = Map.put(%{user_id: user_id, thread: thread_upcase}, info.foreign_key, article.id) + + with {:ok, _} <- ORM.create(ArticleUpvote, args) do + ORM.find(info.model, article.id) + end end) |> Repo.transaction() - |> undo_reaction_result() + |> reaction_result() end end - defp undo_reaction_result({:ok, %{delete_reaction_record: result}}), do: result |> done() + @doc "upvote to a article-like content" + def undo_upvote_article(thread, article_id, %User{id: user_id}) do + with {:ok, info} <- match(thread), + {:ok, article} <- ORM.find(info.model, article_id) do + Multi.new() + |> Multi.run(:inc_article_upvotes_count, fn _, _ -> + update_article_upvotes_count(info, article, :upvotes_count, :dec) + end) + |> Multi.run(:undo_upvote, fn _, _ -> + args = Map.put(%{user_id: user_id}, info.foreign_key, article.id) - defp undo_reaction_result({:error, :delete_reaction_record, _result, _steps}) do - {:error, [message: "delete reaction fails", code: ecode(:react_fails)]} + ORM.findby_delete(ArticleUpvote, args) + ORM.find(info.model, article.id) + end) + |> Repo.transaction() + |> reaction_result() + end end - defp undo_reaction_result({:error, :minus_achievement, _result, _steps}), - do: {:error, [message: "achieve fails", code: ecode(:react_fails)]} - - defp delete_reaction_record(action, %User{id: user_id}, thread, content) do - user_where = dynamic([u], u.user_id == ^user_id) - reaction_where = dynamic_reaction_where(thread, content.id, user_where) + defp update_article_upvotes_count(info, article, field, opt) do + schema = + case field do + :upvotes_count -> ArticleUpvote + :collects_count -> ArticleCollect + end - query = from(f in action.reactor, where: ^reaction_where) + count_query = from(u in schema, where: field(u, ^info.foreign_key) == ^article.id) + cur_count = Repo.aggregate(count_query, :count) - case Repo.one(query) do - nil -> - {:error, "record not found"} + case opt do + :inc -> + new_count = Enum.max([0, cur_count]) + ORM.update(article, Map.put(%{}, field, new_count + 1)) - record -> - Repo.delete(record) - {:ok, content} + :dec -> + new_count = Enum.max([1, cur_count]) + ORM.update(article, Map.put(%{}, field, new_count - 1)) end end - defp dynamic_reaction_where(:post, id, user_where) do - dynamic([p], p.post_id == ^id and ^user_where) - end + defp reaction_result({:ok, %{create_upvote: result}}), do: result |> done() + defp reaction_result({:ok, %{undo_upvote: result}}), do: result |> done() - defp dynamic_reaction_where(:job, id, user_where) do - dynamic([p], p.job_id == ^id and ^user_where) - end + defp reaction_result({:ok, %{create_collect: result}}), do: result |> done() + defp reaction_result({:ok, %{undo_collect: result}}), do: result |> done() - defp dynamic_reaction_where(:repo, id, user_where) do - dynamic([p], p.repo_id == ^id and ^user_where) + defp reaction_result({:error, _, result, _steps}) do + {:error, result} end end diff --git a/lib/groupher_server/cms/delegates/comment_reaction.ex b/lib/groupher_server/cms/delegates/comment_reaction.ex deleted file mode 100644 index 92fd2ff48..000000000 --- a/lib/groupher_server/cms/delegates/comment_reaction.ex +++ /dev/null @@ -1,44 +0,0 @@ -defmodule GroupherServer.CMS.Delegate.CommentReaction do - import GroupherServer.CMS.Utils.Matcher - - alias Helper.ORM - alias GroupherServer.Accounts - - def like_comment(thread, comment_id, %Accounts.User{id: user_id}) do - feel_comment(thread, comment_id, user_id, :like) - end - - def undo_like_comment(thread, comment_id, %Accounts.User{id: user_id}) do - undo_feel_comment(thread, comment_id, user_id, :like) - end - - defp merge_thread_comment_id(:post_comment, comment_id), do: %{post_comment_id: comment_id} - defp merge_thread_comment_id(:job_comment, comment_id), do: %{job_comment_id: comment_id} - defp merge_thread_comment_id(:repo_comment, comment_id), do: %{repo_comment_id: comment_id} - - defp feel_comment(thread, comment_id, user_id, feeling) do - with {:ok, action} <- match_action(thread, feeling) do - clause = Map.merge(%{user_id: user_id}, merge_thread_comment_id(thread, comment_id)) - # clause = %{post_comment_id: comment_id, user_id: user_id} - - case ORM.find_by(action.reactor, clause) do - {:ok, _} -> - {:error, "user has #{to_string(feeling)}d this comment"} - - {:error, _} -> - action.reactor |> ORM.create(clause) - - ORM.find(action.target, comment_id) - end - end - end - - defp undo_feel_comment(thread, comment_id, user_id, feeling) do - with {:ok, action} <- match_action(thread, feeling) do - clause = Map.merge(%{user_id: user_id}, merge_thread_comment_id(thread, comment_id)) - # clause = %{post_comment_id: comment_id, user_id: user_id} - ORM.findby_delete!(action.reactor, clause) - ORM.find(action.target, comment_id) - end - end -end diff --git a/lib/groupher_server/cms/delegates/favorited_category.ex b/lib/groupher_server/cms/delegates/favorited_category.ex deleted file mode 100644 index 37d3a1144..000000000 --- a/lib/groupher_server/cms/delegates/favorited_category.ex +++ /dev/null @@ -1,38 +0,0 @@ -defmodule GroupherServer.CMS.Delegate.FavoritedContents do - @moduledoc """ - CURD operation on post/job ... - """ - alias Helper.ORM - - import Ecto.Query, warn: false - alias GroupherServer.Accounts.User - alias GroupherServer.CMS - - def favorited_category(:post, id, %User{id: user_id}) do - CMS.PostFavorite - |> ORM.find_by(post_id: id, user_id: user_id) - |> handle_reault - end - - def favorited_category(:job, id, %User{id: user_id}) do - CMS.JobFavorite - |> ORM.find_by(job_id: id, user_id: user_id) - |> handle_reault - end - - def favorited_category(:repo, id, %User{id: user_id}) do - CMS.RepoFavorite - |> ORM.find_by(repo_id: id, user_id: user_id) - |> handle_reault - end - - defp handle_reault(result) do - case result do - {:ok, content} -> - {:ok, content.category_id} - - _ -> - {:ok, nil} - end - end -end diff --git a/lib/groupher_server/cms/job.ex b/lib/groupher_server/cms/job.ex index 8312702d5..fd5df1042 100644 --- a/lib/groupher_server/cms/job.ex +++ b/lib/groupher_server/cms/job.ex @@ -14,18 +14,18 @@ defmodule GroupherServer.CMS.Job do Embeds, ArticleComment, Community, - JobFavorite, - JobStar, JobViewer, JobCommunityFlag, - Tag + Tag, + ArticleUpvote, + ArticleCollect } alias Helper.HTML @timestamps_opts [type: :utc_datetime_usec] @required_fields ~w(title company company_logo body digest length)a - @optional_fields ~w(origial_community_id desc company_link link_addr copy_right salary exp education field finance scale article_comments_count article_comments_participators_count)a + @optional_fields ~w(origial_community_id desc company_link link_addr copy_right salary exp education field finance scale article_comments_count article_comments_participators_count upvotes_count collects_count)a @type t :: %Job{} schema "cms_jobs" do @@ -59,12 +59,16 @@ defmodule GroupherServer.CMS.Job do field(:is_pinned, :boolean, default: false, virtual: true) field(:trash, :boolean, default_value: false, virtual: true) + has_many(:upvotes, {"article_upvotes", ArticleUpvote}) + field(:upvotes_count, :integer, default: 0) + + has_many(:collects, {"article_collects", ArticleCollect}) + field(:collects_count, :integer, default: 0) + has_many(:article_comments, {"articles_comments", ArticleComment}) field(:article_comments_count, :integer, default: 0) field(:article_comments_participators_count, :integer, default: 0) - has_many(:favorites, {"jobs_favorites", JobFavorite}) - has_many(:stars, {"jobs_stars", JobStar}) has_many(:viewers, {"jobs_viewers", JobViewer}) many_to_many( diff --git a/lib/groupher_server/cms/job_favorite.ex b/lib/groupher_server/cms/job_favorite.ex deleted file mode 100644 index 9c83e7fd9..000000000 --- a/lib/groupher_server/cms/job_favorite.ex +++ /dev/null @@ -1,38 +0,0 @@ -defmodule GroupherServer.CMS.JobFavorite do - @moduledoc false - alias __MODULE__ - - use Ecto.Schema - import Ecto.Changeset - - alias GroupherServer.{Accounts, CMS} - alias CMS.Job - - @required_fields ~w(user_id job_id)a - @optional_fields ~w(category_id)a - - @type t :: %JobFavorite{} - schema "jobs_favorites" do - belongs_to(:user, Accounts.User, foreign_key: :user_id) - belongs_to(:job, Job, foreign_key: :job_id) - - belongs_to(:category, Accounts.FavoriteCategory) - - timestamps(type: :utc_datetime) - end - - @doc false - def changeset(%JobFavorite{} = job_favorite, attrs) do - job_favorite - |> cast(attrs, @optional_fields ++ @required_fields) - |> validate_required(@required_fields) - |> unique_constraint(:user_id, name: :jobs_favorites_user_id_job_id_index) - end - - @doc false - def update_changeset(%JobFavorite{} = job_favorite, attrs) do - job_favorite - |> cast(attrs, @optional_fields ++ @required_fields) - |> unique_constraint(:user_id, name: :jobs_favorites_user_id_job_id_index) - end -end diff --git a/lib/groupher_server/cms/job_star.ex b/lib/groupher_server/cms/job_star.ex deleted file mode 100644 index 9e957d59a..000000000 --- a/lib/groupher_server/cms/job_star.ex +++ /dev/null @@ -1,29 +0,0 @@ -defmodule GroupherServer.CMS.JobStar do - @moduledoc false - alias __MODULE__ - - use Ecto.Schema - import Ecto.Changeset - - alias GroupherServer.{Accounts, CMS} - alias CMS.Job - - @required_fields ~w(user_id job_id)a - - @type t :: %JobStar{} - schema "jobs_stars" do - belongs_to(:user, Accounts.User, foreign_key: :user_id) - belongs_to(:job, Job, foreign_key: :job_id) - - timestamps(type: :utc_datetime) - end - - @doc false - def changeset(%JobStar{} = job_star, attrs) do - # |> unique_constraint(:user_id, name: :favorites_user_id_article_id_index) - job_star - |> cast(attrs, @required_fields) - |> validate_required(@required_fields) - |> unique_constraint(:user_id, name: :jobs_stars_user_id_job_id_index) - end -end diff --git a/lib/groupher_server/cms/post.ex b/lib/groupher_server/cms/post.ex index 8dcd016e2..ec61645a7 100644 --- a/lib/groupher_server/cms/post.ex +++ b/lib/groupher_server/cms/post.ex @@ -17,17 +17,17 @@ defmodule GroupherServer.CMS.Post do Community, PostComment, PostCommunityFlag, - PostFavorite, - PostStar, PostViewer, - Tag + Tag, + ArticleUpvote, + ArticleCollect } alias Helper.HTML @timestamps_opts [type: :utc_datetime_usec] @required_fields ~w(title body digest length)a - @optional_fields ~w(origial_community_id link_addr copy_right link_addr link_icon article_comments_count article_comments_participators_count)a + @optional_fields ~w(origial_community_id link_addr copy_right link_addr link_icon article_comments_count article_comments_participators_count upvotes_count collects_count)a @type t :: %Post{} schema "cms_posts" do @@ -63,8 +63,12 @@ defmodule GroupherServer.CMS.Post do # 评论参与者,只保留最近 5 个 embeds_many(:article_comments_participators, Accounts.User, on_replace: :delete) - has_many(:favorites, {"posts_favorites", PostFavorite}) - has_many(:stars, {"posts_stars", PostStar}) + has_many(:upvotes, {"article_upvotes", ArticleUpvote}) + field(:upvotes_count, :integer, default: 0) + + has_many(:collects, {"article_collects", ArticleCollect}) + field(:collects_count, :integer, default: 0) + has_many(:viewers, {"posts_viewers", PostViewer}) # The keys are inflected from the schema names! # see https://hexdocs.pm/ecto/Ecto.Schema.html diff --git a/lib/groupher_server/cms/post_favorite.ex b/lib/groupher_server/cms/post_favorite.ex deleted file mode 100644 index d80fb8697..000000000 --- a/lib/groupher_server/cms/post_favorite.ex +++ /dev/null @@ -1,38 +0,0 @@ -defmodule GroupherServer.CMS.PostFavorite do - @moduledoc false - alias __MODULE__ - - use Ecto.Schema - import Ecto.Changeset - - alias GroupherServer.{Accounts, CMS} - alias CMS.Post - - @required_fields ~w(user_id post_id)a - @optional_fields ~w(category_id)a - - @type t :: %PostFavorite{} - schema "posts_favorites" do - belongs_to(:user, Accounts.User, foreign_key: :user_id) - belongs_to(:post, Post, foreign_key: :post_id) - - belongs_to(:category, Accounts.FavoriteCategory) - - timestamps(type: :utc_datetime) - end - - @doc false - def changeset(%PostFavorite{} = post_favorite, attrs) do - post_favorite - |> cast(attrs, @optional_fields ++ @required_fields) - |> validate_required(@required_fields) - |> unique_constraint(:user_id, name: :posts_favorites_user_id_post_id_index) - end - - @doc false - def update_changeset(%PostFavorite{} = post_favorite, attrs) do - post_favorite - |> cast(attrs, @optional_fields ++ @required_fields) - |> unique_constraint(:user_id, name: :posts_favorites_user_id_post_id_index) - end -end diff --git a/lib/groupher_server/cms/post_star.ex b/lib/groupher_server/cms/post_star.ex deleted file mode 100644 index aba505065..000000000 --- a/lib/groupher_server/cms/post_star.ex +++ /dev/null @@ -1,29 +0,0 @@ -defmodule GroupherServer.CMS.PostStar do - @moduledoc false - alias __MODULE__ - - use Ecto.Schema - import Ecto.Changeset - - alias GroupherServer.{Accounts, CMS} - alias CMS.Post - - @required_fields ~w(user_id post_id)a - - @type t :: %PostStar{} - schema "posts_stars" do - belongs_to(:user, Accounts.User, foreign_key: :user_id) - belongs_to(:post, Post, foreign_key: :post_id) - - timestamps(type: :utc_datetime) - end - - @doc false - def changeset(%PostStar{} = post_star, attrs) do - # |> unique_constraint(:user_id, name: :favorites_user_id_article_id_index) - post_star - |> cast(attrs, @required_fields) - |> validate_required(@required_fields) - |> unique_constraint(:user_id, name: :posts_stars_user_id_post_id_index) - end -end diff --git a/lib/groupher_server/cms/repo.ex b/lib/groupher_server/cms/repo.ex index 21f47b4e5..fd34f9b37 100644 --- a/lib/groupher_server/cms/repo.ex +++ b/lib/groupher_server/cms/repo.ex @@ -12,17 +12,18 @@ defmodule GroupherServer.CMS.Repo do Embeds, Community, RepoContributor, - RepoFavorite, RepoViewer, RepoLang, RepoCommunityFlag, - Tag + Tag, + ArticleUpvote, + ArticleCollect } alias Helper.HTML @timestamps_opts [type: :utc_datetime_usec] - @required_fields ~w(title owner_name owner_url repo_url desc readme star_count issues_count prs_count fork_count watch_count)a + @required_fields ~w(title owner_name owner_url repo_url desc readme star_count issues_count prs_count fork_count watch_count upvotes_count collects_count)a @optional_fields ~w(origial_community_id last_sync homepage_url release_tag license)a @type t :: %Repo{} @@ -59,9 +60,14 @@ defmodule GroupherServer.CMS.Repo do field(:is_pinned, :boolean, default: false, virtual: true) field(:trash, :boolean, default_value: false) + has_many(:upvotes, {"article_upvotes", ArticleUpvote}) + field(:upvotes_count, :integer, default: 0) + + has_many(:collects, {"article_collects", ArticleCollect}) + field(:collects_count, :integer, default: 0) + field(:last_sync, :utc_datetime) - has_many(:favorites, {"repos_favorites", RepoFavorite}) has_many(:viewers, {"repos_viewers", RepoViewer}) many_to_many( diff --git a/lib/groupher_server/cms/repo_favorite.ex b/lib/groupher_server/cms/repo_favorite.ex deleted file mode 100644 index 3f765fa6f..000000000 --- a/lib/groupher_server/cms/repo_favorite.ex +++ /dev/null @@ -1,38 +0,0 @@ -defmodule GroupherServer.CMS.RepoFavorite do - @moduledoc false - alias __MODULE__ - - use Ecto.Schema - import Ecto.Changeset - - alias GroupherServer.{Accounts, CMS} - alias CMS.Repo - - @required_fields ~w(user_id repo_id)a - @optional_fields ~w(category_id)a - - @type t :: %RepoFavorite{} - schema "repos_favorites" do - belongs_to(:user, Accounts.User, foreign_key: :user_id) - belongs_to(:repo, Repo, foreign_key: :repo_id) - - belongs_to(:category, Accounts.FavoriteCategory) - - timestamps(type: :utc_datetime) - end - - @doc false - def changeset(%RepoFavorite{} = repo_favorite, attrs) do - repo_favorite - |> cast(attrs, @optional_fields ++ @required_fields) - |> validate_required(@required_fields) - |> unique_constraint(:user_id, name: :repos_favorites_user_id_repo_id_index) - end - - @doc false - def update_changeset(%RepoFavorite{} = repo_favorite, attrs) do - repo_favorite - |> cast(attrs, @optional_fields ++ @required_fields) - |> unique_constraint(:user_id, name: :repos_favorites_user_id_repo_id_index) - end -end diff --git a/lib/groupher_server/cms/utils/loader.ex b/lib/groupher_server/cms/utils/loader.ex index 09da93f72..a4946c30b 100644 --- a/lib/groupher_server/cms/utils/loader.ex +++ b/lib/groupher_server/cms/utils/loader.ex @@ -19,16 +19,12 @@ defmodule GroupherServer.CMS.Utils.Loader do PostComment, PostCommentLike, PostCommentReply, - PostFavorite, - PostStar, # JOB Job, JobViewer, - JobFavorite, # JobStar, # Repo, - RepoViewer, - RepoFavorite + RepoViewer } alias Helper.QueryBuilder @@ -108,28 +104,6 @@ defmodule GroupherServer.CMS.Utils.Loader do RepoViewer |> where([pv], pv.user_id == ^cur_user.id) end - @doc """ - handle query: - 1. bacic filter of pagi,when,sort ... - 2. count of the reactions - 3. check is viewer reacted - """ - def query({"posts_favorites", PostFavorite}, args) do - PostFavorite |> QueryBuilder.members_pack(args) - end - - def query({"posts_stars", PostStar}, args) do - PostStar |> QueryBuilder.members_pack(args) - end - - def query({"jobs_favorites", JobFavorite}, args) do - JobFavorite |> QueryBuilder.members_pack(args) - end - - def query({"repos_favorites", RepoFavorite}, args) do - RepoFavorite |> QueryBuilder.members_pack(args) - end - def query({"communities_subscribers", CommunitySubscriber}, args) do CommunitySubscriber |> QueryBuilder.members_pack(args) end diff --git a/lib/groupher_server/cms/utils/matcher.ex b/lib/groupher_server/cms/utils/matcher.ex index 2560b6df6..02c52f14e 100644 --- a/lib/groupher_server/cms/utils/matcher.ex +++ b/lib/groupher_server/cms/utils/matcher.ex @@ -15,11 +15,6 @@ defmodule GroupherServer.CMS.Utils.Matcher do JobViewer, RepoViewer, # reactions - PostFavorite, - JobFavorite, - RepoFavorite, - PostStar, - JobStar, # comments PostComment, # commtnes reaction @@ -39,10 +34,6 @@ defmodule GroupherServer.CMS.Utils.Matcher do def match_action(:post, :self), do: {:ok, %{target: Post, reactor: Post, preload: :author, viewer: PostViewer}} - def match_action(:post, :favorite), - do: {:ok, %{target: Post, reactor: PostFavorite, preload: :user, preload_right: :post}} - - def match_action(:post, :star), do: {:ok, %{target: Post, reactor: PostStar, preload: :user}} def match_action(:post, :tag), do: {:ok, %{target: Post, reactor: Tag}} # NOTE: the tech, radar, share, city thread also use common tag def match_action(:radar, :tag), do: {:ok, %{target: Post, reactor: Tag}} @@ -68,10 +59,6 @@ defmodule GroupherServer.CMS.Utils.Matcher do def match_action(:job, :community), do: {:ok, %{target: Job, reactor: Community, flag: JobCommunityFlag}} - def match_action(:job, :favorite), - do: {:ok, %{target: Job, reactor: JobFavorite, preload: :user}} - - def match_action(:job, :star), do: {:ok, %{target: Job, reactor: JobStar, preload: :user}} def match_action(:job, :tag), do: {:ok, %{target: Job, reactor: Tag}} ######################################### @@ -85,9 +72,6 @@ defmodule GroupherServer.CMS.Utils.Matcher do def match_action(:repo, :tag), do: {:ok, %{target: Repo, reactor: Tag}} - def match_action(:repo, :favorite), - do: {:ok, %{target: Repo, reactor: RepoFavorite, preload: :user}} - # dynamic where query match def dynamic_where(thread, id) do case thread do @@ -100,15 +84,9 @@ defmodule GroupherServer.CMS.Utils.Matcher do :job -> {:ok, dynamic([p], p.job_id == ^id)} - :job_comment -> - {:ok, dynamic([p], p.job_comment_id == ^id)} - :repo -> {:ok, dynamic([p], p.repo_id == ^id)} - :repo_comment -> - {:ok, dynamic([p], p.repo_comment_id == ^id)} - _ -> {:error, 'where is not match'} end diff --git a/lib/groupher_server/statistics/delegates/contribute.ex b/lib/groupher_server/statistics/delegates/contribute.ex index db15d51c0..e7adeaa8f 100644 --- a/lib/groupher_server/statistics/delegates/contribute.ex +++ b/lib/groupher_server/statistics/delegates/contribute.ex @@ -69,12 +69,9 @@ defmodule GroupherServer.Statistics.Delegate.Contribute do def list_contributes_digest(%Community{id: id}) do scope = Cache.get_scope(:community_contributes, id) - case Cache.get(scope) do - {:ok, result} -> - {:ok, result} - - {:error, _} -> - get_contributes_then_cache(%Community{id: id}) + case Cache.get(:common, scope) do + {:ok, result} -> {:ok, result} + {:error, _} -> get_contributes_then_cache(%Community{id: id}) end end @@ -85,7 +82,7 @@ defmodule GroupherServer.Statistics.Delegate.Contribute do %Community{id: id} |> do_get_contributes() |> to_counts_digest(days: @community_contribute_days) - |> done_and_cache(scope, expire: 60_000) + |> done_and_cache(:common, scope, expire_min: 10) end defp update_contribute_record(%UserContribute{} = contribute) do diff --git a/lib/groupher_server_web/middleware/achievement_proof.ex b/lib/groupher_server_web/middleware/achievement_proof.ex index f06e54714..435e05391 100644 --- a/lib/groupher_server_web/middleware/achievement_proof.ex +++ b/lib/groupher_server_web/middleware/achievement_proof.ex @@ -12,8 +12,8 @@ defmodule GroupherServerWeb.Middleware.AchievementProof do def call(%{value: nil} = resolution, _) do value = %{ reputation: 0, - contents_stared_count: 0, - contents_favorited_count: 0, + articles_upvotes_count: 0, + articles_collects_count: 0, donate_member: false, senior_member: false, sponsor_member: false, diff --git a/lib/groupher_server_web/resolvers/accounts_resolver.ex b/lib/groupher_server_web/resolvers/accounts_resolver.ex index 7d1ce3524..549d39b84 100644 --- a/lib/groupher_server_web/resolvers/accounts_resolver.ex +++ b/lib/groupher_server_web/resolvers/accounts_resolver.ex @@ -75,40 +75,6 @@ defmodule GroupherServerWeb.Resolvers.Accounts do {:error, [message: "need login", code: ecode(:account_login)]} end - def list_favorite_categories(_root, %{filter: filter}, %{context: %{cur_user: cur_user}}) do - Accounts.list_favorite_categories(cur_user, %{private: true}, filter) - end - - def list_favorite_categories(_root, %{user_id: user_id, filter: filter}, _info) - when not is_nil(user_id) do - Accounts.list_favorite_categories(%User{id: user_id}, %{private: false}, filter) - end - - # guest user - def list_favorite_categories(_root, _args, _info) do - {:ok, Utils.empty_pagi_data()} - end - - def create_favorite_category(_root, attrs, %{context: %{cur_user: cur_user}}) do - Accounts.create_favorite_category(cur_user, attrs) - end - - def update_favorite_category(_root, %{id: _id} = args, %{context: %{cur_user: cur_user}}) do - Accounts.update_favorite_category(cur_user, args) - end - - def delete_favorite_category(_root, %{id: id}, %{context: %{cur_user: cur_user}}) do - Accounts.delete_favorite_category(cur_user, id) - end - - def set_favorites(_root, ~m(id thread category_id)a, %{context: %{cur_user: cur_user}}) do - Accounts.set_favorites(cur_user, thread, id, category_id) - end - - def unset_favorites(_root, ~m(id thread category_id)a, %{context: %{cur_user: cur_user}}) do - Accounts.unset_favorites(cur_user, thread, id, category_id) - end - def follow(_root, ~m(user_id)a, %{context: %{cur_user: cur_user}}) do Accounts.follow(cur_user, %User{id: user_id}) end @@ -122,6 +88,7 @@ defmodule GroupherServerWeb.Resolvers.Accounts do end def paged_followers(_root, ~m(filter)a, %{context: %{cur_user: cur_user}}) do + # TODO: rename to list_follower_users Accounts.fetch_followers(cur_user, filter) end @@ -130,29 +97,56 @@ defmodule GroupherServerWeb.Resolvers.Accounts do end def paged_followings(_root, ~m(filter)a, %{context: %{cur_user: cur_user}}) do + # TODO: rename to list_following_users Accounts.fetch_followings(cur_user, filter) end - # get favorited contents - def favorited_contents(_root, ~m(user_id category_id filter thread)a, _info) do - Accounts.reacted_contents(thread, :favorite, category_id, filter, %User{id: user_id}) + def paged_upvoted_articles(_root, ~m(user_login filter)a, _info) do + with {:ok, user_id} <- Accounts.get_userid_and_cache(user_login) do + Accounts.list_upvoted_articles(user_id, filter) + end + end + + def create_collect_folder(_root, attrs, %{context: %{cur_user: cur_user}}) do + Accounts.create_collect_folder(attrs, cur_user) + end + + def update_collect_folder(_root, %{id: id} = attrs, _) do + Accounts.update_collect_folder(id, attrs) + end + + def delete_collect_folder(_root, %{id: id}, _) do + Accounts.delete_collect_folder(id) + end + + def add_to_collect(_root, ~m(thread article_id folder_id)a, %{context: %{cur_user: cur_user}}) do + Accounts.add_to_collect(thread, article_id, folder_id, cur_user) end - def favorited_contents(_root, ~m(user_id filter thread)a, _info) do - Accounts.reacted_contents(thread, :favorite, filter, %User{id: user_id}) + def remove_from_collect(_root, ~m(thread article_id folder_id)a, %{ + context: %{cur_user: cur_user} + }) do + Accounts.remove_from_collect(thread, article_id, folder_id, cur_user) end - def favorited_contents(_root, ~m(filter thread)a, %{context: %{cur_user: cur_user}}) do - Accounts.reacted_contents(thread, :favorite, filter, cur_user) + def paged_collect_folders(_root, ~m(user_login filter)a, %{context: %{cur_user: cur_user}}) do + with {:ok, user_id} <- Accounts.get_userid_and_cache(user_login) do + Accounts.list_collect_folders(user_id, filter, cur_user) + end + end + + def paged_collect_folders(_root, ~m(user_login filter)a, _info) do + with {:ok, user_id} <- Accounts.get_userid_and_cache(user_login) do + Accounts.list_collect_folders(user_id, filter) + end end - # gst stared contents - def stared_contents(_root, ~m(user_id filter thread)a, _info) do - Accounts.reacted_contents(thread, :star, filter, %User{id: user_id}) + def paged_collected_articles(_root, ~m(folder_id filter)a, %{context: %{cur_user: cur_user}}) do + Accounts.list_collect_folder_articles(folder_id, filter, cur_user) end - def stared_contents(_root, ~m(filter thread)a, %{context: %{cur_user: cur_user}}) do - Accounts.reacted_contents(thread, :star, filter, cur_user) + def paged_collected_articles(_root, ~m(folder_id filter)a, _info) do + Accounts.list_collect_folder_articles(folder_id, filter) end # published contents diff --git a/lib/groupher_server_web/resolvers/cms_resolver.ex b/lib/groupher_server_web/resolvers/cms_resolver.ex index 5d8f50fd6..e2de5723c 100644 --- a/lib/groupher_server_web/resolvers/cms_resolver.ex +++ b/lib/groupher_server_web/resolvers/cms_resolver.ex @@ -133,20 +133,20 @@ defmodule GroupherServerWeb.Resolvers.CMS do # ####################### # thread reaction .. # ####################### - def reaction(_root, ~m(id thread action)a, %{context: %{cur_user: user}}) do - CMS.reaction(thread, action, id, user) + def upvote_article(_root, ~m(id thread)a, %{context: %{cur_user: user}}) do + CMS.upvote_article(thread, id, user) end - def undo_reaction(_root, ~m(id thread action)a, %{context: %{cur_user: user}}) do - CMS.undo_reaction(thread, action, id, user) + def undo_upvote_article(_root, ~m(id thread)a, %{context: %{cur_user: user}}) do + CMS.undo_upvote_article(thread, id, user) end - def reaction_users(_root, ~m(id action thread filter)a, _info) do - CMS.reaction_users(thread, action, id, filter) + def upvoted_users(_root, ~m(id thread filter)a, _info) do + CMS.upvoted_users(thread, id, filter) end - def favorited_category(root, ~m(thread)a, %{context: %{cur_user: user}}) do - CMS.favorited_category(thread, root.id, user) + def collected_users(_root, ~m(id thread filter)a, _info) do + CMS.collected_users(thread, id, filter) end # ####################### @@ -239,10 +239,6 @@ defmodule GroupherServerWeb.Resolvers.CMS do CMS.unset_tag(thread, %Tag{id: tag_id}, id) end - def unset_refined_tag(_root, ~m(community_id thread id)a, _info) do - CMS.unset_refined_tag(%Community{id: community_id}, thread, id) - end - def get_tags(_root, %{community_id: community_id, all: true}, _info) do CMS.get_tags(%Community{id: community_id}) end @@ -373,14 +369,6 @@ defmodule GroupherServerWeb.Resolvers.CMS do CMS.reply_comment(thread, id, args, user) end - def like_comment(_root, ~m(thread id)a, %{context: %{cur_user: user}}) do - CMS.like_comment(thread, id, user) - end - - def undo_like_comment(_root, ~m(thread id)a, %{context: %{cur_user: user}}) do - CMS.undo_like_comment(thread, id, user) - end - def stamp_passport(_root, ~m(user_id rules)a, %{context: %{cur_user: _user}}) do CMS.stamp_passport(rules, %User{id: user_id}) end diff --git a/lib/groupher_server_web/schema/account/account_mutations.ex b/lib/groupher_server_web/schema/account/account_mutations.ex index 6b7427e12..e2d03e90a 100644 --- a/lib/groupher_server_web/schema/account/account_mutations.ex +++ b/lib/groupher_server_web/schema/account/account_mutations.ex @@ -39,53 +39,53 @@ defmodule GroupherServerWeb.Schema.Account.Mutations do resolve(&R.Accounts.undo_follow/3) end - @desc "create a favorites category" - field :create_favorite_category, :favorites_category do + @desc "create a collect folder" + field :create_collect_folder, :collect_folder do arg(:title, non_null(:string)) arg(:private, :boolean) arg(:desc, :string) middleware(M.Authorize, :login) - resolve(&R.Accounts.create_favorite_category/3) + resolve(&R.Accounts.create_collect_folder/3) end - @desc "update a favorites category" - field :update_favorite_category, :favorites_category do + @desc "update a collect folder" + field :update_collect_folder, :collect_folder do arg(:id, non_null(:id)) arg(:title, :string) arg(:private, :boolean) arg(:desc, :string) middleware(M.Authorize, :login) - resolve(&R.Accounts.update_favorite_category/3) + resolve(&R.Accounts.update_collect_folder/3) end - @desc "delete a favorites category" - field :delete_favorite_category, :done do + @desc "delete a collect folder" + field :delete_collect_folder, :collect_folder do arg(:id, non_null(:id)) middleware(M.Authorize, :login) - resolve(&R.Accounts.delete_favorite_category/3) + resolve(&R.Accounts.delete_collect_folder/3) end - @desc "put content to favorites with category" - field :set_favorites, :favorites_category do - arg(:id, non_null(:id)) - arg(:category_id, non_null(:id)) + @desc "add article into a collect folder" + field :add_to_collect, :collect_folder do + arg(:article_id, non_null(:id)) + arg(:folder_id, non_null(:id)) arg(:thread, :cms_thread, default_value: :post) middleware(M.Authorize, :login) - resolve(&R.Accounts.set_favorites/3) + resolve(&R.Accounts.add_to_collect/3) end - @desc "take out content from favorites category" - field :unset_favorites, :favorites_category do - arg(:id, non_null(:id)) - arg(:category_id, non_null(:id)) + @desc "remove article from a collect folder" + field :remove_from_collect, :collect_folder do + arg(:article_id, non_null(:id)) + arg(:folder_id, non_null(:id)) arg(:thread, :cms_thread, default_value: :post) middleware(M.Authorize, :login) - resolve(&R.Accounts.unset_favorites/3) + resolve(&R.Accounts.remove_from_collect/3) end @desc "set user's customization" diff --git a/lib/groupher_server_web/schema/account/account_queries.ex b/lib/groupher_server_web/schema/account/account_queries.ex index 028342907..b850852c7 100644 --- a/lib/groupher_server_web/schema/account/account_queries.ex +++ b/lib/groupher_server_web/schema/account/account_queries.ex @@ -62,66 +62,30 @@ defmodule GroupherServerWeb.Schema.Account.Queries do resolve(&R.Accounts.paged_followings/3) end - @desc "get favorites categoories" - field :favorite_categories, :paged_favorites_categories do - arg(:user_id, :id) - arg(:filter, non_null(:common_paged_filter)) + @desc "get paged upvoted articles" + field :paged_upvoted_articles, :paged_articles do + arg(:user_login, non_null(:string)) + arg(:filter, :upvoted_articles_filter) - middleware(M.PageSizeProof) - resolve(&R.Accounts.list_favorite_categories/3) + resolve(&R.Accounts.paged_upvoted_articles/3) end - @desc "paged stared posts" - field :stared_posts, :paged_posts do - arg(:user_id, non_null(:id)) - arg(:filter, non_null(:paged_filter)) - arg(:thread, :post_thread, default_value: :post) + @desc "get paged collect folders of a user" + field :paged_collect_folders, :paged_collect_folders do + arg(:user_login, non_null(:string)) + arg(:filter, non_null(:collect_folders_filter)) middleware(M.PageSizeProof) - resolve(&R.Accounts.stared_contents/3) + resolve(&R.Accounts.paged_collect_folders/3) end - @desc "paged stared jobs" - field :stared_jobs, :paged_jobs do - arg(:user_id, non_null(:id)) - arg(:filter, non_null(:paged_filter)) - arg(:thread, :job_thread, default_value: :job) + @desc "get paged collected articles" + field :paged_collected_articles, :paged_articles do + arg(:folder_id, non_null(:id)) + arg(:filter, non_null(:collected_articles_filter)) middleware(M.PageSizeProof) - resolve(&R.Accounts.stared_contents/3) - end - - @desc "get favorited posts" - field :favorited_posts, :paged_posts do - arg(:user_id, non_null(:id)) - arg(:filter, non_null(:paged_filter)) - arg(:category_id, :id) - arg(:thread, :post_thread, default_value: :post) - - middleware(M.PageSizeProof) - resolve(&R.Accounts.favorited_contents/3) - end - - @desc "get favorited jobs" - field :favorited_jobs, :paged_jobs do - arg(:user_id, non_null(:id)) - arg(:filter, non_null(:paged_filter)) - arg(:category_id, :id) - arg(:thread, :job_thread, default_value: :job) - - middleware(M.PageSizeProof) - resolve(&R.Accounts.favorited_contents/3) - end - - @desc "get favorited repos" - field :favorited_repos, :paged_repos do - arg(:user_id, non_null(:id)) - arg(:filter, non_null(:paged_filter)) - arg(:category_id, :id) - arg(:thread, :repo_thread, default_value: :repo) - - middleware(M.PageSizeProof) - resolve(&R.Accounts.favorited_contents/3) + resolve(&R.Accounts.paged_collected_articles/3) end @desc "get paged published posts" @@ -164,26 +128,6 @@ defmodule GroupherServerWeb.Schema.Account.Queries do resolve(&R.Accounts.published_comments/3) end - @desc "get paged published comments on job" - field :published_job_comments, :paged_job_comments do - arg(:user_id, non_null(:id)) - arg(:filter, non_null(:paged_filter)) - arg(:thread, :job_thread, default_value: :job) - - middleware(M.PageSizeProof) - resolve(&R.Accounts.published_comments/3) - end - - @desc "get paged published comments on repo" - field :published_repo_comments, :paged_repo_comments do - arg(:user_id, non_null(:id)) - arg(:filter, non_null(:paged_filter)) - arg(:thread, :repo_thread, default_value: :repo) - - middleware(M.PageSizeProof) - resolve(&R.Accounts.published_comments/3) - end - @desc "paged communities which the user it's the editor" field :editable_communities, :paged_communities do arg(:user_id, :id) diff --git a/lib/groupher_server_web/schema/account/account_types.ex b/lib/groupher_server_web/schema/account/account_types.ex index 3f7b55a77..d03ef22bb 100644 --- a/lib/groupher_server_web/schema/account/account_types.ex +++ b/lib/groupher_server_web/schema/account/account_types.ex @@ -51,14 +51,6 @@ defmodule GroupherServerWeb.Schema.Account.Types do field(:education_backgrounds, list_of(:education_background)) field(:work_backgrounds, list_of(:work_background)) - # field(:favorites_categories, :paged_favorites_category) do - # arg(:filter, non_null(:common_paged_filter)) - - # middleware(M.Authorize, :login) - # middleware(M.PageSizeProof) - # resolve(&R.Accounts.list_favorite_categories/3) - # end - field(:cms_passport_string, :string) do middleware(M.Authorize, :login) resolve(&R.Accounts.get_passport_string/3) @@ -126,91 +118,6 @@ defmodule GroupherServerWeb.Schema.Account.Types do middleware(M.ViewerDidConvert) end - @desc "paged stared posts" - field :stared_posts, :paged_posts do - arg(:filter, non_null(:paged_filter)) - arg(:thread, :post_thread, default_value: :post) - - middleware(M.PageSizeProof) - resolve(&R.Accounts.stared_contents/3) - end - - @desc "paged stared jobs" - field :stared_jobs, :paged_jobs do - arg(:filter, non_null(:paged_filter)) - arg(:thread, :job_thread, default_value: :job) - - middleware(M.PageSizeProof) - resolve(&R.Accounts.stared_contents/3) - end - - @desc "paged favorited posts" - field :favorited_posts, :paged_posts do - arg(:filter, non_null(:paged_filter)) - arg(:thread, :post_thread, default_value: :post) - - middleware(M.PageSizeProof) - resolve(&R.Accounts.favorited_contents/3) - end - - @desc "paged favorited jobs" - field :favorited_jobs, :paged_jobs do - arg(:filter, non_null(:paged_filter)) - arg(:thread, :job_thread, default_value: :job) - - middleware(M.PageSizeProof) - resolve(&R.Accounts.favorited_contents/3) - end - - @desc "paged favorited repos" - field :favorited_repos, :paged_repos do - arg(:filter, non_null(:paged_filter)) - arg(:thread, :repo_thread, default_value: :repo) - - middleware(M.PageSizeProof) - resolve(&R.Accounts.favorited_contents/3) - end - - @desc "total count of stared posts count" - field :stared_posts_count, :integer do - arg(:count, :count_type, default_value: :count) - - resolve(dataloader(Accounts, :stared_posts)) - middleware(M.ConvertToInt) - end - - @desc "total count of stared jobs count" - field :stared_jobs_count, :integer do - arg(:count, :count_type, default_value: :count) - - resolve(dataloader(Accounts, :stared_jobs)) - middleware(M.ConvertToInt) - end - - @desc "total count of favorited posts count" - field :favorited_posts_count, :integer do - arg(:count, :count_type, default_value: :count) - - resolve(dataloader(Accounts, :favorited_posts)) - middleware(M.ConvertToInt) - end - - @desc "total count of favorited jobs count" - field :favorited_jobs_count, :integer do - arg(:count, :count_type, default_value: :count) - - resolve(dataloader(Accounts, :favorited_jobs)) - middleware(M.ConvertToInt) - end - - @desc "total count of favorited videos count" - field :favorited_repos_count, :integer do - arg(:count, :count_type, default_value: :count) - - resolve(dataloader(Accounts, :favorited_repos)) - middleware(M.ConvertToInt) - end - field :contributes, :contribute_map do resolve(&R.Statistics.list_contributes_digest/3) end @@ -298,7 +205,11 @@ defmodule GroupherServerWeb.Schema.Account.Types do social_fields() end - object :favorites_category do + object :collect_folder_meta do + collect_folder_meta_fields() + end + + object :collect_folder do field(:id, :id) field(:title, :string) field(:desc, :string) @@ -308,10 +219,11 @@ defmodule GroupherServerWeb.Schema.Account.Types do field(:last_updated, :datetime) field(:inserted_at, :datetime) field(:updated_at, :datetime) + field(:meta, :collect_folder_meta) end - object :paged_favorites_categories do - field(:entries, list_of(:favorites_category)) + object :paged_collect_folders do + field(:entries, list_of(:collect_folder)) pagination_fields() end @@ -326,8 +238,8 @@ defmodule GroupherServerWeb.Schema.Account.Types do object :achievement do field(:reputation, :integer) # field(:followers_count, :integer) - field(:contents_stared_count, :integer) - field(:contents_favorited_count, :integer) + field(:articles_upvotes_count, :integer) + field(:articles_collects_count, :integer) # field(:contents_watched_count, :integer) field(:source_contribute, :source_contribute) diff --git a/lib/groupher_server_web/schema/cms/cms_misc.ex b/lib/groupher_server_web/schema/cms/cms_misc.ex index 662627c8b..dfee96be6 100644 --- a/lib/groupher_server_web/schema/cms/cms_misc.ex +++ b/lib/groupher_server_web/schema/cms/cms_misc.ex @@ -16,7 +16,6 @@ defmodule GroupherServerWeb.Schema.CMS.Misc do enum(:count_type, do: value(:count)) enum(:viewer_did_type, do: value(:viewer_did)) - # enum(:favorite_action, do: value(:favorite)) enum(:star_action, do: value(:star)) enum(:comment_action, do: value(:comment)) @@ -27,14 +26,14 @@ defmodule GroupherServerWeb.Schema.CMS.Misc do end enum :react_action do - value(:favorite) - value(:star) + value(:collect) + value(:upvote) # value(:watch) end enum :reactable_action do - value(:star) - # value(:favorite) + value(:upvote) + # value(:collect) # value(:watch) end @@ -51,8 +50,6 @@ defmodule GroupherServerWeb.Schema.CMS.Misc do enum :cms_comment do value(:post_comment) - value(:job_comment) - value(:repo_comment) end enum :commentable_thread do @@ -112,12 +109,12 @@ defmodule GroupherServerWeb.Schema.CMS.Misc do enum :sort_enum do value(:most_views) value(:most_updated) - value(:most_favorites) + value(:most_upvotes) value(:most_stars) value(:most_comments) value(:least_views) value(:least_updated) - value(:least_favorites) + value(:least_upvotes) value(:least_stars) value(:least_watched) value(:least_comments) @@ -133,7 +130,7 @@ defmodule GroupherServerWeb.Schema.CMS.Misc do value(:most_views) value(:most_comments) value(:recent_updated) - value(:most_favorites) + value(:most_upvotes) end enum :length_enum do @@ -252,6 +249,24 @@ defmodule GroupherServerWeb.Schema.CMS.Misc do field(:sort, :repo_sort_enum) end + @desc "common filter for upvoted articles" + input_object :upvoted_articles_filter do + field(:thread, :cms_thread) + pagination_args() + end + + @desc "common filter for collect folders" + input_object :collect_folders_filter do + field(:thread, :cms_thread) + pagination_args() + end + + @desc "common filter for collect articles" + input_object :collected_articles_filter do + field(:thread, :cms_thread) + pagination_args() + end + @desc """ cms github repo contribotor """ @@ -290,7 +305,7 @@ defmodule GroupherServerWeb.Schema.CMS.Misc do end @doc """ - only used for reaction result, like: favorite/star/watch ... + only used for reaction result, like: upvote/collect/watch ... """ interface :article do field(:id, :id) diff --git a/lib/groupher_server_web/schema/cms/cms_queries.ex b/lib/groupher_server_web/schema/cms/cms_queries.ex index a9acf2fe1..c89ac01b1 100644 --- a/lib/groupher_server_web/schema/cms/cms_queries.ex +++ b/lib/groupher_server_web/schema/cms/cms_queries.ex @@ -119,6 +119,26 @@ defmodule GroupherServerWeb.Schema.CMS.Queries do resolve(&R.CMS.paged_jobs/3) end + @desc "get paged upvoted users of an article" + field :upvoted_users, :paged_users do + arg(:id, non_null(:id)) + arg(:thread, :cms_thread, default_value: :post) + arg(:filter, non_null(:paged_filter)) + + middleware(M.PageSizeProof) + resolve(&R.CMS.upvoted_users/3) + end + + @desc "get paged upvoted users of an article" + field :collected_users, :paged_users do + arg(:id, non_null(:id)) + arg(:thread, :cms_thread, default_value: :post) + arg(:filter, non_null(:paged_filter)) + + middleware(M.PageSizeProof) + resolve(&R.CMS.collected_users/3) + end + @desc "get paged users of a reaction related to cms content" field :reaction_users, :paged_users do arg(:id, non_null(:id)) diff --git a/lib/groupher_server_web/schema/cms/cms_types.ex b/lib/groupher_server_web/schema/cms/cms_types.ex index b4ba920da..fc69b6c43 100644 --- a/lib/groupher_server_web/schema/cms/cms_types.ex +++ b/lib/groupher_server_web/schema/cms/cms_types.ex @@ -70,9 +70,10 @@ defmodule GroupherServerWeb.Schema.CMS.Types do end has_viewed_field() - # fields for: favorite count, favorited_users, viewer_did_favorite.. - favorite_fields(:post) - star_fields(:post) + # viewer_has_upvoted + # viewer_has_collected + # upvoted_count + # collected_count timestamp_fields() end @@ -112,8 +113,6 @@ defmodule GroupherServerWeb.Schema.CMS.Types do comments_counter_fields(:job) has_viewed_field() - # fields for: favorite count, favorited_users, viewer_did_favorite.. - favorite_fields(:job) timestamp_fields() end @@ -158,8 +157,6 @@ defmodule GroupherServerWeb.Schema.CMS.Types do # comments_count # comments_participators comments_counter_fields(:repo) - # fields for: favorite count, favorited_users, viewer_did_favorite.. - favorite_fields(:repo) timestamp_fields() end @@ -387,14 +384,17 @@ defmodule GroupherServerWeb.Schema.CMS.Types do comments_fields() end - object :post_comment do - comments_fields() - field(:post, :post, resolve: dataloader(CMS, :post)) + object :common_article do + field(:thread, :string) + field(:id, :id) + # field(:body_html, :string) + field(:title, :string) + field(:author, :user, resolve: dataloader(CMS, :author)) end - object :job_comment do + object :post_comment do comments_fields() - field(:job, :job, resolve: dataloader(CMS, :job)) + field(:post, :post, resolve: dataloader(CMS, :post)) end object :repo_comment do @@ -443,16 +443,6 @@ defmodule GroupherServerWeb.Schema.CMS.Types do pagination_fields() end - object :paged_job_comments do - field(:entries, list_of(:job_comment)) - pagination_fields() - end - - object :paged_repo_comments do - field(:entries, list_of(:repo_comment)) - pagination_fields() - end - object :paged_communities do field(:entries, list_of(:community)) pagination_fields() @@ -468,6 +458,11 @@ defmodule GroupherServerWeb.Schema.CMS.Types do pagination_fields() end + object :paged_articles do + field(:entries, list_of(:common_article)) + pagination_fields() + end + @desc "article meta info" object :article_meta do field(:is_edited, :boolean) diff --git a/lib/groupher_server_web/schema/cms/mutations/comment.ex b/lib/groupher_server_web/schema/cms/mutations/comment.ex index 6e3cccd13..d34d15cea 100644 --- a/lib/groupher_server_web/schema/cms/mutations/comment.ex +++ b/lib/groupher_server_web/schema/cms/mutations/comment.ex @@ -75,24 +75,5 @@ defmodule GroupherServerWeb.Schema.CMS.Mutations.Comment do resolve(&R.CMS.reply_comment/3) middleware(M.Statistics.MakeContribute, for: :user) end - - @desc "like a comment" - field :like_comment, :comment do - arg(:thread, non_null(:cms_comment), default_value: :post_comment) - arg(:id, non_null(:id)) - - middleware(M.Authorize, :login) - resolve(&R.CMS.like_comment/3) - end - - @desc "undo like comment" - # field :undo_like_comment, :idlike do - field :undo_like_comment, :comment do - arg(:thread, non_null(:cms_comment), default_value: :post_comment) - arg(:id, non_null(:id)) - - middleware(M.Authorize, :login) - resolve(&R.CMS.undo_like_comment/3) - end end end diff --git a/lib/groupher_server_web/schema/cms/mutations/operation.ex b/lib/groupher_server_web/schema/cms/mutations/operation.ex index fb6104ac5..c8367c805 100644 --- a/lib/groupher_server_web/schema/cms/mutations/operation.ex +++ b/lib/groupher_server_web/schema/cms/mutations/operation.ex @@ -130,24 +130,22 @@ defmodule GroupherServerWeb.Schema.CMS.Mutations.Operation do resolve(&R.CMS.unset_community/3) end - @desc "react on a cms content, except favorite" - field :reaction, :article do + @desc "upvote an article" + field :upvote_article, :article do arg(:id, non_null(:id)) - arg(:thread, non_null(:react_thread)) - arg(:action, non_null(:reactable_action)) + arg(:thread, :cms_thread, default_value: :post) middleware(M.Authorize, :login) - resolve(&R.CMS.reaction/3) + resolve(&R.CMS.upvote_article/3) end - @desc "undoreact on a cms content" - field :undo_reaction, :article do + @desc "undo upvote an article" + field :undo_upvote_article, :article do arg(:id, non_null(:id)) - arg(:thread, non_null(:react_thread)) - arg(:action, non_null(:reactable_action)) + arg(:thread, :cms_thread, default_value: :post) middleware(M.Authorize, :login) - resolve(&R.CMS.undo_reaction/3) + resolve(&R.CMS.undo_upvote_article/3) end end end diff --git a/lib/groupher_server_web/schema/utils/helper.ex b/lib/groupher_server_web/schema/utils/helper.ex index 0c4f6c53f..d11ff9b9a 100644 --- a/lib/groupher_server_web/schema/utils/helper.ex +++ b/lib/groupher_server_web/schema/utils/helper.ex @@ -4,11 +4,12 @@ defmodule GroupherServerWeb.Schema.Utils.Helper do """ import Helper.Utils, only: [get_config: 2] - alias GroupherServer.CMS + alias GroupherServer.{Accounts, CMS} alias CMS.{ArticleComment} @page_size get_config(:general, :page_size) @supported_emotions ArticleComment.supported_emotions() + @supported_collect_folder_threads Accounts.CollectFolder.supported_threads() defmacro timestamp_fields do quote do @@ -101,89 +102,6 @@ defmodule GroupherServerWeb.Schema.Utils.Helper do end end - # fields for: favorite count, favorited_users, viewer_did_favorite.. - defmacro favorite_fields(thread) do - quote do - @doc "if viewer has favroted of this #{unquote(thread)}" - field :viewer_has_favorited, :boolean do - arg(:viewer_did, :viewer_did_type, default_value: :viewer_did) - - middleware(M.Authorize, :login) - middleware(M.PutCurrentUser) - resolve(dataloader(CMS, :favorites)) - middleware(M.ViewerDidConvert) - end - - @doc "favroted count of this #{unquote(thread)}" - field :favorited_count, :integer do - arg(:count, :count_type, default_value: :count) - - arg( - :type, - unquote(String.to_atom("#{to_string(thread)}_thread")), - default_value: unquote(thread) - ) - - resolve(dataloader(CMS, :favorites)) - middleware(M.ConvertToInt) - end - - @doc "list of user who has favroted this #{unquote(thread)}" - field :favorited_users, list_of(:user) do - arg(:filter, :members_filter) - - middleware(M.PageSizeProof) - resolve(dataloader(CMS, :favorites)) - end - - @doc "get viewer's favroted category if seted" - field :favorited_category_id, :id do - arg( - :thread, - unquote(String.to_atom("#{to_string(thread)}_thread")), - default_value: unquote(thread) - ) - - middleware(M.Authorize, :login) - resolve(&R.CMS.favorited_category/3) - end - end - end - - # fields for: star count, users, viewer_did_starred.. - defmacro star_fields(thread) do - quote do - field :viewer_has_starred, :boolean do - arg(:viewer_did, :viewer_did_type, default_value: :viewer_did) - - middleware(M.Authorize, :login) - middleware(M.PutCurrentUser) - resolve(dataloader(CMS, :stars)) - middleware(M.ViewerDidConvert) - end - - field :starred_count, :integer do - arg(:count, :count_type, default_value: :count) - - arg( - :type, - unquote(String.to_atom("#{to_string(thread)}_thread")), - default_value: unquote(thread) - ) - - resolve(dataloader(CMS, :stars)) - middleware(M.ConvertToInt) - end - - field :starred_users, list_of(:user) do - arg(:filter, :members_filter) - - middleware(M.PageSizeProof) - resolve(dataloader(CMS, :stars)) - end - end - end - defmacro comments_fields do quote do field(:id, :id) @@ -271,8 +189,10 @@ defmodule GroupherServerWeb.Schema.Utils.Helper do end end - # general emotions for comments - # NOTE: xxx_user_logins field is not support for gq-endpoint + @doc """ + general emotions for comments + #NOTE: xxx_user_logins field is not support for gq-endpoint + """ defmacro emotion_fields() do @supported_emotions |> Enum.map(fn emotion -> @@ -283,4 +203,17 @@ defmodule GroupherServerWeb.Schema.Utils.Helper do end end) end + + @doc """ + general collect folder meta info + """ + defmacro collect_folder_meta_fields() do + @supported_collect_folder_threads + |> Enum.map(fn thread -> + quote do + field(unquote(:"has_#{thread}"), :boolean) + field(unquote(:"#{thread}_count"), :integer) + end + end) + end end diff --git a/lib/helper/cache.ex b/lib/helper/cache.ex index ef4907cfe..7f5f50857 100644 --- a/lib/helper/cache.ex +++ b/lib/helper/cache.ex @@ -2,19 +2,37 @@ defmodule Helper.Cache do @moduledoc """ memory cache using cachex https://github.com/whitfin/cachex """ + import Cachex.Spec + + def config(:common) do + [ + limit: limit(size: 5000, policy: Cachex.Policy.LRW, reclaim: 0.1), + expiration: expiration(default: :timer.minutes(10)) + ] + end + + @doc """ + cache config for user.login -> user.id, used in accounts resolver + user.id is a linearly increasing integer, kind sensitive, so use user.login instead + """ + def config(:user_login) do + [ + limit: limit(size: 10_000, policy: Cachex.Policy.LRW, reclaim: 0.1), + # expired in one week, it's fine, since user's login and id will never change + expiration: expiration(default: :timer.minutes(10_080)) + ] + end @doc """ ## Example - iex> Helper.Cache.get(a) + iex> Helper.Cache.get(:common, :a) {:ok, "b"} """ - def get(cache_key) do - case Cachex.get(:site_cache, cache_key) do - {:ok, nil} -> - {:error, nil} - - {:ok, result} -> - {:ok, result} + @spec get(Atom.t(), String.t()) :: {:error, nil} | {:ok, any} + def get(pool, key) do + case Cachex.get(pool, key) do + {:ok, nil} -> {:error, nil} + {:ok, result} -> {:ok, result} end end @@ -23,13 +41,18 @@ defmodule Helper.Cache do iex> Helper.Cache.put(a, "x") {:ok, "x"} """ - def put(cache_key, cache_value) do - Cachex.put(:site_cache, cache_key, cache_value) + def put(pool, key, value) do + Cachex.put(pool, key, value) + end + + def put(pool, key, value, expire_sec: expire_sec) do + Cachex.put(pool, key, value) + Cachex.expire(pool, key, :timer.seconds(expire_sec)) end - def put(cache_key, cache_value, expire: expire_time) do - Cachex.put(:site_cache, cache_key, cache_value) - Cachex.expire(:site_cache, cache_key, expire_time) + def put(pool, key, value, expire_min: expire_min) do + Cachex.put(pool, key, value) + Cachex.expire(pool, key, :timer.minutes(expire_min)) end @doc """ @@ -38,7 +61,7 @@ defmodule Helper.Cache do iex> Helper.Cache.clear() {:ok, 1} """ - def clear_all(), do: Cachex.clear(:site_cache) + def clear(pool), do: Cachex.clear(pool) @doc """ cache scope of community contributes digest diff --git a/lib/helper/error_code.ex b/lib/helper/error_code.ex index 0faecb2ac..6e9148b22 100644 --- a/lib/helper/error_code.ex +++ b/lib/helper/error_code.ex @@ -43,6 +43,9 @@ defmodule Helper.ErrorCode do def ecode(:create_comment), do: @comment_base + 1 # article def ecode(:too_much_pinned_article), do: @article_base + 1 + def ecode(:already_collected_in_folder), do: @article_base + 2 + def ecode(:delete_no_empty_collect_folder), do: @article_base + 3 + def ecode(:private_collect_folder), do: @article_base + 4 def ecode, do: @default_base # def ecode(_), do: @default_base diff --git a/lib/helper/orm.ex b/lib/helper/orm.ex index ff7963b7e..8341ed1e0 100644 --- a/lib/helper/orm.ex +++ b/lib/helper/orm.ex @@ -8,6 +8,7 @@ defmodule Helper.ORM do import Helper.ErrorHandler + alias Helper.Types, as: T alias GroupherServer.Repo alias Helper.{QueryBuilder, SpecType} @@ -22,6 +23,23 @@ defmodule Helper.ORM do queryable |> Repo.paginate(page: page, page_size: size) end + # NOTE: should have limit length for list, otherwise it will cause mem issues + @doc "simu paginator in normal list, used for embeds_many etc" + def embeds_paginater(list, %{page: page, size: size} = _filter) when is_list(list) do + chunked_list = Enum.chunk_every(list, size) + + entries = chunked_list |> Enum.at(page - 1) + total_count = list |> length + + %{ + entries: entries, + page_number: page, + page_size: size, + total_count: total_count, + total_pages: chunked_list |> length + } + end + @doc """ wrap Repo.get with preload and result/errer format handle """ @@ -243,4 +261,27 @@ defmodule Helper.ORM do def next_count(queryable) do queryable |> count() |> add() end + + @doc "extract common articles info" + @spec extract_articles(T.paged_data(), [Atom.t()]) :: T.paged_article_common() + def extract_articles(%{entries: entries} = paged_articles, supported_threads) do + paged_articles + |> Map.put(:entries, Enum.map(entries, &extract_article_info(&1, supported_threads))) + end + + defp extract_article_info(reaction, supported_threads) do + thread = Enum.find(supported_threads, &(not is_nil(Map.get(reaction, &1)))) + article = Map.get(reaction, thread) + + export_article_info(thread, article) + end + + defp export_article_info(thread, article) do + %{ + thread: thread, + id: article.id, + title: article.title, + upvotes_count: Map.get(article, :upvotes_count) + } + end end diff --git a/lib/helper/query_builder.ex b/lib/helper/query_builder.ex index bb34c1fd7..0a35ef62a 100644 --- a/lib/helper/query_builder.ex +++ b/lib/helper/query_builder.ex @@ -11,7 +11,7 @@ defmodule Helper.QueryBuilder do bewteen [THREAD] and [REACT] [THREAD]: cms thread, include: Post, Job, Repo ... - [REACT]; favorites, stars, watchs ... + [REACT]; upvotes, stars, watchs ... """ def members_pack(queryable, %{filter: filter}) do queryable |> load_inner_users(filter) diff --git a/lib/helper/scheduler.ex b/lib/helper/scheduler.ex index a8fdc0f16..fd95b4747 100644 --- a/lib/helper/scheduler.ex +++ b/lib/helper/scheduler.ex @@ -11,6 +11,6 @@ defmodule Helper.Scheduler do just in case the cache system broken """ def clear_all_cache do - Cache.clear_all() + # Cache.clear_all() end end diff --git a/lib/helper/types.ex b/lib/helper/types.ex index 1ba1302fa..e9b06c407 100644 --- a/lib/helper/types.ex +++ b/lib/helper/types.ex @@ -7,14 +7,16 @@ defmodule Helper.Types do alias Accounts.User @typedoc """ - Type GraphQL flavor the error format + general response conventions """ - @type gq_error :: {:error, [message: String.t(), code: non_neg_integer()]} + + @type done :: + {:ok, :pass} | {:ok, Map} | {:error, List.t()} | {:error, String} | {:error, Map.t()} @typedoc """ - general response conventions + Type GraphQL flavor the error format """ - @type done :: {:ok, map} | {:error, map} + @type gq_error :: {:error, [message: String.t(), code: non_neg_integer()]} @type id :: non_neg_integer() | String.t() @@ -31,7 +33,7 @@ defmodule Helper.Types do company: nil | String.t() } - @type article_thread :: :post | :job + @type article_thread :: :post | :job | :repo @type comment_thread :: :post | :job @type paged_filter :: %{ @@ -48,9 +50,29 @@ defmodule Helper.Types do total_pages: Integer.t() } + @type paged_data :: %{ + entries: [Map.t()], + page_number: Integer.t(), + page_size: Integer.t(), + total_count: Integer.t(), + total_pages: Integer.t() + } + @type article_common :: %{ - title: String.t() + id: Integer.t(), + thread: Atom.t(), + title: String.t(), + upvotes_count: Integer.t() + } + + @type paged_article_common :: %{ + entries: [article_common], + page_number: Integer.t(), + page_size: Integer.t(), + total_count: Integer.t(), + total_pages: Integer.t() } + @type article_info :: %{ thread: article_thread, article: %{ diff --git a/lib/helper/utils/utils.ex b/lib/helper/utils/utils.ex index 30aca4b0a..7cba6f439 100644 --- a/lib/helper/utils/utils.ex +++ b/lib/helper/utils/utils.ex @@ -67,16 +67,23 @@ defmodule Helper.Utils do def done({n, nil}) when is_integer(n), do: {:ok, %{done: true}} def done(result), do: {:ok, result} - def done_and_cache(result, scope, expire: expire_time) do + def done_and_cache(result, pool, scope, expire_sec: expire_sec) do with {:ok, res} <- done(result) do - Cache.put(scope, res, expire: expire_time) + Cache.put(pool, scope, res, expire_sec: expire_sec) {:ok, res} end end - def done_and_cache(result, scope) do + def done_and_cache(result, pool, scope, expire_min: expire_min) do with {:ok, res} <- done(result) do - Cache.put(scope, res) + Cache.put(pool, scope, res, expire_min: expire_min) + {:ok, res} + end + end + + def done_and_cache(result, pool, scope) do + with {:ok, res} <- done(result) do + Cache.put(pool, scope, res) {:ok, res} end end diff --git a/priv/repo/migrations/20210430021726_create_common_article_upvote.exs b/priv/repo/migrations/20210430021726_create_common_article_upvote.exs new file mode 100644 index 000000000..614250964 --- /dev/null +++ b/priv/repo/migrations/20210430021726_create_common_article_upvote.exs @@ -0,0 +1,24 @@ +defmodule GroupherServer.Repo.Migrations.CreateCommonArticleUpvote do + use Ecto.Migration + + def change do + create table(:article_upvotes) do + add(:user_id, references(:users, on_delete: :delete_all), null: false) + + add(:post_id, references(:cms_posts, on_delete: :delete_all)) + add(:job_id, references(:cms_jobs, on_delete: :delete_all)) + add(:repo_id, references(:cms_repos, on_delete: :delete_all)) + + timestamps() + end + + create(index(:article_upvotes, [:user_id])) + create(index(:article_upvotes, [:post_id])) + create(index(:article_upvotes, [:job_id])) + create(index(:article_upvotes, [:repo_id])) + + create(unique_index(:article_upvotes, [:user_id, :post_id])) + create(unique_index(:article_upvotes, [:user_id, :job_id])) + create(unique_index(:article_upvotes, [:user_id, :repo_id])) + end +end diff --git a/priv/repo/migrations/20210430041030_add_upvotes_count_to_articles.exs b/priv/repo/migrations/20210430041030_add_upvotes_count_to_articles.exs new file mode 100644 index 000000000..ed4ba3356 --- /dev/null +++ b/priv/repo/migrations/20210430041030_add_upvotes_count_to_articles.exs @@ -0,0 +1,17 @@ +defmodule GroupherServer.Repo.Migrations.AddUpvotesCountToArticles do + use Ecto.Migration + + def change do + alter table(:cms_posts) do + add(:upvotes_count, :integer, default: 0) + end + + alter table(:cms_jobs) do + add(:upvotes_count, :integer, default: 0) + end + + alter table(:cms_repos) do + add(:upvotes_count, :integer, default: 0) + end + end +end diff --git a/priv/repo/migrations/20210430070102_create_collect_for_article.exs b/priv/repo/migrations/20210430070102_create_collect_for_article.exs new file mode 100644 index 000000000..13f509e24 --- /dev/null +++ b/priv/repo/migrations/20210430070102_create_collect_for_article.exs @@ -0,0 +1,25 @@ +defmodule GroupherServer.Repo.Migrations.CreateCollectForArticle do + use Ecto.Migration + + def change do + create table(:article_collects) do + add(:thread, :string) + add(:user_id, references(:users, on_delete: :delete_all), null: false) + + add(:post_id, references(:cms_posts, on_delete: :delete_all)) + add(:job_id, references(:cms_jobs, on_delete: :delete_all)) + add(:repo_id, references(:cms_repos, on_delete: :delete_all)) + + timestamps() + end + + create(index(:article_collects, [:user_id])) + create(index(:article_collects, [:post_id])) + create(index(:article_collects, [:job_id])) + create(index(:article_collects, [:repo_id])) + + create(unique_index(:article_collects, [:user_id, :post_id])) + create(unique_index(:article_collects, [:user_id, :job_id])) + create(unique_index(:article_collects, [:user_id, :repo_id])) + end +end diff --git a/priv/repo/migrations/20210430095524_add_collects_count_to_articles.exs b/priv/repo/migrations/20210430095524_add_collects_count_to_articles.exs new file mode 100644 index 000000000..4fa9cc59e --- /dev/null +++ b/priv/repo/migrations/20210430095524_add_collects_count_to_articles.exs @@ -0,0 +1,17 @@ +defmodule GroupherServer.Repo.Migrations.AddCollectsCountToArticles do + use Ecto.Migration + + def change do + alter table(:cms_posts) do + add(:collects_count, :integer, default: 0) + end + + alter table(:cms_jobs) do + add(:collects_count, :integer, default: 0) + end + + alter table(:cms_repos) do + add(:collects_count, :integer, default: 0) + end + end +end diff --git a/priv/repo/migrations/20210501070920_add_thread_to_article_upvote.exs b/priv/repo/migrations/20210501070920_add_thread_to_article_upvote.exs new file mode 100644 index 000000000..590b8edbc --- /dev/null +++ b/priv/repo/migrations/20210501070920_add_thread_to_article_upvote.exs @@ -0,0 +1,9 @@ +defmodule GroupherServer.Repo.Migrations.AddThreadToArticleUpvote do + use Ecto.Migration + + def change do + alter table(:article_upvotes) do + add(:thread, :string) + end + end +end diff --git a/priv/repo/migrations/20210501143223_create_collect_folder.exs b/priv/repo/migrations/20210501143223_create_collect_folder.exs new file mode 100644 index 000000000..e97533b1c --- /dev/null +++ b/priv/repo/migrations/20210501143223_create_collect_folder.exs @@ -0,0 +1,22 @@ +defmodule GroupherServer.Repo.Migrations.CreateCollectFolder do + use Ecto.Migration + + def change do + create table(:collect_folders) do + add(:user_id, references(:users, on_delete: :delete_all, null: false)) + + add(:title, :string) + add(:desc, :string) + add(:total_count, :integer, default: 0) + add(:index, :integer) + add(:private, :boolean, default: false) + + add(:collects, :map) + add(:last_updated, :utc_datetime) + + timestamps() + end + + create(unique_index(:collect_folders, [:user_id, :title])) + end +end diff --git a/priv/repo/migrations/20210502100107_rename_achievement_fields.exs b/priv/repo/migrations/20210502100107_rename_achievement_fields.exs new file mode 100644 index 000000000..77a53dbc2 --- /dev/null +++ b/priv/repo/migrations/20210502100107_rename_achievement_fields.exs @@ -0,0 +1,8 @@ +defmodule GroupherServer.Repo.Migrations.RenameAchievementFields do + use Ecto.Migration + + def change do + rename(table(:user_achievements), :contents_stared_count, to: :articles_upvotes_count) + rename(table(:user_achievements), :contents_favorited_count, to: :articles_collects_count) + end +end diff --git a/priv/repo/migrations/20210502132427_add_meta_to_collect_folder.exs b/priv/repo/migrations/20210502132427_add_meta_to_collect_folder.exs new file mode 100644 index 000000000..b7b30975f --- /dev/null +++ b/priv/repo/migrations/20210502132427_add_meta_to_collect_folder.exs @@ -0,0 +1,9 @@ +defmodule GroupherServer.Repo.Migrations.AddMetaToCollectFolder do + use Ecto.Migration + + def change do + alter table(:collect_folders) do + add(:meta, :map) + end + end +end diff --git a/priv/repo/migrations/20210503080631_add_collect_folder_to_article_collect_record.exs b/priv/repo/migrations/20210503080631_add_collect_folder_to_article_collect_record.exs new file mode 100644 index 000000000..73165b667 --- /dev/null +++ b/priv/repo/migrations/20210503080631_add_collect_folder_to_article_collect_record.exs @@ -0,0 +1,9 @@ +defmodule GroupherServer.Repo.Migrations.AddCollectFolderToArticleCollectRecord do + use Ecto.Migration + + def change do + alter table(:article_collects) do + add(:collect_folders, :map) + end + end +end diff --git a/priv/repo/migrations/20210504042708_remove_old_favorites_staff.exs b/priv/repo/migrations/20210504042708_remove_old_favorites_staff.exs new file mode 100644 index 000000000..f1542dd84 --- /dev/null +++ b/priv/repo/migrations/20210504042708_remove_old_favorites_staff.exs @@ -0,0 +1,14 @@ +defmodule GroupherServer.Repo.Migrations.RemoveOldFavoritesStaff do + use Ecto.Migration + + def change do + drop(table(:posts_favorites)) + drop(table(:jobs_favorites)) + drop(table(:repos_favorites)) + + drop(table(:posts_stars)) + drop(table(:jobs_stars)) + + drop(table(:favorite_categories)) + end +end diff --git a/test/groupher_server/accounts/accounts_test.exs b/test/groupher_server/accounts/accounts_test.exs index c8c93b6c5..7b5d8349e 100644 --- a/test/groupher_server/accounts/accounts_test.exs +++ b/test/groupher_server/accounts/accounts_test.exs @@ -141,8 +141,8 @@ defmodule GroupherServer.Test.Accounts do achievement = created_user.achievement assert achievement.user_id == created_user.id assert achievement.reputation == 0 - assert achievement.contents_favorited_count == 0 - assert achievement.contents_stared_count == 0 + assert achievement.articles_collects_count == 0 + assert achievement.articles_upvotes_count == 0 assert achievement.source_contribute.h5 == false assert achievement.source_contribute.web == false assert achievement.source_contribute.we_app == false diff --git a/test/groupher_server/accounts/achievement_test.exs b/test/groupher_server/accounts/achievement_test.exs index 3f0807c8b..9e9ca3e20 100644 --- a/test/groupher_server/accounts/achievement_test.exs +++ b/test/groupher_server/accounts/achievement_test.exs @@ -7,8 +7,8 @@ defmodule GroupherServer.Test.Accounts.Achievement do alias GroupherServer.Accounts.User @follow_weight get_config(:general, :user_achieve_follow_weight) - @favorite_weight get_config(:general, :user_achieve_favorite_weight) - @star_weight get_config(:general, :user_achieve_star_weight) + @collect_weight get_config(:general, :user_achieve_collect_weight) + @upvote_weight get_config(:general, :user_achieve_upvote_weight) # @watch_weight get_config(:general, :user_achieve_watch_weight) setup do @@ -51,22 +51,23 @@ defmodule GroupherServer.Test.Accounts.Achievement do describe "[Accounts Achievement funtion]" do alias Accounts.Achievement - test "Accounts.achieve should inc / minus achievement by parts", ~m(user)a do - user |> Accounts.achieve(:add, :follow) - user |> Accounts.achieve(:add, :star) - user |> Accounts.achieve(:add, :favorite) + @tag :wip + test "Accounts.achieve should inc / dec achievement by parts", ~m(user)a do + user |> Accounts.achieve(:inc, :follow) + user |> Accounts.achieve(:inc, :upvote) + user |> Accounts.achieve(:inc, :collect) {:ok, achievement} = Achievement |> ORM.find_by(user_id: user.id) assert achievement.followers_count == 1 - assert achievement.contents_stared_count == 1 - assert achievement.contents_favorited_count == 1 + assert achievement.articles_upvotes_count == 1 + assert achievement.articles_collects_count == 1 - reputation = @follow_weight + @star_weight + @favorite_weight + reputation = @follow_weight + @upvote_weight + @collect_weight assert achievement.reputation == reputation - user |> Accounts.achieve(:minus, :follow) - user |> Accounts.achieve(:minus, :star) - user |> Accounts.achieve(:minus, :favorite) + user |> Accounts.achieve(:dec, :follow) + user |> Accounts.achieve(:dec, :upvote) + user |> Accounts.achieve(:dec, :collect) {:ok, achievement} = Achievement |> ORM.find_by(user_id: user.id) assert achievement.followers_count == 0 @@ -75,10 +76,11 @@ defmodule GroupherServer.Test.Accounts.Achievement do assert achievement.reputation == 0 end + @tag :wip test "Accounts.achieve can not minus count < 0", ~m(user)a do - user |> Accounts.achieve(:minus, :follow) - user |> Accounts.achieve(:minus, :star) - user |> Accounts.achieve(:minus, :favorite) + user |> Accounts.achieve(:dec, :follow) + user |> Accounts.achieve(:dec, :upvote) + user |> Accounts.achieve(:dec, :collect) {:ok, achievement} = Achievement |> ORM.find_by(user_id: user.id) assert achievement.followers_count == 0 diff --git a/test/groupher_server/accounts/collect_folder_test.exs b/test/groupher_server/accounts/collect_folder_test.exs new file mode 100644 index 000000000..abb38bbc4 --- /dev/null +++ b/test/groupher_server/accounts/collect_folder_test.exs @@ -0,0 +1,307 @@ +defmodule GroupherServer.Test.Accounts.CollectFolder do + @moduledoc false + + use GroupherServer.TestTools + + alias Helper.ORM + alias GroupherServer.{Accounts, CMS} + + alias Accounts.{Embeds} + + @default_meta Embeds.CollectFolderMeta.default_meta() + + setup do + {:ok, user} = db_insert(:user) + {:ok, user2} = db_insert(:user) + {:ok, post} = db_insert(:post) + {:ok, post2} = db_insert(:post) + + {:ok, job} = db_insert(:job) + + {:ok, ~m(user user2 post post2 job)a} + end + + describe "[collect folder curd]" do + @tag :wip + test "user can create collect folder", ~m(user)a do + folder_title = "test folder" + + args = %{title: folder_title, private: true, collects: []} + {:ok, folder} = Accounts.create_collect_folder(args, user) + + assert folder.title == folder_title + assert folder.user_id == user.id + assert folder.private == true + assert folder.meta |> Map.from_struct() |> Map.delete(:id) == @default_meta + end + + @tag :wip + test "user create dup collect folder fails", ~m(user)a do + {:ok, _folder} = Accounts.create_collect_folder(%{title: "test folder"}, user) + {:error, reason} = Accounts.create_collect_folder(%{title: "test folder"}, user) + + assert reason |> is_error?(:already_exsit) + end + + @tag :wip + test "user can delete a empty collect folder", ~m(user)a do + {:ok, folder} = Accounts.create_collect_folder(%{title: "test folder"}, user) + {:ok, _} = Accounts.delete_collect_folder(folder.id) + + assert {:error, _} = ORM.find(CMS.ArticleCollect, folder.id) + end + + @tag :wip + test "user can not delete a non-empty collect folder", ~m(post user)a do + {:ok, folder} = Accounts.create_collect_folder(%{title: "test folder"}, user) + {:ok, _folder} = Accounts.add_to_collect(:post, post.id, folder.id, user) + + {:error, reason} = Accounts.delete_collect_folder(folder.id) + + assert reason |> is_error?(:delete_no_empty_collect_folder) + end + + @tag :wip + test "user can get public collect-folder list", ~m(user)a do + {:ok, _folder} = Accounts.create_collect_folder(%{title: "test folder"}, user) + {:ok, _folder} = Accounts.create_collect_folder(%{title: "test folder2"}, user) + + {:ok, result} = Accounts.list_collect_folders(user.id, %{page: 1, size: 20}) + + assert result |> is_valid_pagination?(:raw) + assert result.total_count == 2 + end + + @tag :wip + test "user can get public collect-folder list by thread", ~m(user post)a do + {:ok, folder} = Accounts.create_collect_folder(%{title: "test folder"}, user) + {:ok, _folder} = Accounts.create_collect_folder(%{title: "test folder2"}, user) + + {:ok, folder} = Accounts.add_to_collect(:post, post.id, folder.id, user) + + {:ok, result} = Accounts.list_collect_folders(user.id, %{thread: :post, page: 1, size: 20}) + + assert result |> is_valid_pagination?(:raw) + assert result.total_count == 1 + + assert result.entries |> List.first() |> Map.get(:id) == folder.id + end + + @tag :wip + test "user can not get private folder list of other user", ~m(user user2)a do + {:ok, _folder} = + Accounts.create_collect_folder(%{title: "test folder", private: true}, user2) + + {:ok, _folder} = Accounts.create_collect_folder(%{title: "test folder2"}, user2) + + {:ok, result} = Accounts.list_collect_folders(user2.id, %{page: 1, size: 20}, user) + + assert result |> is_valid_pagination?(:raw) + assert result.total_count == 1 + end + + @tag :wip + test "collect creator can get both public and private folder list", ~m(user)a do + {:ok, _folder} = + Accounts.create_collect_folder(%{title: "test folder", private: true}, user) + + {:ok, _folder} = Accounts.create_collect_folder(%{title: "test folder2"}, user) + {:ok, result} = Accounts.list_collect_folders(user.id, %{page: 1, size: 20}, user) + + assert result |> is_valid_pagination?(:raw) + assert result.total_count == 2 + end + + @tag :wip + test "user can update a collect folder", ~m(user)a do + {:ok, folder} = Accounts.create_collect_folder(%{title: "test folder", private: true}, user) + + args = %{title: "new title", desc: "new desc"} + {:ok, updated} = Accounts.update_collect_folder(folder.id, args) + + assert updated.desc == "new desc" + assert updated.title == "new title" + + {:ok, updated} = Accounts.update_collect_folder(folder.id, %{private: true}) + assert updated.private == true + end + end + + describe "[add/remove from collect]" do + @tag :wip + test "can add post to exsit colect-folder", ~m(user post)a do + {:ok, folder} = Accounts.create_collect_folder(%{title: "test folder"}, user) + + {:ok, folder} = Accounts.add_to_collect(:post, post.id, folder.id, user) + + assert folder.total_count == 1 + assert folder.collects |> length == 1 + assert folder.collects |> List.first() |> Map.get(:post_id) == post.id + end + + @tag :wip + test "can not collect some article in one collect-folder", ~m(user post)a do + {:ok, folder} = Accounts.create_collect_folder(%{title: "test folder"}, user) + {:ok, folder} = Accounts.add_to_collect(:post, post.id, folder.id, user) + {:error, reason} = Accounts.add_to_collect(:post, post.id, folder.id, user) + + assert reason |> is_error?(:already_collected_in_folder) + end + + @tag :wip + test "colect-folder should in article_collect's meta info too", ~m(user post)a do + {:ok, folder} = Accounts.create_collect_folder(%{title: "test folder"}, user) + {:ok, folder} = Accounts.add_to_collect(:post, post.id, folder.id, user) + + collect_in_folder = folder.collects |> List.first() + {:ok, article_collect} = ORM.find(CMS.ArticleCollect, collect_in_folder.id) + article_collect_folder = article_collect.collect_folders |> List.first() + assert article_collect_folder.id == folder.id + end + + @tag :wip + test "one article collected in different collect-folder should only have one article-collect record", + ~m(user post)a do + {:ok, folder} = Accounts.create_collect_folder(%{title: "test folder"}, user) + {:ok, folder2} = Accounts.create_collect_folder(%{title: "test folder2"}, user) + + {:ok, _folder} = Accounts.add_to_collect(:post, post.id, folder.id, user) + {:ok, _folder} = Accounts.add_to_collect(:post, post.id, folder2.id, user) + + {:ok, result} = ORM.find_all(CMS.ArticleCollect, %{page: 1, size: 10}) + article_collect = result.entries |> List.first() + + assert article_collect.post_id == post.id + assert result.total_count == 1 + end + + @tag :wip + test "can remove post to exsit colect-folder", ~m(user post post2)a do + {:ok, folder} = Accounts.create_collect_folder(%{title: "test folder"}, user) + {:ok, _folder} = Accounts.add_to_collect(:post, post.id, folder.id, user) + {:ok, _folder} = Accounts.add_to_collect(:post, post2.id, folder.id, user) + + {:ok, _} = Accounts.remove_from_collect(:post, post.id, folder.id, user) + + {:ok, result} = Accounts.list_collect_folder_articles(folder.id, %{page: 1, size: 10}, user) + + assert result.total_count == 1 + assert result.entries |> length == 1 + assert result.entries |> List.first() |> Map.get(:id) == post2.id + end + + @tag :wip + test "can remove post to exsit colect-folder should update article collect meta", + ~m(user post)a do + {:ok, folder} = Accounts.create_collect_folder(%{title: "test folder"}, user) + {:ok, folder2} = Accounts.create_collect_folder(%{title: "test folder2"}, user) + + {:ok, _folder} = Accounts.add_to_collect(:post, post.id, folder.id, user) + {:ok, _folder} = Accounts.add_to_collect(:post, post.id, folder2.id, user) + + {:ok, _} = Accounts.remove_from_collect(:post, post.id, folder.id, user) + + {:ok, result} = ORM.find_all(CMS.ArticleCollect, %{page: 1, size: 10}) + + article_collect = + result.entries |> List.first() |> Map.get(:collect_folders) |> List.first() + + assert article_collect.id == folder2.id + end + + @tag :wip + test "post belongs to other folder should keep article collect record", + ~m(user post)a do + {:ok, folder} = Accounts.create_collect_folder(%{title: "test folder"}, user) + {:ok, folder2} = Accounts.create_collect_folder(%{title: "test folder2"}, user) + + {:ok, _folder} = Accounts.add_to_collect(:post, post.id, folder.id, user) + {:ok, _folder} = Accounts.add_to_collect(:post, post.id, folder2.id, user) + + {:ok, _} = Accounts.remove_from_collect(:post, post.id, folder.id, user) + + {:ok, result} = ORM.find_all(CMS.ArticleCollect, %{page: 1, size: 10}) + article_collect = result.entries |> List.first() + + assert article_collect.collect_folders |> length == 1 + + {:ok, _} = Accounts.remove_from_collect(:post, post.id, folder.id, user) + {:ok, result} = ORM.find_all(CMS.ArticleCollect, %{page: 1, size: 10}) + assert result.total_count == 0 + end + + @tag :wip + test "add post to exsit colect-folder should update meta", ~m(user post post2 job)a do + {:ok, folder} = Accounts.create_collect_folder(%{title: "test folder"}, user) + + {:ok, _folder} = Accounts.add_to_collect(:post, post.id, folder.id, user) + {:ok, _folder} = Accounts.add_to_collect(:post, post2.id, folder.id, user) + {:ok, _folder} = Accounts.add_to_collect(:job, job.id, folder.id, user) + + {:ok, folder} = Accounts.remove_from_collect(:post, post.id, folder.id, user) + + assert folder.meta.has_post + assert folder.meta.has_job + + assert folder.meta.post_count == 1 + assert folder.meta.job_count == 1 + end + + @tag :wip + test "remove post to exsit colect-folder should update meta", ~m(user post post2 job)a do + {:ok, folder} = Accounts.create_collect_folder(%{title: "test folder"}, user) + {:ok, _folder} = Accounts.add_to_collect(:post, post.id, folder.id, user) + {:ok, _folder} = Accounts.add_to_collect(:post, post2.id, folder.id, user) + {:ok, _folder} = Accounts.add_to_collect(:job, job.id, folder.id, user) + + {:ok, folder} = Accounts.remove_from_collect(:post, post.id, folder.id, user) + assert folder.meta.has_post + assert folder.meta.has_job + + {:ok, folder} = Accounts.remove_from_collect(:post, post2.id, folder.id, user) + + assert not folder.meta.has_post + assert folder.meta.has_job + + {:ok, folder} = Accounts.remove_from_collect(:job, job.id, folder.id, user) + + assert not folder.meta.has_post + assert not folder.meta.has_job + end + + @tag :wip + test "can get articles of a collect folder", ~m(user post job)a do + {:ok, folder} = Accounts.create_collect_folder(%{title: "test folder"}, user) + {:ok, _folder} = Accounts.add_to_collect(:post, post.id, folder.id, user) + {:ok, _folder} = Accounts.add_to_collect(:job, job.id, folder.id, user) + + {:ok, result} = Accounts.list_collect_folder_articles(folder.id, %{page: 1, size: 10}, user) + assert result |> is_valid_pagination?(:raw) + + collect_job = result.entries |> List.first() + collect_post = result.entries |> List.last() + + assert collect_job.id == job.id + assert collect_job.title == job.title + + assert collect_post.id == post.id + assert collect_post.title == post.title + end + + @tag :wip + test "can not get articles of a private collect folder if not owner", + ~m(user user2 post job)a do + {:ok, folder} = Accounts.create_collect_folder(%{title: "test folder", private: true}, user) + {:ok, _folder} = Accounts.add_to_collect(:post, post.id, folder.id, user) + {:ok, _folder} = Accounts.add_to_collect(:job, job.id, folder.id, user) + + {:ok, result} = Accounts.list_collect_folder_articles(folder.id, %{page: 1, size: 10}, user) + assert result |> is_valid_pagination?(:raw) + + {:error, reason} = + Accounts.list_collect_folder_articles(folder.id, %{page: 1, size: 10}, user2) + + assert reason |> is_error?(:private_collect_folder) + end + end +end diff --git a/test/groupher_server/accounts/favorite_category_test.exs b/test/groupher_server/accounts/favorite_category_test.exs deleted file mode 100644 index 5e6448bba..000000000 --- a/test/groupher_server/accounts/favorite_category_test.exs +++ /dev/null @@ -1,184 +0,0 @@ -defmodule GroupherServer.Test.Accounts.FavoriteCategory do - use GroupherServer.TestTools - - alias Helper.ORM - alias GroupherServer.{Accounts, CMS} - - alias Accounts.FavoriteCategory - - setup do - {:ok, user} = db_insert(:user) - {:ok, post} = db_insert(:post) - {:ok, post2} = db_insert(:post) - - {:ok, ~m(user post post2)a} - end - - describe "[favorite category curd]" do - test "user can create favorite category", ~m(user)a do - test_category = "test category" - - {:ok, category} = - Accounts.create_favorite_category(user, %{title: test_category, private: true}) - - assert category.title == test_category - assert category.user_id == user.id - assert category.private == true - end - - test "user create dup favorite category fails", ~m(user)a do - {:ok, _category} = Accounts.create_favorite_category(user, %{title: "test category"}) - {:error, reason} = Accounts.create_favorite_category(user, %{title: "test category"}) - - assert reason |> Keyword.get(:code) == ecode(:already_exsit) - end - - test "user can get public categories list", ~m(user)a do - {:ok, _category} = Accounts.create_favorite_category(user, %{title: "test category"}) - {:ok, _category} = Accounts.create_favorite_category(user, %{title: "test category2"}) - - {:ok, result} = - Accounts.list_favorite_categories(user, %{private: false}, %{page: 1, size: 20}) - - assert result |> is_valid_pagination?(:raw) - assert result.total_count == 2 - end - - test "user can update a favorite category", ~m(user)a do - {:ok, category} = Accounts.create_favorite_category(user, %{title: "test category"}) - - {:ok, updated} = - Accounts.update_favorite_category(user, %{id: category.id, desc: "new desc"}) - - assert updated.desc == "new desc" - - {:ok, updated} = Accounts.update_favorite_category(user, %{id: category.id, private: true}) - assert updated.private == true - end - - test "user can delete a favorite category", ~m(user post post2)a do - test_category = "test category" - {:ok, category} = Accounts.create_favorite_category(user, %{title: test_category}) - - {:ok, _post_favorite} = Accounts.set_favorites(user, :post, post.id, category.id) - {:ok, _post_favorite} = Accounts.set_favorites(user, :post, post2.id, category.id) - - assert {:ok, _} = Accounts.delete_favorite_category(user, category.id) - - assert {:error, _} = - CMS.PostFavorite - |> ORM.find_by(%{category_id: category.id, user_id: user.id}) - - assert {:error, _} = FavoriteCategory |> ORM.find(category.id) - end - end - - describe "[favorite category set/unset]" do - test "user can set category to a favorited post", ~m(user post)a do - test_category = "test category" - {:ok, category} = Accounts.create_favorite_category(user, %{title: test_category}) - {:ok, _favorites_category} = Accounts.set_favorites(user, :post, post.id, category.id) - - {:ok, post_favorite} = - CMS.PostFavorite |> ORM.find_by(%{post_id: post.id, user_id: user.id}) - - assert post_favorite.category_id == category.id - end - - test "user can change category to a categoried favorited post", ~m(user post)a do - test_category = "test category" - {:ok, category} = Accounts.create_favorite_category(user, %{title: test_category}) - - {:ok, _} = CMS.reaction(:post, :favorite, post.id, user) - {:ok, _favorite_category} = Accounts.set_favorites(user, :post, post.id, category.id) - - {:ok, post_favorite} = - CMS.PostFavorite |> ORM.find_by(%{post_id: post.id, user_id: user.id}) - - assert post_favorite.category_id == category.id - - test_category2 = "test category2" - {:ok, category} = Accounts.create_favorite_category(user, %{title: test_category2}) - {:ok, _favorite_category} = Accounts.set_favorites(user, :post, post.id, category.id) - - {:ok, post_favorite} = - CMS.PostFavorite |> ORM.find_by(%{post_id: post.id, user_id: user.id}) - - assert post_favorite.category_id == category.id - end - - test "user set a un-created user's category fails", ~m(user post)a do - {:ok, user2} = db_insert(:user) - test_category = "test category" - {:ok, category} = Accounts.create_favorite_category(user2, %{title: test_category}) - - assert {:error, _} = Accounts.set_favorites(user, :post, post.id, category.id) - end - - test "user set to a already categoried post fails", ~m(user post)a do - test_category = "test category" - {:ok, category} = Accounts.create_favorite_category(user, %{title: test_category}) - {:ok, _} = Accounts.set_favorites(user, :post, post.id, category.id) - - {:error, reason} = Accounts.set_favorites(user, :post, post.id, category.id) - assert reason |> Keyword.get(:code) == ecode(:already_did) - end - - test "user can unset category to a favorited post", ~m(user post)a do - test_category = "test category" - {:ok, category} = Accounts.create_favorite_category(user, %{title: test_category}) - {:ok, _post_favorite} = Accounts.set_favorites(user, :post, post.id, category.id) - assert {:ok, _} = CMS.PostFavorite |> ORM.find_by(%{post_id: post.id, user_id: user.id}) - - {:ok, _category} = Accounts.unset_favorites(user, :post, post.id, category.id) - - assert {:error, _} = CMS.PostFavorite |> ORM.find_by(%{post_id: post.id, user_id: user.id}) - end - - test "after unset category the old category count should -1", ~m(user post)a do - test_category = "test category" - test_category2 = "test category2" - {:ok, category} = Accounts.create_favorite_category(user, %{title: test_category}) - {:ok, category2} = Accounts.create_favorite_category(user, %{title: test_category2}) - - {:ok, _post_favorite} = Accounts.set_favorites(user, :post, post.id, category.id) - {:ok, favorete_cat} = Accounts.FavoriteCategory |> ORM.find(category.id) - assert favorete_cat.total_count == 1 - - {:ok, _post_favorite} = Accounts.set_favorites(user, :post, post.id, category2.id) - {:ok, favorete_cat} = Accounts.FavoriteCategory |> ORM.find(category2.id) - assert favorete_cat.total_count == 1 - - {:ok, favorete_cat} = Accounts.FavoriteCategory |> ORM.find(category.id) - assert favorete_cat.total_count == 0 - end - end - - describe "[favorite category total_count]" do - test "total_count + 1 after set category to a favorited post", ~m(user post post2)a do - test_category = "test category" - {:ok, category} = Accounts.create_favorite_category(user, %{title: test_category}) - assert category.total_count == 0 - - {:ok, _post_favorite} = Accounts.set_favorites(user, :post, post.id, category.id) - - {:ok, category} = FavoriteCategory |> ORM.find(category.id) - assert category.total_count == 1 - - {:ok, _post_favorite} = Accounts.set_favorites(user, :post, post2.id, category.id) - {:ok, category} = FavoriteCategory |> ORM.find(category.id) - - assert category.total_count == 2 - end - - test "total_count - 1 after unset category to a favorited post", ~m(user post)a do - test_category = "test category" - {:ok, category} = Accounts.create_favorite_category(user, %{title: test_category}) - {:ok, _post_favorite} = Accounts.set_favorites(user, :post, post.id, category.id) - - {:ok, category} = Accounts.unset_favorites(user, :post, post.id, category.id) - - assert category.total_count == 0 - end - end -end diff --git a/test/groupher_server/accounts/reacted_articles_test.exs b/test/groupher_server/accounts/reacted_articles_test.exs new file mode 100644 index 000000000..45c484bce --- /dev/null +++ b/test/groupher_server/accounts/reacted_articles_test.exs @@ -0,0 +1,62 @@ +defmodule GroupherServer.Test.Accounts.ReactedContents do + @moduledoc false + + use GroupherServer.TestTools + + alias GroupherServer.{Accounts, CMS} + + setup do + {:ok, user} = db_insert(:user) + {:ok, post} = db_insert(:post) + {:ok, job} = db_insert(:job) + + {:ok, ~m(user post job)a} + end + + describe "[user upvoted articles]" do + @tag :wip + test "user can get paged upvoted common articles", ~m(user post job)a do + {:ok, _} = CMS.upvote_article(:post, post.id, user) + {:ok, _} = CMS.upvote_article(:job, job.id, user) + + filter = %{page: 1, size: 20} + {:ok, articles} = Accounts.list_upvoted_articles(user.id, filter) + + article_post = articles |> Map.get(:entries) |> List.last() + article_job = articles |> Map.get(:entries) |> List.first() + + assert articles |> is_valid_pagination?(:raw) + assert job.id == article_job |> Map.get(:id) + assert post.id == article_post |> Map.get(:id) + + assert [:id, :thread, :title, :upvotes_count] == article_post |> Map.keys() + assert [:id, :thread, :title, :upvotes_count] == article_job |> Map.keys() + end + + @tag :wip + test "user can get paged upvoted posts by thread filter", ~m(user post job)a do + {:ok, _} = CMS.upvote_article(:post, post.id, user) + {:ok, _} = CMS.upvote_article(:job, job.id, user) + + filter = %{thread: :post, page: 1, size: 20} + {:ok, articles} = Accounts.list_upvoted_articles(user.id, filter) + + assert articles |> is_valid_pagination?(:raw) + assert post.id == articles |> Map.get(:entries) |> List.last() |> Map.get(:id) + assert 1 == articles |> Map.get(:total_count) + end + + @tag :wip + test "user can get paged upvoted jobs by thread filter", ~m(user post job)a do + {:ok, _} = CMS.upvote_article(:post, post.id, user) + {:ok, _} = CMS.upvote_article(:job, job.id, user) + + filter = %{thread: :job, page: 1, size: 20} + {:ok, articles} = Accounts.list_upvoted_articles(user.id, filter) + + assert articles |> is_valid_pagination?(:raw) + assert job.id == articles |> Map.get(:entries) |> List.last() |> Map.get(:id) + assert 1 == articles |> Map.get(:total_count) + end + end +end diff --git a/test/groupher_server/accounts/reacted_contents_test.exs b/test/groupher_server/accounts/reacted_contents_test.exs deleted file mode 100644 index 62d953c14..000000000 --- a/test/groupher_server/accounts/reacted_contents_test.exs +++ /dev/null @@ -1,53 +0,0 @@ -defmodule GroupherServer.Test.Accounts.ReactedContents do - use GroupherServer.TestTools - - alias GroupherServer.{Accounts, CMS} - - setup do - {:ok, user} = db_insert(:user) - {:ok, post} = db_insert(:post) - {:ok, job} = db_insert(:job) - - {:ok, ~m(user post job)a} - end - - describe "[user favorited contents]" do - test "user can get paged favorited_posts", ~m(user post)a do - {:ok, _} = CMS.reaction(:post, :favorite, post.id, user) - - filter = %{page: 1, size: 20} - {:ok, posts} = Accounts.reacted_contents(:post, :favorite, filter, user) - assert posts |> is_valid_pagination?(:raw) - assert post.id == posts |> Map.get(:entries) |> List.first() |> Map.get(:id) - end - - test "user can get paged favorited_jobs", ~m(user job)a do - {:ok, _} = CMS.reaction(:job, :favorite, job.id, user) - - filter = %{page: 1, size: 20} - {:ok, jobs} = Accounts.reacted_contents(:job, :favorite, filter, user) - assert jobs |> is_valid_pagination?(:raw) - assert job.id == jobs |> Map.get(:entries) |> List.first() |> Map.get(:id) - end - end - - describe "[user stared contents]" do - test "user can get paged stard_posts", ~m(user post)a do - {:ok, _} = CMS.reaction(:post, :star, post.id, user) - - filter = %{page: 1, size: 20} - {:ok, posts} = Accounts.reacted_contents(:post, :star, filter, user) - assert posts |> is_valid_pagination?(:raw) - assert post.id == posts |> Map.get(:entries) |> List.first() |> Map.get(:id) - end - - test "user can get paged stared_jobs", ~m(user job)a do - {:ok, _} = CMS.reaction(:job, :star, job.id, user) - - filter = %{page: 1, size: 20} - {:ok, jobs} = Accounts.reacted_contents(:job, :star, filter, user) - assert jobs |> is_valid_pagination?(:raw) - assert job.id == jobs |> Map.get(:entries) |> List.first() |> Map.get(:id) - end - end -end diff --git a/test/groupher_server/accounts/utils_test.exs b/test/groupher_server/accounts/utils_test.exs new file mode 100644 index 000000000..874f3640e --- /dev/null +++ b/test/groupher_server/accounts/utils_test.exs @@ -0,0 +1,22 @@ +defmodule GroupherServer.Test.Accounts.Utils do + @moduledoc false + use GroupherServer.TestTools + + alias GroupherServer.Accounts + alias Helper.Cache + + @cache_pool :user_login + + describe "[get userid]" do + @tag :wip + test "get_userid_and_cache should work" do + {:ok, user} = db_insert(:user) + + {:ok, user_id} = Accounts.get_userid_and_cache(user.login) + assert user.id == user_id + + assert {:ok, user_id} = Cache.get(@cache_pool, user.login) + assert user_id == user.id + end + end +end diff --git a/test/groupher_server/cms/article_collect_test.exs b/test/groupher_server/cms/article_collect_test.exs new file mode 100644 index 000000000..0a5810e27 --- /dev/null +++ b/test/groupher_server/cms/article_collect_test.exs @@ -0,0 +1,114 @@ +defmodule GroupherServer.Test.ArticleCollect do + use GroupherServer.TestTools + + alias Helper.ORM + alias GroupherServer.CMS + alias CMS.{Post, Job} + + setup do + {:ok, user} = db_insert(:user) + {:ok, user2} = db_insert(:user) + {:ok, community} = db_insert(:community) + + post_attrs = mock_attrs(:post, %{community_id: community.id}) + job_attrs = mock_attrs(:job, %{community_id: community.id}) + + {:ok, ~m(user user2 community post_attrs job_attrs)a} + end + + describe "[cms post collect]" do + @tag :wip + test "post can be collect && collects_count should inc by 1", + ~m(user user2 community post_attrs)a do + {:ok, post} = CMS.create_content(community, :post, post_attrs, user) + + {:ok, article_collect} = CMS.collect_article(:post, post.id, user) + {:ok, article} = ORM.find(Post, article_collect.post_id) + + assert article.id == post.id + assert article.collects_count == 1 + + {:ok, article_collect} = CMS.collect_article(:post, post.id, user2) + {:ok, article} = ORM.find(Post, article_collect.post_id) + + assert article.collects_count == 2 + end + + @tag :wip + test "post can be undo collect && collects_count should dec by 1", + ~m(user community post_attrs)a do + {:ok, post} = CMS.create_content(community, :post, post_attrs, user) + + {:ok, article_collect} = CMS.collect_article(:post, post.id, user) + {:ok, article} = ORM.find(Post, article_collect.post_id) + assert article.id == post.id + assert article.collects_count == 1 + + {:ok, article_collect} = CMS.undo_collect_article(:post, post.id, user) + {:ok, article} = ORM.find(Post, article_collect.post_id) + assert article.collects_count == 0 + end + + @tag :wip + test "can get collect_users", ~m(user user2 community post_attrs)a do + {:ok, post} = CMS.create_content(community, :post, post_attrs, user) + + {:ok, _article} = CMS.collect_article(:post, post.id, user) + {:ok, _article} = CMS.collect_article(:post, post.id, user2) + + {:ok, users} = CMS.collected_users(:post, post.id, %{page: 1, size: 2}) + + assert users |> is_valid_pagination?(:raw) + assert user_exist_in?(user, users.entries) + assert user_exist_in?(user2, users.entries) + end + end + + describe "[cms job collect]" do + @tag :wip + test "job can be collect && collects_count should inc by 1", + ~m(user user2 community job_attrs)a do + {:ok, job} = CMS.create_content(community, :job, job_attrs, user) + + {:ok, article_collect} = CMS.collect_article(:job, job.id, user) + {:ok, article} = ORM.find(Job, article_collect.job_id) + + assert article.id == job.id + assert article.collects_count == 1 + + {:ok, article_collect} = CMS.collect_article(:job, job.id, user2) + {:ok, article} = ORM.find(Job, article_collect.job_id) + assert article.collects_count == 2 + end + + @tag :wip + test "job can be undo collect && collects_count should dec by 1", + ~m(user community job_attrs)a do + {:ok, job} = CMS.create_content(community, :job, job_attrs, user) + + {:ok, article_collect} = CMS.collect_article(:job, job.id, user) + {:ok, article} = ORM.find(Job, article_collect.job_id) + + assert article.id == job.id + assert article.collects_count == 1 + + {:ok, article_collect} = CMS.undo_collect_article(:job, job.id, user) + {:ok, article} = ORM.find(Job, article_collect.job_id) + assert article.collects_count == 0 + end + + @tag :wip + test "can get collect_users", ~m(user user2 community job_attrs)a do + {:ok, job} = CMS.create_content(community, :job, job_attrs, user) + + {:ok, _article} = CMS.collect_article(:job, job.id, user) + {:ok, _article} = CMS.collect_article(:job, job.id, user2) + + {:ok, users} = CMS.collected_users(:job, job.id, %{page: 1, size: 2}) + + assert users |> is_valid_pagination?(:raw) + assert user_exist_in?(user, users.entries) + assert user_exist_in?(user2, users.entries) + end + end +end diff --git a/test/groupher_server/cms/article_comment_test.exs b/test/groupher_server/cms/article_comment_test.exs index b6d708d77..91ebbc2d8 100644 --- a/test/groupher_server/cms/article_comment_test.exs +++ b/test/groupher_server/cms/article_comment_test.exs @@ -555,25 +555,22 @@ defmodule GroupherServer.Test.CMS.ArticleComment do assert deleted_comment.body_html == @delete_hint end - @tag :wip + @tag :wip2 test "delete comment still update article's comments_count field", ~m(user post)a do - total_count = 10 - - all_comments = - Enum.reduce(1..total_count, [], fn _, acc -> - {:ok, comment} = CMS.create_article_comment(:post, post.id, "commment", user) - - acc ++ [comment] - end) + {:ok, _comment} = CMS.create_article_comment(:post, post.id, "commment", user) + {:ok, _comment} = CMS.create_article_comment(:post, post.id, "commment", user) + {:ok, comment} = CMS.create_article_comment(:post, post.id, "commment", user) + {:ok, _comment} = CMS.create_article_comment(:post, post.id, "commment", user) + {:ok, _comment} = CMS.create_article_comment(:post, post.id, "commment", user) {:ok, post} = ORM.find(Post, post.id) - assert post.article_comments_count == total_count - random_comment = all_comments |> Enum.at(1) - {:ok, _} = CMS.delete_article_comment(random_comment.id, user) + assert post.article_comments_count == 5 + + {:ok, _} = CMS.delete_article_comment(comment.id, user) {:ok, post} = ORM.find(Post, post.id) - assert post.article_comments_count == total_count - 1 + assert post.article_comments_count == 4 end @tag :wip diff --git a/test/groupher_server/cms/article_pin_test.exs b/test/groupher_server/cms/article_pin_test.exs index 6fc09d951..bb81ab323 100644 --- a/test/groupher_server/cms/article_pin_test.exs +++ b/test/groupher_server/cms/article_pin_test.exs @@ -35,8 +35,8 @@ defmodule GroupherServer.Test.CMS.ArticlePin do assert pind_article.post_id == post.id end - @tag :wip2 - test "one community & thread can only pin certern count of post", ~m(community post user)a do + @tag :wip + test "one community & thread can only pin certern count of post", ~m(community user)a do Enum.reduce(1..@max_pinned_article_count_per_thread, [], fn _, acc -> {:ok, new_post} = CMS.create_content(community, :post, mock_attrs(:post), user) {:ok, _} = CMS.pin_article(:post, new_post.id, community.id) @@ -53,7 +53,7 @@ defmodule GroupherServer.Test.CMS.ArticlePin do assert {:error, _} = CMS.pin_article(:post, 8848, community.id) end - @tag :wip2 + @tag :wip test "can undo pin to a post", ~m(community post)a do {:ok, _} = CMS.pin_article(:post, post.id, community.id) diff --git a/test/groupher_server/cms/article_upvote_test.exs b/test/groupher_server/cms/article_upvote_test.exs new file mode 100644 index 000000000..cbd90b863 --- /dev/null +++ b/test/groupher_server/cms/article_upvote_test.exs @@ -0,0 +1,101 @@ +defmodule GroupherServer.Test.ArticleUpvote do + @moduledoc false + use GroupherServer.TestTools + + alias GroupherServer.CMS + + setup do + {:ok, user} = db_insert(:user) + {:ok, user2} = db_insert(:user) + {:ok, community} = db_insert(:community) + + post_attrs = mock_attrs(:post, %{community_id: community.id}) + job_attrs = mock_attrs(:job, %{community_id: community.id}) + + {:ok, ~m(user user2 community post_attrs job_attrs)a} + end + + describe "[cms post upvote]" do + @tag :wip + test "post can be upvote && upvotes_count should inc by 1", + ~m(user user2 community post_attrs)a do + {:ok, post} = CMS.create_content(community, :post, post_attrs, user) + + {:ok, article} = CMS.upvote_article(:post, post.id, user) + assert article.id == post.id + assert article.upvotes_count == 1 + + {:ok, article} = CMS.upvote_article(:post, post.id, user2) + assert article.upvotes_count == 2 + end + + @tag :wip + test "post can be undo upvote && upvotes_count should dec by 1", + ~m(user user2 community post_attrs)a do + {:ok, post} = CMS.create_content(community, :post, post_attrs, user) + + {:ok, article} = CMS.upvote_article(:post, post.id, user) + assert article.id == post.id + assert article.upvotes_count == 1 + + {:ok, article} = CMS.undo_upvote_article(:post, post.id, user2) + assert article.upvotes_count == 0 + end + + @tag :wip + test "can get upvotes_users", ~m(user user2 community post_attrs)a do + {:ok, post} = CMS.create_content(community, :post, post_attrs, user) + + {:ok, _article} = CMS.upvote_article(:post, post.id, user) + {:ok, _article} = CMS.upvote_article(:post, post.id, user2) + + {:ok, users} = CMS.upvoted_users(:post, post.id, %{page: 1, size: 2}) + + assert users |> is_valid_pagination?(:raw) + assert user_exist_in?(user, users.entries) + assert user_exist_in?(user2, users.entries) + end + end + + describe "[cms job upvote]" do + @tag :wip + test "job can be upvote && upvotes_count should inc by 1", + ~m(user user2 community job_attrs)a do + {:ok, job} = CMS.create_content(community, :job, job_attrs, user) + + {:ok, article} = CMS.upvote_article(:job, job.id, user) + assert article.id == job.id + assert article.upvotes_count == 1 + + {:ok, article} = CMS.upvote_article(:job, job.id, user2) + assert article.upvotes_count == 2 + end + + @tag :wip + test "job can be undo upvote && upvotes_count should dec by 1", + ~m(user user2 community job_attrs)a do + {:ok, job} = CMS.create_content(community, :job, job_attrs, user) + + {:ok, article} = CMS.upvote_article(:job, job.id, user) + assert article.id == job.id + assert article.upvotes_count == 1 + + {:ok, article} = CMS.undo_upvote_article(:job, job.id, user2) + assert article.upvotes_count == 0 + end + + @tag :wip + test "can get upvotes_users", ~m(user user2 community job_attrs)a do + {:ok, job} = CMS.create_content(community, :job, job_attrs, user) + + {:ok, _article} = CMS.upvote_article(:job, job.id, user) + {:ok, _article} = CMS.upvote_article(:job, job.id, user2) + + {:ok, users} = CMS.upvoted_users(:job, job.id, %{page: 1, size: 2}) + + assert users |> is_valid_pagination?(:raw) + assert user_exist_in?(user, users.entries) + assert user_exist_in?(user2, users.entries) + end + end +end diff --git a/test/groupher_server/cms/job_reactions_test.exs b/test/groupher_server/cms/job_reactions_test.exs deleted file mode 100644 index f16bdf42f..000000000 --- a/test/groupher_server/cms/job_reactions_test.exs +++ /dev/null @@ -1,46 +0,0 @@ -defmodule GroupherServer.Test.CMS.JobReactions do - use GroupherServer.TestTools - - alias GroupherServer.CMS - - setup do - {:ok, user} = db_insert(:user) - {:ok, community} = db_insert(:community) - - job_attrs = mock_attrs(:job, %{community_id: community.id}) - - {:ok, ~m(user community job_attrs)a} - end - - describe "[cms job star/favorite reaction]" do - test "star and undo star reaction to job", ~m(user community job_attrs)a do - {:ok, job} = CMS.create_content(community, :job, job_attrs, user) - - {:ok, _} = CMS.reaction(:job, :star, job.id, user) - {:ok, reaction_users} = CMS.reaction_users(:job, :star, job.id, %{page: 1, size: 1}) - reaction_users = reaction_users |> Map.get(:entries) - assert 1 == reaction_users |> Enum.filter(fn ruser -> user.id == ruser.id end) |> length - - {:ok, _} = CMS.undo_reaction(:job, :star, job.id, user) - {:ok, reaction_users2} = CMS.reaction_users(:job, :star, job.id, %{page: 1, size: 1}) - reaction_users2 = reaction_users2 |> Map.get(:entries) - - assert 0 == reaction_users2 |> Enum.filter(fn ruser -> user.id == ruser.id end) |> length - end - - test "favorite and undo favorite reaction to job", ~m(user community job_attrs)a do - {:ok, job} = CMS.create_content(community, :job, job_attrs, user) - - {:ok, _} = CMS.reaction(:job, :favorite, job.id, user) - {:ok, reaction_users} = CMS.reaction_users(:job, :favorite, job.id, %{page: 1, size: 1}) - reaction_users = reaction_users |> Map.get(:entries) - assert 1 == reaction_users |> Enum.filter(fn ruser -> user.id == ruser.id end) |> length - - {:ok, _} = CMS.undo_reaction(:job, :favorite, job.id, user) - {:ok, reaction_users2} = CMS.reaction_users(:job, :favorite, job.id, %{page: 1, size: 1}) - reaction_users2 = reaction_users2 |> Map.get(:entries) - - assert 0 == reaction_users2 |> Enum.filter(fn ruser -> user.id == ruser.id end) |> length - end - end -end diff --git a/test/groupher_server/cms/post_comment_test.exs b/test/groupher_server/cms/post_comment_test.exs deleted file mode 100644 index 7b3f709e5..000000000 --- a/test/groupher_server/cms/post_comment_test.exs +++ /dev/null @@ -1,230 +0,0 @@ -defmodule GroupherServer.Test.PostComment do - # currently only test comments for post type, rename and seprherate later - use GroupherServer.TestTools - - alias Helper.ORM - alias GroupherServer.CMS - - alias CMS.{PostComment, PostCommentReply} - - setup do - {:ok, post} = db_insert(:post) - {:ok, user} = db_insert(:user) - {:ok, community} = db_insert(:community) - - body = "this is a test comment" - - {:ok, comment} = - CMS.create_comment(:post, post.id, %{community: community.raw, body: body}, user) - - {:ok, ~m(post user community comment)a} - end - - describe "[comment CURD]" do - test "login user comment to exsiting post", ~m(post community user)a do - body = "this is a test comment" - - assert {:ok, comment} = - CMS.create_comment(:post, post.id, %{community: community.raw, body: body}, user) - - assert comment.post_id == post.id - assert comment.body == body - assert comment.author_id == user.id - end - - test "created comment should have a increased floor number", ~m(post community user)a do - body = "this is a test comment" - - assert {:ok, comment1} = - CMS.create_comment(:post, post.id, %{community: community.raw, body: body}, user) - - {:ok, user2} = db_insert(:user) - - assert {:ok, comment2} = - CMS.create_comment(:post, post.id, %{community: community.raw, body: body}, user2) - - assert comment1.floor == 2 - assert comment2.floor == 3 - end - - test "create comment to non-exsit post fails", ~m(user community)a do - body = "this is a test comment" - - assert {:error, _} = - CMS.create_comment( - :post, - non_exsit_id(), - %{community: community.raw, body: body}, - user - ) - end - - test "can reply a comment, and reply should be in comment replies list", - ~m(community comment user)a do - reply_body = "this is a reply comment" - - {:ok, reply} = - CMS.reply_comment(:post, comment.id, %{community: community.raw, body: reply_body}, user) - - {:ok, reply_preload} = ORM.find(PostComment, reply.id, preload: :reply_to) - {:ok, comment_preload} = ORM.find(PostComment, comment.id, preload: :replies) - - assert reply_preload.reply_to.id == comment.id - assert reply_preload.author_id == user.id - assert reply_preload.body == reply_body - # reply id should be in comments replies list - assert comment_preload.replies |> Enum.any?(&(&1.reply_id == reply.id)) - end - - test "comment can be deleted", ~m(post community user)a do - body = "this is a test comment" - - assert {:ok, comment} = - CMS.create_comment(:post, post.id, %{community: community.raw, body: body}, user) - - {:ok, deleted} = CMS.delete_comment(:post, comment.id) - assert deleted.id == comment.id - end - - test "after delete, the coments of id > deleted.id should decrease the floor number", - ~m(post community user)a do - body = "this is a test comment" - # in setup we have a comment - total = 30 + 1 - - comments = - Enum.reduce(1..total, [], fn _, acc -> - {:ok, value} = - CMS.create_comment(:post, post.id, %{community: community.raw, body: body}, user) - - acc ++ [value] - end) - - [comment_1, comment_2, comment_3, comment_last] = comments |> firstn_and_last(3) - - assert comment_1.floor == 2 - assert comment_2.floor == 3 - assert comment_3.floor == 4 - assert comment_last.floor == total + 1 - - {:ok, _} = CMS.delete_comment(:post, comment_1.id) - - {:ok, new_comment_2} = ORM.find(PostComment, comment_2.id) - {:ok, new_comment_3} = ORM.find(PostComment, comment_3.id) - {:ok, new_comment_last} = ORM.find(PostComment, comment_last.id) - - assert new_comment_2.floor == 2 - assert new_comment_3.floor == 3 - assert new_comment_last.floor == total - end - - test "comment with replies should be deleted together", ~m(community comment user)a do - reply_body = "this is a reply comment" - - {:ok, reply} = - CMS.reply_comment(:post, comment.id, %{community: community.raw, body: reply_body}, user) - - PostComment |> ORM.find_delete!(comment.id) - - {:error, _} = ORM.find(PostComment, comment.id) - {:error, _} = ORM.find(PostComment, reply.id) - - {:error, _} = - PostCommentReply |> ORM.find_by(post_comment_id: comment.id, reply_id: reply.id) - end - - test "comments pagination should work", ~m(post community user)a do - body = "fake comment" - - Enum.reduce(1..30, [], fn _, acc -> - {:ok, value} = - CMS.create_comment(:post, post.id, %{community: community.raw, body: body}, user) - - acc ++ [value] - end) - - {:ok, results} = CMS.list_comments(:post, post.id, %{page: 1, size: 10}) - - assert results |> is_valid_pagination?(:raw) - end - - test "comment reply can be list one-by-one --> by replied user", ~m(comment community)a do - {:ok, user1} = db_insert(:user) - {:ok, user2} = db_insert(:user) - {:ok, user3} = db_insert(:user) - - {:ok, _} = - CMS.reply_comment( - :post, - comment.id, - %{community: community.raw, body: "reply by user1"}, - user1 - ) - - {:ok, _} = - CMS.reply_comment( - :post, - comment.id, - %{community: community.raw, body: "reply by user2"}, - user2 - ) - - {:ok, _} = - CMS.reply_comment( - :post, - comment.id, - %{community: community.raw, body: "reply by user3"}, - user3 - ) - - {:ok, found_reply1} = CMS.list_replies(:post, comment.id, user1) - assert user1.id == found_reply1 |> List.first() |> Map.get(:author_id) - - {:ok, found_reply2} = CMS.list_replies(:post, comment.id, user2) - assert user2.id == found_reply2 |> List.first() |> Map.get(:author_id) - - {:ok, found_reply3} = CMS.list_replies(:post, comment.id, user3) - assert user3.id == found_reply3 |> List.first() |> Map.get(:author_id) - end - end - - describe "[comment Reactions]" do - test "user can like a comment", ~m(comment user)a do - {:ok, liked_comment} = CMS.like_comment(:post_comment, comment.id, user) - - {:ok, comment_preload} = ORM.find(PostComment, liked_comment.id, preload: :likes) - - assert comment_preload.likes |> Enum.any?(&(&1.post_comment_id == comment.id)) - end - - test "user like comment twice fails", ~m(comment user)a do - {:ok, _} = CMS.like_comment(:post_comment, comment.id, user) - {:error, _error} = CMS.like_comment(:post_comment, comment.id, user) - # TODO: fix err_msg later - end - - test "user can undo a like action", ~m(comment user)a do - {:ok, like} = CMS.like_comment(:post_comment, comment.id, user) - {:ok, _} = CMS.undo_like_comment(:post_comment, comment.id, user) - - {:ok, comment_preload} = ORM.find(PostComment, comment.id, preload: :likes) - assert false == comment_preload.likes |> Enum.any?(&(&1.id == like.id)) - end - - test "user can get paged likes of a post comment", ~m(comment)a do - {:ok, user1} = db_insert(:user) - {:ok, user2} = db_insert(:user) - {:ok, user3} = db_insert(:user) - - {:ok, _like1} = CMS.like_comment(:post_comment, comment.id, user1) - {:ok, _like2} = CMS.like_comment(:post_comment, comment.id, user2) - {:ok, _like3} = CMS.like_comment(:post_comment, comment.id, user3) - - {:ok, results} = CMS.reaction_users(:post_comment, :like, comment.id, %{page: 1, size: 10}) - - assert results.entries |> Enum.any?(&(&1.id == user1.id)) - assert results.entries |> Enum.any?(&(&1.id == user2.id)) - assert results.entries |> Enum.any?(&(&1.id == user3.id)) - end - end -end diff --git a/test/groupher_server/cms/post_reactions_test.exs b/test/groupher_server/cms/post_reactions_test.exs deleted file mode 100644 index 47d3870ce..000000000 --- a/test/groupher_server/cms/post_reactions_test.exs +++ /dev/null @@ -1,47 +0,0 @@ -defmodule GroupherServer.Test.PostReactions do - use GroupherServer.TestTools - - alias GroupherServer.CMS - - setup do - {:ok, user} = db_insert(:user) - {:ok, community} = db_insert(:community) - - post_attrs = mock_attrs(:post, %{community_id: community.id}) - - {:ok, ~m(user community post_attrs)a} - end - - describe "[cms post star/favorite reaction]" do - test "star and undo star reaction to post", ~m(user community post_attrs)a do - {:ok, post} = CMS.create_content(community, :post, post_attrs, user) - - {:ok, _} = CMS.reaction(:post, :star, post.id, user) - {:ok, reaction_users} = CMS.reaction_users(:post, :star, post.id, %{page: 1, size: 1}) - reaction_users = reaction_users |> Map.get(:entries) - assert 1 == reaction_users |> Enum.filter(fn ruser -> user.id == ruser.id end) |> length - - {:ok, _} = CMS.undo_reaction(:post, :star, post.id, user) - {:ok, reaction_users2} = CMS.reaction_users(:post, :star, post.id, %{page: 1, size: 1}) - reaction_users2 = reaction_users2 |> Map.get(:entries) - - assert 0 == reaction_users2 |> Enum.filter(fn ruser -> user.id == ruser.id end) |> length - end - - test "favorite and undo favorite reaction to post", ~m(user community post_attrs)a do - {:ok, post} = CMS.create_content(community, :post, post_attrs, user) - - {:ok, _} = CMS.reaction(:post, :favorite, post.id, user) - {:ok, reaction_users} = CMS.reaction_users(:post, :favorite, post.id, %{page: 1, size: 1}) - reaction_users = reaction_users |> Map.get(:entries) - assert 1 == reaction_users |> Enum.filter(fn ruser -> user.id == ruser.id end) |> length - - # undo test - {:ok, _} = CMS.undo_reaction(:post, :favorite, post.id, user) - {:ok, reaction_users2} = CMS.reaction_users(:post, :favorite, post.id, %{page: 1, size: 1}) - reaction_users2 = reaction_users2 |> Map.get(:entries) - - assert 0 == reaction_users2 |> Enum.filter(fn ruser -> user.id == ruser.id end) |> length - end - end -end diff --git a/test/groupher_server/cms/repo_reactions_test.exs b/test/groupher_server/cms/repo_reactions_test.exs deleted file mode 100644 index 2b0519478..000000000 --- a/test/groupher_server/cms/repo_reactions_test.exs +++ /dev/null @@ -1,32 +0,0 @@ -defmodule GroupherServer.Test.CMS.RepoReactions do - use GroupherServer.TestTools - - alias GroupherServer.CMS - - setup do - {:ok, user} = db_insert(:user) - {:ok, community} = db_insert(:community) - - repo_attrs = mock_attrs(:repo, %{community_id: community.id}) - - {:ok, ~m(user community repo_attrs)a} - end - - describe "[cms repo star/favorite reaction]" do - test "favorite and undo favorite reaction to repo", ~m(user community repo_attrs)a do - {:ok, repo} = CMS.create_content(community, :repo, repo_attrs, user) - - {:ok, _} = CMS.reaction(:repo, :favorite, repo.id, user) - {:ok, reaction_users} = CMS.reaction_users(:repo, :favorite, repo.id, %{page: 1, size: 1}) - reaction_users = reaction_users |> Map.get(:entries) - assert 1 == reaction_users |> Enum.filter(fn ruser -> user.id == ruser.id end) |> length - - # undo test - {:ok, _} = CMS.undo_reaction(:repo, :favorite, repo.id, user) - {:ok, reaction_users2} = CMS.reaction_users(:repo, :favorite, repo.id, %{page: 1, size: 1}) - reaction_users2 = reaction_users2 |> Map.get(:entries) - - assert 0 == reaction_users2 |> Enum.filter(fn ruser -> user.id == ruser.id end) |> length - end - end -end diff --git a/test/groupher_server/statistics/statistics_test.exs b/test/groupher_server/statistics/statistics_test.exs index 01497e7a2..acf81b5b8 100644 --- a/test/groupher_server/statistics/statistics_test.exs +++ b/test/groupher_server/statistics/statistics_test.exs @@ -148,12 +148,13 @@ defmodule GroupherServer.Test.Statistics do assert length(contributes) == @community_contribute_days + 1 end + @tag :wip test "the contributes data should be cached after first query", ~m(community)a do scope = Cache.get_scope(:community_contributes, community.id) - assert {:error, nil} = Cache.get(scope) + assert {:error, nil} = Cache.get(:common, scope) {:ok, contributes} = Statistics.list_contributes_digest(%Community{id: community.id}) - {:ok, cached_contributes} = Cache.get(scope) + {:ok, cached_contributes} = Cache.get(:common, scope) assert contributes == cached_contributes end @@ -166,7 +167,7 @@ defmodule GroupherServer.Test.Statistics do test "cache should be update after make contributes", ~m(community)a do scope = Cache.get_scope(:community_contributes, community.id) - assert {:error, nil} = Cache.get(scope) + assert {:error, nil} = Cache.get(:common, scope) Statistics.make_contribute(%Community{id: community.id}) @@ -174,7 +175,7 @@ defmodule GroupherServer.Test.Statistics do # Process.sleep(1000) # IO.inspect(res, label: "res") - # assert {:ok, _} = Cache.get(scope) + # assert {:ok, _} = Cache.get(:common, scope) end end end diff --git a/test/groupher_server_web/mutation/accounts/collect_folder_test.exs b/test/groupher_server_web/mutation/accounts/collect_folder_test.exs new file mode 100644 index 000000000..1d7e1ef6c --- /dev/null +++ b/test/groupher_server_web/mutation/accounts/collect_folder_test.exs @@ -0,0 +1,196 @@ +defmodule GroupherServer.Test.Mutation.Accounts.CollectFolder do + @moduledoc false + use GroupherServer.TestTools + + alias Helper.ORM + alias GroupherServer.{Accounts, CMS} + + alias Accounts.CollectFolder + + setup do + {:ok, user} = db_insert(:user) + {:ok, post} = db_insert(:post) + {:ok, job} = db_insert(:job) + {:ok, repo} = db_insert(:repo) + + user_conn = simu_conn(:user, user) + guest_conn = simu_conn(:guest) + + {:ok, ~m(user_conn guest_conn user post job repo)a} + end + + describe "[Accounts CollectFolder CURD]" do + @query """ + mutation($title: String!, $desc: String, $private: Boolean) { + createCollectFolder(title: $title, desc: $desc, private: $private) { + id + title + private + lastUpdated + } + } + """ + @tag :wip + test "login user can create collect folder", ~m(user_conn)a do + variables = %{title: "test folder", desc: "cool folder"} + created = user_conn |> mutation_result(@query, variables, "createCollectFolder") + {:ok, found} = CollectFolder |> ORM.find(created |> Map.get("id")) + + assert created |> Map.get("id") == to_string(found.id) + assert created["lastUpdated"] != nil + end + + @tag :wip + test "login user can create private collect folder", ~m(user_conn)a do + variables = %{title: "test folder", desc: "cool folder", private: true} + created = user_conn |> mutation_result(@query, variables, "createCollectFolder") + {:ok, found} = CollectFolder |> ORM.find(created |> Map.get("id")) + + assert created |> Map.get("id") == to_string(found.id) + assert created |> Map.get("private") + end + + @tag :wip + test "unauth user create category fails", ~m(guest_conn)a do + variables = %{title: "test folder"} + assert guest_conn |> mutation_get_error?(@query, variables, ecode(:account_login)) + end + + @query """ + mutation($id: ID!, $title: String, $desc: String, $private: Boolean) { + updateCollectFolder( + id: $id + title: $title + desc: $desc + private: $private + ) { + id + title + desc + private + lastUpdated + } + } + """ + @tag :wip + test "login user can update own collect folder", ~m(user_conn user)a do + args = %{title: "folder_title", private: false} + {:ok, folder} = Accounts.create_collect_folder(args, user) + + variables = %{id: folder.id, title: "new title", desc: "new desc", private: true} + updated = user_conn |> mutation_result(@query, variables, "updateCollectFolder") + + assert updated["desc"] == "new desc" + assert updated["private"] == true + assert updated["title"] == "new title" + assert updated["lastUpdated"] != nil + end + + @query """ + mutation($id: ID!) { + deleteCollectFolder(id: $id) { + id + } + } + """ + @tag :wip + test "login user can delete own collect folder", ~m(user_conn user)a do + args = %{title: "folder_title", private: false} + {:ok, folder} = Accounts.create_collect_folder(args, user) + + variables = %{id: folder.id} + user_conn |> mutation_result(@query, variables, "deleteCollectFolder") + assert {:error, _} = CollectFolder |> ORM.find(folder.id) + end + end + + describe "[Accounts CollectFolder add/remove]" do + @query """ + mutation($articleId: ID!, $folderId: ID!, $thread: CmsThread) { + addToCollect(articleId: $articleId, folderId: $folderId, thread: $thread) { + id + title + totalCount + lastUpdated + + meta { + hasPost + hasJob + hasRepo + postCount + jobCount + repoCount + } + } + } + """ + @tag :wip + test "user can add a post to collect folder", ~m(user user_conn post)a do + args = %{title: "folder_title", private: false} + {:ok, folder} = Accounts.create_collect_folder(args, user) + + variables = %{articleId: post.id, folderId: folder.id, thread: "POST"} + folder = user_conn |> mutation_result(@query, variables, "addToCollect") + + assert folder["totalCount"] == 1 + assert folder["lastUpdated"] != nil + + assert folder["meta"] == %{ + "hasJob" => false, + "hasPost" => true, + "hasRepo" => false, + "jobCount" => 0, + "postCount" => 1, + "repoCount" => 0 + } + + {:ok, article_collect} = + CMS.ArticleCollect |> ORM.find_by(%{post_id: post.id, user_id: user.id}) + + folder_in_article_collect = article_collect.collect_folders |> List.first() + + assert folder_in_article_collect.meta.has_post + assert folder_in_article_collect.meta.post_count == 1 + end + + @query """ + mutation($articleId: ID!, $folderId: ID!, $thread: CmsThread) { + removeFromCollect(articleId: $articleId, folderId: $folderId, thread: $thread) { + id + title + totalCount + lastUpdated + + meta { + hasPost + hasJob + hasRepo + postCount + jobCount + repoCount + } + } + } + """ + @tag :wip + test "user can remove a post from collect folder", ~m(user user_conn post)a do + args = %{title: "folder_title", private: false} + {:ok, folder} = Accounts.create_collect_folder(args, user) + {:ok, _folder} = Accounts.add_to_collect(:post, post.id, folder.id, user) + + variables = %{articleId: post.id, folderId: folder.id, thread: "POST"} + result = user_conn |> mutation_result(@query, variables, "removeFromCollect") + + assert result["meta"] == %{ + "hasJob" => false, + "hasPost" => false, + "hasRepo" => false, + "jobCount" => 0, + "postCount" => 0, + "repoCount" => 0 + } + + assert result["totalCount"] == 0 + end + end +end diff --git a/test/groupher_server_web/mutation/accounts/favorite_category_test.exs b/test/groupher_server_web/mutation/accounts/favorite_category_test.exs deleted file mode 100644 index 2555300de..000000000 --- a/test/groupher_server_web/mutation/accounts/favorite_category_test.exs +++ /dev/null @@ -1,270 +0,0 @@ -defmodule GroupherServer.Test.Mutation.Accounts.FavoriteCategory do - use GroupherServer.TestTools - - alias Helper.ORM - alias GroupherServer.{Accounts, CMS} - - alias Accounts.FavoriteCategory - - setup do - {:ok, user} = db_insert(:user) - {:ok, post} = db_insert(:post) - {:ok, job} = db_insert(:job) - {:ok, repo} = db_insert(:repo) - - user_conn = simu_conn(:user, user) - guest_conn = simu_conn(:guest) - - {:ok, ~m(user_conn guest_conn user post job repo)a} - end - - describe "[Accounts FavoriteCategory CURD]" do - @query """ - mutation($title: String!, $private: Boolean) { - createFavoriteCategory(title: $title, private: $private) { - id - title - lastUpdated - } - } - """ - test "login user can create favorite category", ~m(user_conn)a do - test_category = "test category" - variables = %{title: test_category} - created = user_conn |> mutation_result(@query, variables, "createFavoriteCategory") - {:ok, found} = FavoriteCategory |> ORM.find(created |> Map.get("id")) - - assert created |> Map.get("id") == to_string(found.id) - assert created["lastUpdated"] != nil - end - - test "unauth user create category fails", ~m(guest_conn)a do - test_category = "test category" - variables = %{title: test_category} - - assert guest_conn |> mutation_get_error?(@query, variables, ecode(:account_login)) - end - - @query """ - mutation($id: ID!, $title: String, $desc: String, $private: Boolean) { - updateFavoriteCategory( - id: $id - title: $title - desc: $desc - private: $private - ) { - id - title - desc - private - lastUpdated - } - } - """ - test "login user can update own favorite category", ~m(user_conn user)a do - test_category = "test category" - - {:ok, category} = - Accounts.create_favorite_category(user, %{ - title: test_category, - desc: "old desc", - private: false - }) - - variables = %{id: category.id, title: "new title", desc: "new desc", private: true} - updated = user_conn |> mutation_result(@query, variables, "updateFavoriteCategory") - - assert updated["desc"] == "new desc" - assert updated["private"] == true - assert updated["title"] == "new title" - assert updated["lastUpdated"] != nil - end - - @query """ - mutation($id: ID!) { - deleteFavoriteCategory(id: $id) { - done - } - } - """ - test "login user can delete own favorite category", ~m(user_conn user)a do - test_category = "test category" - {:ok, category} = Accounts.create_favorite_category(user, %{title: test_category}) - - variables = %{id: category.id} - deleted = user_conn |> mutation_result(@query, variables, "deleteFavoriteCategory") - - assert deleted["done"] == true - assert {:error, _} = FavoriteCategory |> ORM.find(category.id) - end - - test "after favorite deleted, the favroted action also be deleted ", - ~m(user_conn user post)a do - test_category = "test category" - {:ok, category} = Accounts.create_favorite_category(user, %{title: test_category}) - - {:ok, _favorite_category} = Accounts.set_favorites(user, :post, post.id, category.id) - - assert {:ok, _} = - CMS.PostFavorite |> ORM.find_by(%{user_id: user.id, category_id: category.id}) - - variables = %{id: category.id} - user_conn |> mutation_result(@query, variables, "deleteFavoriteCategory") - - assert {:error, _} = - CMS.PostFavorite |> ORM.find_by(%{user_id: user.id, category_id: category.id}) - end - - test "after favorite deleted, the related author's reputation should be downgrade", - ~m(user_conn user post job)a do - {:ok, author} = db_insert(:author) - {:ok, post2} = db_insert(:post, %{author: author}) - {:ok, job2} = db_insert(:job, %{author: author}) - test_category = "test category" - {:ok, category} = Accounts.create_favorite_category(user, %{title: test_category}) - - test_category2 = "test category2" - {:ok, category2} = Accounts.create_favorite_category(user, %{title: test_category2}) - - {:ok, _} = Accounts.set_favorites(user, :post, post.id, category.id) - {:ok, _} = Accounts.set_favorites(user, :post, post2.id, category.id) - {:ok, _} = Accounts.set_favorites(user, :job, job.id, category.id) - {:ok, _} = Accounts.set_favorites(user, :job, job2.id, category2.id) - - # author.id - {:ok, achievement} = ORM.find_by(Accounts.Achievement, user_id: author.user.id) - - assert achievement.contents_favorited_count == 2 - assert achievement.reputation == 4 - - variables = %{id: category.id} - user_conn |> mutation_result(@query, variables, "deleteFavoriteCategory") - - {:ok, achievement} = ORM.find_by(Accounts.Achievement, user_id: author.user.id) - - assert achievement.contents_favorited_count == 1 - assert achievement.reputation == 2 - end - end - - describe "[Accounts FavoriteCategory set/unset]" do - @query """ - mutation($id: ID!, $thread: CmsThread, $categoryId: ID!) { - setFavorites(id: $id, thread: $thread, categoryId: $categoryId) { - id - title - totalCount - lastUpdated - } - } - """ - test "user can put a post to favorites category", ~m(user user_conn post)a do - test_category = "test category" - {:ok, category} = Accounts.create_favorite_category(user, %{title: test_category}) - - variables = %{id: post.id, thread: "POST", categoryId: category.id} - created = user_conn |> mutation_result(@query, variables, "setFavorites") - {:ok, found} = CMS.PostFavorite |> ORM.find_by(%{post_id: post.id, user_id: user.id}) - - assert created["totalCount"] == 1 - assert created["lastUpdated"] != nil - - assert found.category_id == category.id - assert found.user_id == user.id - assert found.post_id == post.id - end - - test "user can put a job to favorites category", ~m(user user_conn job)a do - test_category = "test category" - {:ok, category} = Accounts.create_favorite_category(user, %{title: test_category}) - - variables = %{id: job.id, thread: "JOB", categoryId: category.id} - created = user_conn |> mutation_result(@query, variables, "setFavorites") - {:ok, found} = CMS.JobFavorite |> ORM.find_by(%{job_id: job.id, user_id: user.id}) - - assert created["totalCount"] == 1 - assert created["lastUpdated"] != nil - - assert found.category_id == category.id - assert found.user_id == user.id - assert found.job_id == job.id - end - - test "user can put a repo to favorites category", ~m(user user_conn repo)a do - test_category = "test category" - {:ok, category} = Accounts.create_favorite_category(user, %{title: test_category}) - - variables = %{id: repo.id, thread: "REPO", categoryId: category.id} - created = user_conn |> mutation_result(@query, variables, "setFavorites") - {:ok, found} = CMS.RepoFavorite |> ORM.find_by(%{repo_id: repo.id, user_id: user.id}) - - assert created["totalCount"] == 1 - assert created["lastUpdated"] != nil - - assert found.category_id == category.id - assert found.user_id == user.id - assert found.repo_id == repo.id - end - - @query """ - mutation($id: ID!, $thread: CmsThread, $categoryId: ID!) { - unsetFavorites(id: $id, thread: $thread, categoryId: $categoryId) { - id - title - totalCount - lastUpdated - } - } - """ - test "user can unset a post to favorites category", ~m(user user_conn post)a do - test_category = "test category" - {:ok, category} = Accounts.create_favorite_category(user, %{title: test_category}) - {:ok, _favorite_category} = Accounts.set_favorites(user, :post, post.id, category.id) - - {:ok, category} = Accounts.FavoriteCategory |> ORM.find(category.id) - assert category.total_count == 1 - assert category.last_updated != nil - - variables = %{id: post.id, thread: "POST", categoryId: category.id} - user_conn |> mutation_result(@query, variables, "unsetFavorites") - - {:ok, category} = Accounts.FavoriteCategory |> ORM.find(category.id) - assert category.total_count == 0 - assert {:error, _} = CMS.PostFavorite |> ORM.find_by(%{post_id: post.id, user_id: user.id}) - end - - test "user can unset a job to favorites category", ~m(user user_conn job)a do - test_category = "test category" - {:ok, category} = Accounts.create_favorite_category(user, %{title: test_category}) - {:ok, _favorite_category} = Accounts.set_favorites(user, :job, job.id, category.id) - - {:ok, category} = Accounts.FavoriteCategory |> ORM.find(category.id) - assert category.total_count == 1 - assert category.last_updated != nil - - variables = %{id: job.id, thread: "JOB", categoryId: category.id} - user_conn |> mutation_result(@query, variables, "unsetFavorites") - - {:ok, category} = Accounts.FavoriteCategory |> ORM.find(category.id) - assert category.total_count == 0 - assert {:error, _} = CMS.JobFavorite |> ORM.find_by(%{job_id: job.id, user_id: user.id}) - end - - test "user can unset a repo to favorites category", ~m(user user_conn repo)a do - test_category = "test category" - {:ok, category} = Accounts.create_favorite_category(user, %{title: test_category}) - {:ok, _favorite_category} = Accounts.set_favorites(user, :repo, repo.id, category.id) - - {:ok, category} = Accounts.FavoriteCategory |> ORM.find(category.id) - assert category.total_count == 1 - assert category.last_updated != nil - - variables = %{id: repo.id, thread: "REPO", categoryId: category.id} - user_conn |> mutation_result(@query, variables, "unsetFavorites") - - {:ok, category} = Accounts.FavoriteCategory |> ORM.find(category.id) - assert category.total_count == 0 - assert {:error, _} = CMS.RepoFavorite |> ORM.find_by(%{repo_id: repo.id, user_id: user.id}) - end - end -end diff --git a/test/groupher_server_web/mutation/cms/article_upvote_test.exs b/test/groupher_server_web/mutation/cms/article_upvote_test.exs new file mode 100644 index 000000000..c971cbc46 --- /dev/null +++ b/test/groupher_server_web/mutation/cms/article_upvote_test.exs @@ -0,0 +1,117 @@ +defmodule GroupherServer.Test.Mutation.ArticleUpvote do + @moduledoc false + use GroupherServer.TestTools + + alias GroupherServer.CMS + + setup do + {:ok, post} = db_insert(:post) + {:ok, job} = db_insert(:job) + {:ok, user} = db_insert(:user) + + guest_conn = simu_conn(:guest) + user_conn = simu_conn(:user, user) + + {:ok, ~m(user_conn guest_conn post job user)a} + end + + describe "[post upvote]" do + @query """ + mutation($id: ID!, $thread: CmsThread!) { + upvoteArticle(id: $id, thread: $thread) { + id + } + } + """ + @tag :wip + test "login user can upvote a post", ~m(user_conn post)a do + variables = %{id: post.id, thread: "POST"} + created = user_conn |> mutation_result(@query, variables, "upvoteArticle") + + assert created["id"] == to_string(post.id) + end + + @tag :wip + test "unauth user upvote a post fails", ~m(guest_conn post)a do + variables = %{id: post.id, thread: "POST"} + + assert guest_conn + |> mutation_get_error?(@query, variables, ecode(:account_login)) + end + + @query """ + mutation($id: ID!, $thread: CmsThread!) { + undoUpvoteArticle(id: $id, thread: $thread) { + id + } + } + """ + @tag :wip + test "login user can undo upvote to a post", ~m(user_conn post user)a do + {:ok, _} = CMS.upvote_article(:post, post.id, user) + + variables = %{id: post.id, thread: "POST"} + updated = user_conn |> mutation_result(@query, variables, "undoUpvoteArticle") + + assert updated["id"] == to_string(post.id) + end + + @tag :wip + test "unauth user undo upvote a post fails", ~m(guest_conn post)a do + variables = %{id: post.id, thread: "POST"} + + assert guest_conn + |> mutation_get_error?(@query, variables, ecode(:account_login)) + end + end + + describe "[job upvote]" do + @query """ + mutation($id: ID!, $thread: CmsThread!) { + upvoteArticle(id: $id, thread: $thread) { + id + } + } + """ + @tag :wip + test "login user can upvote a job", ~m(user_conn job)a do + variables = %{id: job.id, thread: "JOB"} + created = user_conn |> mutation_result(@query, variables, "upvoteArticle") + + assert created["id"] == to_string(job.id) + end + + @tag :wip + test "unauth user upvote a job fails", ~m(guest_conn job)a do + variables = %{id: job.id, thread: "JOB"} + + assert guest_conn + |> mutation_get_error?(@query, variables, ecode(:account_login)) + end + + @query """ + mutation($id: ID!, $thread: CmsThread!) { + undoUpvoteArticle(id: $id, thread: $thread) { + id + } + } + """ + @tag :wip + test "login user can undo upvote to a job", ~m(user_conn job user)a do + {:ok, _} = CMS.upvote_article(:job, job.id, user) + + variables = %{id: job.id, thread: "JOB"} + updated = user_conn |> mutation_result(@query, variables, "undoUpvoteArticle") + + assert updated["id"] == to_string(job.id) + end + + @tag :wip + test "unauth user undo upvote a job fails", ~m(guest_conn job)a do + variables = %{id: job.id, thread: "JOB"} + + assert guest_conn + |> mutation_get_error?(@query, variables, ecode(:account_login)) + end + end +end diff --git a/test/groupher_server_web/mutation/cms/job_reaction_test.exs b/test/groupher_server_web/mutation/cms/job_reaction_test.exs deleted file mode 100644 index 9412e069b..000000000 --- a/test/groupher_server_web/mutation/cms/job_reaction_test.exs +++ /dev/null @@ -1,61 +0,0 @@ -defmodule GroupherServer.Test.Mutation.JobReaction do - use GroupherServer.TestTools - - alias GroupherServer.CMS - - setup do - {:ok, job} = db_insert(:job) - {:ok, user} = db_insert(:user) - - guest_conn = simu_conn(:guest) - user_conn = simu_conn(:user, user) - - {:ok, ~m(user_conn guest_conn job user)a} - end - - describe "[job star]" do - @query """ - mutation($id: ID!, $action: ReactableAction!, $thread: ReactThread!) { - reaction(id: $id, action: $action, thread: $thread) { - id - } - } - """ - test "login user can star a job", ~m(user_conn job)a do - variables = %{id: job.id, thread: "JOB", action: "STAR"} - created = user_conn |> mutation_result(@query, variables, "reaction") - - assert created["id"] == to_string(job.id) - end - - test "unauth user star a job fails", ~m(guest_conn job)a do - variables = %{id: job.id, thread: "JOB", action: "STAR"} - - assert guest_conn - |> mutation_get_error?(@query, variables, ecode(:account_login)) - end - - @query """ - mutation($id: ID!, $action: ReactableAction!, $thread: ReactThread!) { - undoReaction(id: $id, action: $action, thread: $thread) { - id - } - } - """ - test "login user can undo star a job", ~m(user_conn job user)a do - {:ok, _} = CMS.reaction(:job, :star, job.id, user) - - variables = %{id: job.id, thread: "JOB", action: "STAR"} - updated = user_conn |> mutation_result(@query, variables, "undoReaction") - - assert updated["id"] == to_string(job.id) - end - - test "unauth user undo star a job fails", ~m(guest_conn job)a do - variables = %{id: job.id, thread: "JOB", action: "STAR"} - - assert guest_conn - |> mutation_get_error?(@query, variables, ecode(:account_login)) - end - end -end diff --git a/test/groupher_server_web/mutation/cms/post_comment_test.exs b/test/groupher_server_web/mutation/cms/post_comment_test.exs index e55372176..e0e3e8487 100644 --- a/test/groupher_server_web/mutation/cms/post_comment_test.exs +++ b/test/groupher_server_web/mutation/cms/post_comment_test.exs @@ -250,22 +250,4 @@ defmodule GroupherServer.Test.Mutation.PostComment do test "TODO owner can NOT delete comment when comment has created after 3 hours" do end end - - describe "[post comment reactions]" do - @like_comment_query """ - mutation($thread: CmsComment!, $id: ID!) { - likeComment(thread: $thread, id: $id) { - id - } - } - """ - test "login user can like a comment", ~m(user_conn comment)a do - variables = %{thread: "POST_COMMENT", id: comment.id} - user_conn |> mutation_result(@like_comment_query, variables, "likeComment") - - {:ok, found} = CMS.PostComment |> ORM.find(comment.id, preload: :likes) - - assert found.likes |> Enum.any?(&(&1.post_comment_id == comment.id)) - end - end end diff --git a/test/groupher_server_web/mutation/cms/post_reaction_test.exs b/test/groupher_server_web/mutation/cms/post_reaction_test.exs deleted file mode 100644 index e484f0b00..000000000 --- a/test/groupher_server_web/mutation/cms/post_reaction_test.exs +++ /dev/null @@ -1,61 +0,0 @@ -defmodule GroupherServer.Test.Mutation.PostReaction do - use GroupherServer.TestTools - - alias GroupherServer.CMS - - setup do - {:ok, post} = db_insert(:post) - {:ok, user} = db_insert(:user) - - guest_conn = simu_conn(:guest) - user_conn = simu_conn(:user, user) - - {:ok, ~m(user_conn guest_conn post user)a} - end - - describe "[post star]" do - @query """ - mutation($id: ID!, $action: ReactableAction!, $thread: ReactThread!) { - reaction(id: $id, action: $action, thread: $thread) { - id - } - } - """ - test "login user can star a post", ~m(user_conn post)a do - variables = %{id: post.id, thread: "POST", action: "STAR"} - created = user_conn |> mutation_result(@query, variables, "reaction") - - assert created["id"] == to_string(post.id) - end - - test "unauth user star a post fails", ~m(guest_conn post)a do - variables = %{id: post.id, thread: "POST", action: "STAR"} - - assert guest_conn - |> mutation_get_error?(@query, variables, ecode(:account_login)) - end - - @query """ - mutation($id: ID!, $action: ReactableAction!, $thread: ReactThread!) { - undoReaction(id: $id, action: $action, thread: $thread) { - id - } - } - """ - test "login user can undo star a post", ~m(user_conn post user)a do - {:ok, _} = CMS.reaction(:post, :star, post.id, user) - - variables = %{id: post.id, thread: "POST", action: "STAR"} - updated = user_conn |> mutation_result(@query, variables, "undoReaction") - - assert updated["id"] == to_string(post.id) - end - - test "unauth user undo star a post fails", ~m(guest_conn post)a do - variables = %{id: post.id, thread: "POST", action: "STAR"} - - assert guest_conn - |> mutation_get_error?(@query, variables, ecode(:account_login)) - end - end -end diff --git a/test/groupher_server_web/query/accounts/achievement_test.exs b/test/groupher_server_web/query/accounts/achievement_test.exs index 0cb6e4cf3..0ce41f536 100644 --- a/test/groupher_server_web/query/accounts/achievement_test.exs +++ b/test/groupher_server_web/query/accounts/achievement_test.exs @@ -6,8 +6,8 @@ defmodule GroupherServer.Test.Query.Account.Achievement do alias Helper.ORM @follow_weight get_config(:general, :user_achieve_follow_weight) - @favorite_weight get_config(:general, :user_achieve_favorite_weight) - # @star_weight get_config(:general, :user_achieve_star_weight) + @collect_weight get_config(:general, :user_achieve_collect_weight) + # @upvote_weight get_config(:general, :user_achieve_upvote_weight) setup do {:ok, user} = db_insert(:user) @@ -24,8 +24,8 @@ defmodule GroupherServer.Test.Query.Account.Achievement do id achievement { reputation - contentsStaredCount - contentsFavoritedCount + articlesUpvotesCount + articlesCollectsCount sourceContribute { web server @@ -34,6 +34,7 @@ defmodule GroupherServer.Test.Query.Account.Achievement do } } """ + @tag :wip test "empty user should get empty achievement", ~m(guest_conn user)a do variables = %{login: user.login} @@ -168,7 +169,7 @@ defmodule GroupherServer.Test.Query.Account.Achievement do end end - describe "[account favorite achieveMent]" do + describe "[account collect achieveMent]" do alias GroupherServer.CMS @query """ @@ -177,14 +178,15 @@ defmodule GroupherServer.Test.Query.Account.Achievement do id achievement { reputation - contentsFavoritedCount + articlesCollectsCount } } } """ - test "inc user's achievement after user's post got favorited", ~m(guest_conn user)a do + @tag :wip + test "inc user's achievement after user's post got collected", ~m(guest_conn user)a do {:ok, post} = db_insert(:post) - {:ok, _} = CMS.reaction(:post, :favorite, post.id, user) + {:ok, _article_collect} = CMS.collect_article(:post, post.id, user) {:ok, post} = CMS.Post |> ORM.find(post.id, preload: [author: :user]) author_user_login = post.author.user.login @@ -192,32 +194,33 @@ defmodule GroupherServer.Test.Query.Account.Achievement do variables = %{login: author_user_login} results = guest_conn |> query_result(@query, variables, "user") - assert results["achievement"] |> Map.get("contentsFavoritedCount") == 1 - assert results["achievement"] |> Map.get("reputation") == @favorite_weight + assert results["achievement"] |> Map.get("articlesCollectsCount") == 1 + assert results["achievement"] |> Map.get("reputation") == @collect_weight end - test "minus user's acheiveements after user's post get cancle favorited", ~m(guest_conn)a do + @tag :wip + test "minus user's acheiveements after user's post's collect cancled", ~m(guest_conn)a do total_count = 10 {:ok, post} = db_insert(:post) {:ok, users} = db_insert_multi(:user, total_count) Enum.each(users, fn user -> - {:ok, _} = CMS.reaction(:post, :favorite, post.id, user) + {:ok, _article_collect} = CMS.collect_article(:post, post.id, user) end) {:ok, post} = CMS.Post |> ORM.find(post.id, preload: [author: :user]) author_user_login = post.author.user.login user = users |> Enum.shuffle() |> List.first() - {:ok, _} = CMS.undo_reaction(:post, :favorite, post.id, user) + {:ok, _article_collect} = CMS.undo_collect_article(:post, post.id, user) variables = %{login: author_user_login} results = guest_conn |> query_result(@query, variables, "user") - assert results["achievement"] |> Map.get("contentsFavoritedCount") == total_count - 1 + assert results["achievement"] |> Map.get("articlesCollectsCount") == total_count - 1 assert results["achievement"] |> Map.get("reputation") == - @favorite_weight * total_count - @favorite_weight + @collect_weight * total_count - @collect_weight end end end diff --git a/test/groupher_server_web/query/accounts/colleced_articles_test.exs b/test/groupher_server_web/query/accounts/colleced_articles_test.exs new file mode 100644 index 000000000..bda0b151b --- /dev/null +++ b/test/groupher_server_web/query/accounts/colleced_articles_test.exs @@ -0,0 +1,158 @@ +defmodule GroupherServer.Test.Query.Accounts.CollectedArticles do + use GroupherServer.TestTools + + alias GroupherServer.Accounts + + @total_count 20 + + setup do + {:ok, user} = db_insert(:user) + + {:ok, posts} = db_insert_multi(:post, @total_count) + + guest_conn = simu_conn(:guest) + user_conn = simu_conn(:user, user) + + {:ok, ~m(guest_conn user_conn user posts)a} + end + + @query """ + query($userLogin: String!, $filter: CollectFoldersFilter!) { + pagedCollectFolders(userLogin: $userLogin, filter: $filter) { + entries { + id + title + private + } + totalPages + totalCount + pageSize + pageNumber + } + } + """ + @tag :wip + test "other user can get other user's paged collect folders", ~m(user_conn guest_conn)a do + {:ok, user} = db_insert(:user) + + {:ok, _folder} = Accounts.create_collect_folder(%{title: "test folder"}, user) + {:ok, _folder} = Accounts.create_collect_folder(%{title: "test folder2"}, user) + + variables = %{userLogin: user.login, filter: %{page: 1, size: 20}} + results = user_conn |> query_result(@query, variables, "pagedCollectFolders") + results2 = guest_conn |> query_result(@query, variables, "pagedCollectFolders") + + assert results["totalCount"] == 2 + assert results2["totalCount"] == 2 + + assert results |> is_valid_pagination?() + assert results2 |> is_valid_pagination?() + end + + @tag :wip + test "other user can get other user's paged collect folders filter by thread", + ~m(guest_conn)a do + {:ok, user} = db_insert(:user) + {:ok, post} = db_insert(:post) + {:ok, job} = db_insert(:job) + {:ok, job2} = db_insert(:job) + + {:ok, folder_post} = Accounts.create_collect_folder(%{title: "test folder2"}, user) + {:ok, folder_job} = Accounts.create_collect_folder(%{title: "test folder"}, user) + + {:ok, _folder} = Accounts.add_to_collect(:post, post.id, folder_post.id, user) + {:ok, _folder} = Accounts.add_to_collect(:job, job.id, folder_post.id, user) + {:ok, _folder} = Accounts.add_to_collect(:job, job2.id, folder_job.id, user) + + variables = %{userLogin: user.login, filter: %{thread: "JOB", page: 1, size: 20}} + results = guest_conn |> query_result(@query, variables, "pagedCollectFolders") + assert results["totalCount"] == 2 + + variables = %{userLogin: user.login, filter: %{thread: "POST", page: 1, size: 20}} + results = guest_conn |> query_result(@query, variables, "pagedCollectFolders") + assert results["totalCount"] == 1 + end + + @tag :wip + test "owner can get it's paged collect folders with private folders", + ~m(user user_conn guest_conn)a do + {:ok, _folder} = Accounts.create_collect_folder(%{title: "test folder", private: true}, user) + {:ok, _folder} = Accounts.create_collect_folder(%{title: "test folder2"}, user) + + variables = %{userLogin: user.login, filter: %{page: 1, size: 20}} + results = user_conn |> query_result(@query, variables, "pagedCollectFolders") + results2 = guest_conn |> query_result(@query, variables, "pagedCollectFolders") + + assert results["totalCount"] == 2 + assert results2["totalCount"] == 1 + end + + @query """ + query($folderId: ID!, $filter: CollectedArticlesFilter!) { + pagedCollectedArticles(folderId: $folderId, filter: $filter) { + entries { + id + title + } + totalPages + totalCount + pageSize + pageNumber + } + } + """ + @tag :wip + test "can get paged articles inside a collect-folder", ~m(user_conn guest_conn user posts)a do + {:ok, folder} = Accounts.create_collect_folder(%{title: "test folder"}, user) + + Enum.each(posts, fn post -> + {:ok, _folder} = Accounts.add_to_collect(:post, post.id, folder.id, user) + end) + + post1 = Enum.at(posts, 0) + post2 = Enum.at(posts, 1) + post3 = Enum.at(posts, 2) + + variables = %{folderId: folder.id, filter: %{page: 1, size: 20}} + + results = user_conn |> query_result(@query, variables, "pagedCollectedArticles") + results2 = guest_conn |> query_result(@query, variables, "pagedCollectedArticles") + + assert results["totalCount"] == @total_count + assert results["entries"] |> Enum.any?(&(&1["id"] == to_string(post1.id))) + assert results["entries"] |> Enum.any?(&(&1["id"] == to_string(post2.id))) + assert results["entries"] |> Enum.any?(&(&1["id"] == to_string(post3.id))) + + assert results == results2 + end + + @tag :wip + test "can not get collect-folder articles when folder is private", ~m(guest_conn posts)a do + {:ok, user2} = db_insert(:user) + {:ok, folder} = Accounts.create_collect_folder(%{title: "test folder", private: true}, user2) + + Enum.each(posts, fn post -> + {:ok, _folder} = Accounts.add_to_collect(:post, post.id, folder.id, user2) + end) + + variables = %{folderId: folder.id, filter: %{page: 1, size: 20}} + + assert guest_conn |> query_get_error?(@query, variables, ecode(:private_collect_folder)) + end + + @tag :wip + test "owner can get collect-folder articles when folder is private", + ~m(user_conn user posts)a do + {:ok, folder} = Accounts.create_collect_folder(%{title: "test folder", private: true}, user) + + Enum.each(posts, fn post -> + {:ok, _folder} = Accounts.add_to_collect(:post, post.id, folder.id, user) + end) + + variables = %{folderId: folder.id, filter: %{page: 1, size: 20}} + + results = user_conn |> query_result(@query, variables, "pagedCollectedArticles") + + assert results["totalCount"] == @total_count + end +end diff --git a/test/groupher_server_web/query/accounts/favorite_category_test.exs b/test/groupher_server_web/query/accounts/favorite_category_test.exs deleted file mode 100644 index 81fbfa347..000000000 --- a/test/groupher_server_web/query/accounts/favorite_category_test.exs +++ /dev/null @@ -1,71 +0,0 @@ -defmodule GroupherServer.Test.Query.Accounts.FavoriteCategory do - use GroupherServer.TestTools - - alias GroupherServer.Accounts - # alias Accounts.FavoriteCategory - - setup do - {:ok, user} = db_insert(:user) - {:ok, post} = db_insert(:post) - - user_conn = simu_conn(:user, user) - guest_conn = simu_conn(:guest) - - {:ok, ~m(user_conn guest_conn user post)a} - end - - describe "[Accounts FavoriteCategory]" do - @query """ - query($userId: ID, $filter: CommonPagedFilter!) { - favoriteCategories(userId: $userId, filter: $filter) { - entries { - id - title - private - } - totalPages - totalCount - pageSize - pageNumber - } - } - """ - test "user can get list of favorite categories", ~m(user user_conn)a do - test_category = "test category" - {:ok, _} = Accounts.create_favorite_category(user, %{title: test_category, private: false}) - - variables = %{filter: %{page: 1, size: 20}} - results = user_conn |> query_result(@query, variables, "favoriteCategories") - assert results |> is_valid_pagination? - assert results["totalCount"] == 1 - end - - test "author can get it's own private categories", ~m(user user_conn)a do - test_category = "test category" - test_category2 = "test category2" - {:ok, _} = Accounts.create_favorite_category(user, %{title: test_category, private: false}) - {:ok, _} = Accounts.create_favorite_category(user, %{title: test_category2, private: true}) - - variables = %{filter: %{page: 1, size: 20}} - results = user_conn |> query_result(@query, variables, "favoriteCategories") - assert results |> is_valid_pagination? - assert results["totalCount"] == 2 - end - - test "guest user can't get others private favorite categories", ~m(guest_conn)a do - {:ok, user} = db_insert(:user) - - test_category = "test category" - test_category2 = "test category2" - {:ok, _} = Accounts.create_favorite_category(user, %{title: test_category, private: false}) - {:ok, _} = Accounts.create_favorite_category(user, %{title: test_category2, private: true}) - - variables = %{userId: user.id, filter: %{page: 1, size: 20}} - results = guest_conn |> query_result(@query, variables, "favoriteCategories") - assert results |> is_valid_pagination? - - assert results["entries"] |> Enum.any?(&(&1["title"] !== test_category2)) - assert results["totalCount"] == 1 - end - end -end diff --git a/test/groupher_server_web/query/accounts/favorited_jobs_test.exs b/test/groupher_server_web/query/accounts/favorited_jobs_test.exs deleted file mode 100644 index 72baae3d9..000000000 --- a/test/groupher_server_web/query/accounts/favorited_jobs_test.exs +++ /dev/null @@ -1,124 +0,0 @@ -defmodule GroupherServer.Test.Query.Accounts.FavritedJobs do - use GroupherServer.TestTools - - alias GroupherServer.CMS - - @total_count 20 - - setup do - {:ok, user} = db_insert(:user) - {:ok, jobs} = db_insert_multi(:job, @total_count) - - guest_conn = simu_conn(:guest) - user_conn = simu_conn(:user, user) - - {:ok, ~m(guest_conn user_conn user jobs)a} - end - - describe "[accounts favorited jobs]" do - @query """ - query($filter: PagedFilter!) { - user { - id - favoritedJobs(filter: $filter) { - entries { - id - } - totalCount - } - favoritedJobsCount - } - } - """ - test "login user can get it's own favoritedJobs", ~m(user_conn user jobs)a do - Enum.each(jobs, fn job -> - {:ok, _} = CMS.reaction(:job, :favorite, job.id, user) - end) - - random_id = jobs |> Enum.shuffle() |> List.first() |> Map.get(:id) |> to_string - - variables = %{filter: %{page: 1, size: 20}} - results = user_conn |> query_result(@query, variables, "user") - assert results["favoritedJobs"] |> Map.get("totalCount") == @total_count - assert results["favoritedJobsCount"] == @total_count - - assert results["favoritedJobs"] - |> Map.get("entries") - |> Enum.any?(&(&1["id"] == random_id)) - end - - @query """ - query($userId: ID!, $categoryId: ID,$filter: PagedFilter!) { - favoritedJobs(userId: $userId, categoryId: $categoryId, filter: $filter) { - entries { - id - } - totalCount - } - } - """ - test "other user can get other user's paged favoritedJobs", - ~m(user_conn guest_conn jobs)a do - {:ok, user} = db_insert(:user) - - Enum.each(jobs, fn job -> - {:ok, _} = CMS.reaction(:job, :favorite, job.id, user) - end) - - variables = %{userId: user.id, filter: %{page: 1, size: 20}} - results = user_conn |> query_result(@query, variables, "favoritedJobs") - results2 = guest_conn |> query_result(@query, variables, "favoritedJobs") - - assert results["totalCount"] == @total_count - assert results2["totalCount"] == @total_count - end - - test "login user can get self paged favoritedJobs", ~m(user_conn user jobs)a do - Enum.each(jobs, fn job -> - {:ok, _} = CMS.reaction(:job, :favorite, job.id, user) - end) - - variables = %{userId: user.id, filter: %{page: 1, size: 20}} - results = user_conn |> query_result(@query, variables, "favoritedJobs") - - assert results["totalCount"] == @total_count - end - - alias GroupherServer.Accounts - - test "can get paged favoritedJobs on a spec category", ~m(user_conn guest_conn jobs)a do - {:ok, user} = db_insert(:user) - - Enum.each(jobs, fn job -> - {:ok, _} = CMS.reaction(:job, :favorite, job.id, user) - end) - - job1 = Enum.at(jobs, 0) - job2 = Enum.at(jobs, 1) - job3 = Enum.at(jobs, 2) - job4 = Enum.at(jobs, 4) - - test_category = "test category" - test_category2 = "test category2" - - {:ok, category} = Accounts.create_favorite_category(user, %{title: test_category}) - {:ok, category2} = Accounts.create_favorite_category(user, %{title: test_category2}) - - {:ok, _favorites_category} = Accounts.set_favorites(user, :job, job1.id, category.id) - {:ok, _favorites_category} = Accounts.set_favorites(user, :job, job2.id, category.id) - {:ok, _favorites_category} = Accounts.set_favorites(user, :job, job3.id, category.id) - {:ok, _favorites_category} = Accounts.set_favorites(user, :job, job4.id, category2.id) - - variables = %{userId: user.id, categoryId: category.id, filter: %{page: 1, size: 20}} - results = user_conn |> query_result(@query, variables, "favoritedJobs") - results2 = guest_conn |> query_result(@query, variables, "favoritedJobs") - - assert results["totalCount"] == 3 - assert results["entries"] |> Enum.any?(&(&1["id"] == to_string(job1.id))) - assert results["entries"] |> Enum.any?(&(&1["id"] == to_string(job2.id))) - assert results["entries"] |> Enum.any?(&(&1["id"] == to_string(job3.id))) - - assert results == results2 - end - end -end diff --git a/test/groupher_server_web/query/accounts/favorited_posts_test.exs b/test/groupher_server_web/query/accounts/favorited_posts_test.exs deleted file mode 100644 index 84ea3f1bb..000000000 --- a/test/groupher_server_web/query/accounts/favorited_posts_test.exs +++ /dev/null @@ -1,114 +0,0 @@ -defmodule GroupherServer.Test.Query.Accounts.FavritedPosts do - use GroupherServer.TestTools - - alias GroupherServer.CMS - - @total_count 20 - - setup do - {:ok, user} = db_insert(:user) - - {:ok, posts} = db_insert_multi(:post, @total_count) - - guest_conn = simu_conn(:guest) - user_conn = simu_conn(:user, user) - - {:ok, ~m(guest_conn user_conn user posts)a} - end - - describe "[account favorited posts]" do - @query """ - query($filter: PagedFilter!) { - user { - id - favoritedPosts(filter: $filter) { - entries { - id - } - totalCount - } - favoritedPostsCount - } - } - """ - test "login user can get it's own favoritedPosts", ~m(user_conn user posts)a do - Enum.each(posts, fn post -> - {:ok, _} = CMS.reaction(:post, :favorite, post.id, user) - end) - - random_id = posts |> Enum.shuffle() |> List.first() |> Map.get(:id) |> to_string - - variables = %{filter: %{page: 1, size: 20}} - results = user_conn |> query_result(@query, variables, "user") - assert results["favoritedPosts"] |> Map.get("totalCount") == @total_count - assert results["favoritedPostsCount"] == @total_count - - assert results["favoritedPosts"] - |> Map.get("entries") - |> Enum.any?(&(&1["id"] == random_id)) - end - - @query """ - query($userId: ID!, $categoryId: ID, $filter: PagedFilter!) { - favoritedPosts(userId: $userId, categoryId: $categoryId, filter: $filter) { - entries { - id - } - totalCount - } - } - """ - test "other user can get other user's paged favoritedPosts", - ~m(user_conn guest_conn posts)a do - {:ok, user} = db_insert(:user) - - Enum.each(posts, fn post -> - {:ok, _} = CMS.reaction(:post, :favorite, post.id, user) - end) - - variables = %{userId: user.id, filter: %{page: 1, size: 20}} - results = user_conn |> query_result(@query, variables, "favoritedPosts") - results2 = guest_conn |> query_result(@query, variables, "favoritedPosts") - - assert results["totalCount"] == @total_count - assert results2["totalCount"] == @total_count - end - - alias GroupherServer.Accounts - - test "can get paged favoritedPosts on a spec category", ~m(user_conn guest_conn posts)a do - {:ok, user} = db_insert(:user) - - Enum.each(posts, fn post -> - {:ok, _} = CMS.reaction(:post, :favorite, post.id, user) - end) - - post1 = Enum.at(posts, 0) - post2 = Enum.at(posts, 1) - post3 = Enum.at(posts, 2) - post4 = Enum.at(posts, 4) - - test_category = "test category" - test_category2 = "test category2" - - {:ok, category} = Accounts.create_favorite_category(user, %{title: test_category}) - {:ok, category2} = Accounts.create_favorite_category(user, %{title: test_category2}) - - {:ok, _favorites_category} = Accounts.set_favorites(user, :post, post1.id, category.id) - {:ok, _favorites_category} = Accounts.set_favorites(user, :post, post2.id, category.id) - {:ok, _favorites_category} = Accounts.set_favorites(user, :post, post3.id, category.id) - {:ok, _favorites_category} = Accounts.set_favorites(user, :post, post4.id, category2.id) - - variables = %{userId: user.id, categoryId: category.id, filter: %{page: 1, size: 20}} - results = user_conn |> query_result(@query, variables, "favoritedPosts") - results2 = guest_conn |> query_result(@query, variables, "favoritedPosts") - - assert results["totalCount"] == 3 - assert results["entries"] |> Enum.any?(&(&1["id"] == to_string(post1.id))) - assert results["entries"] |> Enum.any?(&(&1["id"] == to_string(post2.id))) - assert results["entries"] |> Enum.any?(&(&1["id"] == to_string(post3.id))) - - assert results == results2 - end - end -end diff --git a/test/groupher_server_web/query/accounts/favorited_repos_test.exs b/test/groupher_server_web/query/accounts/favorited_repos_test.exs deleted file mode 100644 index 835df53f9..000000000 --- a/test/groupher_server_web/query/accounts/favorited_repos_test.exs +++ /dev/null @@ -1,124 +0,0 @@ -defmodule GroupherServer.Test.Query.Accounts.FavritedRepos do - use GroupherServer.TestTools - - alias GroupherServer.CMS - - @total_count 20 - - setup do - {:ok, user} = db_insert(:user) - {:ok, repos} = db_insert_multi(:repo, @total_count) - - guest_conn = simu_conn(:guest) - user_conn = simu_conn(:user, user) - - {:ok, ~m(guest_conn user_conn user repos)a} - end - - describe "[accounts favorited repos]" do - @query """ - query($filter: PagedFilter!) { - user { - id - favoritedRepos(filter: $filter) { - entries { - id - } - totalCount - } - favoritedReposCount - } - } - """ - test "login user can get it's own favoritedRepos", ~m(user_conn user repos)a do - Enum.each(repos, fn repo -> - {:ok, _} = CMS.reaction(:repo, :favorite, repo.id, user) - end) - - random_id = repos |> Enum.shuffle() |> List.first() |> Map.get(:id) |> to_string - - variables = %{filter: %{page: 1, size: 20}} - results = user_conn |> query_result(@query, variables, "user") - assert results["favoritedRepos"] |> Map.get("totalCount") == @total_count - assert results["favoritedReposCount"] == @total_count - - assert results["favoritedRepos"] - |> Map.get("entries") - |> Enum.any?(&(&1["id"] == random_id)) - end - - @query """ - query($userId: ID!, $categoryId: ID,$filter: PagedFilter!) { - favoritedRepos(userId: $userId, categoryId: $categoryId, filter: $filter) { - entries { - id - } - totalCount - } - } - """ - test "other user can get other user's paged favoritedRepos", - ~m(user_conn guest_conn repos)a do - {:ok, user} = db_insert(:user) - - Enum.each(repos, fn repo -> - {:ok, _} = CMS.reaction(:repo, :favorite, repo.id, user) - end) - - variables = %{userId: user.id, filter: %{page: 1, size: 20}} - results = user_conn |> query_result(@query, variables, "favoritedRepos") - results2 = guest_conn |> query_result(@query, variables, "favoritedRepos") - - assert results["totalCount"] == @total_count - assert results2["totalCount"] == @total_count - end - - test "login user can get self paged favoritedRepos", ~m(user_conn user repos)a do - Enum.each(repos, fn repo -> - {:ok, _} = CMS.reaction(:repo, :favorite, repo.id, user) - end) - - variables = %{userId: user.id, filter: %{page: 1, size: 20}} - results = user_conn |> query_result(@query, variables, "favoritedRepos") - - assert results["totalCount"] == @total_count - end - - alias GroupherServer.Accounts - - test "can get paged favoritedRepos on a spec category", ~m(user_conn guest_conn repos)a do - {:ok, user} = db_insert(:user) - - Enum.each(repos, fn repo -> - {:ok, _} = CMS.reaction(:repo, :favorite, repo.id, user) - end) - - repo1 = Enum.at(repos, 0) - repo2 = Enum.at(repos, 1) - repo3 = Enum.at(repos, 2) - repo4 = Enum.at(repos, 4) - - test_category = "test category" - test_category2 = "test category2" - - {:ok, category} = Accounts.create_favorite_category(user, %{title: test_category}) - {:ok, category2} = Accounts.create_favorite_category(user, %{title: test_category2}) - - {:ok, _favorites_category} = Accounts.set_favorites(user, :repo, repo1.id, category.id) - {:ok, _favorites_category} = Accounts.set_favorites(user, :repo, repo2.id, category.id) - {:ok, _favorites_category} = Accounts.set_favorites(user, :repo, repo3.id, category.id) - {:ok, _favorites_category} = Accounts.set_favorites(user, :repo, repo4.id, category2.id) - - variables = %{userId: user.id, categoryId: category.id, filter: %{page: 1, size: 20}} - results = user_conn |> query_result(@query, variables, "favoritedRepos") - results2 = guest_conn |> query_result(@query, variables, "favoritedRepos") - - assert results["totalCount"] == 3 - assert results["entries"] |> Enum.any?(&(&1["id"] == to_string(repo1.id))) - assert results["entries"] |> Enum.any?(&(&1["id"] == to_string(repo2.id))) - assert results["entries"] |> Enum.any?(&(&1["id"] == to_string(repo3.id))) - - assert results == results2 - end - end -end diff --git a/test/groupher_server_web/query/accounts/stared_contents_test.exs b/test/groupher_server_web/query/accounts/stared_contents_test.exs deleted file mode 100644 index 3d17b411e..000000000 --- a/test/groupher_server_web/query/accounts/stared_contents_test.exs +++ /dev/null @@ -1,136 +0,0 @@ -defmodule GroupherServer.Test.Query.Accounts.StaredContents do - use GroupherServer.TestTools - - alias GroupherServer.CMS - - @total_count 20 - - setup do - {:ok, user} = db_insert(:user) - {:ok, posts} = db_insert_multi(:post, @total_count) - {:ok, jobs} = db_insert_multi(:job, @total_count) - - guest_conn = simu_conn(:guest) - user_conn = simu_conn(:user, user) - - {:ok, ~m(guest_conn user_conn user posts jobs)a} - end - - describe "[accounts stared posts]" do - @query """ - query($filter: PagedFilter!) { - user { - id - staredPosts(filter: $filter) { - entries { - id - } - totalCount - } - staredPostsCount - } - } - """ - test "login user can get it's own staredPosts", ~m(user_conn user posts)a do - Enum.each(posts, fn post -> - {:ok, _} = CMS.reaction(:post, :star, post.id, user) - end) - - random_id = posts |> Enum.shuffle() |> List.first() |> Map.get(:id) |> to_string - - variables = %{filter: %{page: 1, size: 20}} - results = user_conn |> query_result(@query, variables, "user") - assert results["staredPosts"] |> Map.get("totalCount") == @total_count - assert results["staredPostsCount"] == @total_count - - assert results["staredPosts"] - |> Map.get("entries") - |> Enum.any?(&(&1["id"] == random_id)) - end - - @query """ - query($userId: ID, $filter: PagedFilter!) { - staredPosts(userId: $userId, filter: $filter) { - entries { - id - } - totalCount - } - } - """ - test "other user can get other user's paged staredPosts", - ~m(user_conn guest_conn posts)a do - {:ok, user} = db_insert(:user) - - Enum.each(posts, fn post -> - {:ok, _} = CMS.reaction(:post, :star, post.id, user) - end) - - variables = %{userId: user.id, filter: %{page: 1, size: 20}} - results = user_conn |> query_result(@query, variables, "staredPosts") - results2 = guest_conn |> query_result(@query, variables, "staredPosts") - - assert results["totalCount"] == @total_count - assert results2["totalCount"] == @total_count - end - end - - describe "[accounts stared jobs]" do - @query """ - query($filter: PagedFilter!) { - user { - id - staredJobs(filter: $filter) { - entries { - id - } - totalCount - } - staredJobsCount - } - } - """ - test "login user can get it's own staredJobs", ~m(user_conn user jobs)a do - Enum.each(jobs, fn job -> - {:ok, _} = CMS.reaction(:job, :star, job.id, user) - end) - - random_id = jobs |> Enum.shuffle() |> List.first() |> Map.get(:id) |> to_string - - variables = %{filter: %{page: 1, size: 20}} - results = user_conn |> query_result(@query, variables, "user") - assert results["staredJobs"] |> Map.get("totalCount") == @total_count - assert results["staredJobsCount"] == @total_count - - assert results["staredJobs"] - |> Map.get("entries") - |> Enum.any?(&(&1["id"] == random_id)) - end - - @query """ - query($userId: ID, $filter: PagedFilter!) { - staredJobs(userId: $userId, filter: $filter) { - entries { - id - } - totalCount - } - } - """ - test "other user can get other user's paged staredJobs", - ~m(user_conn guest_conn jobs)a do - {:ok, user} = db_insert(:user) - - Enum.each(jobs, fn job -> - {:ok, _} = CMS.reaction(:job, :star, job.id, user) - end) - - variables = %{userId: user.id, filter: %{page: 1, size: 20}} - results = user_conn |> query_result(@query, variables, "staredJobs") - results2 = guest_conn |> query_result(@query, variables, "staredJobs") - - assert results["totalCount"] == @total_count - assert results2["totalCount"] == @total_count - end - end -end diff --git a/test/groupher_server_web/query/accounts/upvoteed_articles_test.exs b/test/groupher_server_web/query/accounts/upvoteed_articles_test.exs new file mode 100644 index 000000000..e24580906 --- /dev/null +++ b/test/groupher_server_web/query/accounts/upvoteed_articles_test.exs @@ -0,0 +1,77 @@ +defmodule GroupherServer.Test.Query.Accounts.UpvotedArticles do + @moduledoc false + use GroupherServer.TestTools + + alias GroupherServer.CMS + + @total_count 20 + + setup do + {:ok, user} = db_insert(:user) + {:ok, posts} = db_insert_multi(:post, @total_count) + {:ok, jobs} = db_insert_multi(:job, @total_count) + + guest_conn = simu_conn(:guest) + user_conn = simu_conn(:user, user) + + {:ok, ~m(guest_conn user_conn user posts jobs)a} + end + + describe "[accounts upvoted posts]" do + @query """ + query($userLogin: String!, $filter: UpvotedArticlesFilter!) { + pagedUpvotedArticles(userLogin: $userLogin, filter: $filter) { + entries { + id + title + thread + } + totalCount + } + } + """ + @tag :wip + test "both login and unlogin user can get one's paged upvoteded posts", + ~m(user_conn guest_conn posts)a do + {:ok, user} = db_insert(:user) + + Enum.each(posts, fn post -> + {:ok, _} = CMS.upvote_article(:post, post.id, user) + end) + + variables = %{ + userLogin: user.login, + filter: %{thread: "POST", page: 1, size: 20} + } + + results = user_conn |> query_result(@query, variables, "pagedUpvotedArticles") + results2 = guest_conn |> query_result(@query, variables, "pagedUpvotedArticles") + + assert results["totalCount"] == @total_count + assert results2["totalCount"] == @total_count + end + + @tag :wip + test "if no thread filter will get alll paged upvoteded articles", + ~m(guest_conn posts jobs)a do + {:ok, user} = db_insert(:user) + + Enum.each(posts, fn post -> + {:ok, _} = CMS.upvote_article(:post, post.id, user) + end) + + Enum.each(jobs, fn job -> + {:ok, _} = CMS.upvote_article(:job, job.id, user) + end) + + variables = %{ + userLogin: user.login, + filter: %{page: 1, size: 20} + } + + results = guest_conn |> query_result(@query, variables, "pagedUpvotedArticles") + + assert results["totalCount"] == @total_count + @total_count + end + end +end diff --git a/test/groupher_server_web/query/cms/article_reaction_users_test.exs b/test/groupher_server_web/query/cms/article_reaction_users_test.exs new file mode 100644 index 000000000..93b26c3cf --- /dev/null +++ b/test/groupher_server_web/query/cms/article_reaction_users_test.exs @@ -0,0 +1,122 @@ +defmodule GroupherServer.Test.Query.ArticleReactionUsers do + @moduledoc false + use GroupherServer.TestTools + + alias GroupherServer.CMS + + setup do + {:ok, post} = db_insert(:post) + {:ok, job} = db_insert(:job) + {:ok, user} = db_insert(:user) + {:ok, user2} = db_insert(:user) + + guest_conn = simu_conn(:guest) + user_conn = simu_conn(:user, user) + + {:ok, ~m(user_conn guest_conn user user2 post job)a} + end + + describe "[upvoted users]" do + @query """ + query( + $id: ID! + $thread: CmsThread + $filter: PagedFilter! + ) { + upvotedUsers(id: $id, thread: $thread, filter: $filter) { + entries { + id + avatar + nickname + } + totalPages + totalCount + pageSize + pageNumber + } + } + """ + @tag :wip + test "guest can get favroted user list after upvote to a post", + ~m(guest_conn post user user2)a do + {:ok, _} = CMS.upvote_article(:post, post.id, user) + {:ok, _} = CMS.upvote_article(:post, post.id, user2) + + variables = %{id: post.id, thread: "POST", filter: %{page: 1, size: 20}} + results = guest_conn |> query_result(@query, variables, "upvotedUsers") + + assert results |> is_valid_pagination? + assert results["totalCount"] == 2 + + assert user_exist_in?(user, results["entries"], :string_key) + assert user_exist_in?(user2, results["entries"], :string_key) + end + + @tag :wip + test "guest can get favroted user list after upvote to a job", + ~m(guest_conn job user user2)a do + {:ok, _} = CMS.upvote_article(:job, job.id, user) + {:ok, _} = CMS.upvote_article(:job, job.id, user2) + + variables = %{id: job.id, thread: "JOB", filter: %{page: 1, size: 20}} + results = guest_conn |> query_result(@query, variables, "upvotedUsers") + + assert results |> is_valid_pagination? + assert results["totalCount"] == 2 + + assert user_exist_in?(user, results["entries"], :string_key) + assert user_exist_in?(user2, results["entries"], :string_key) + end + end + + describe "[collect users]" do + @query """ + query( + $id: ID! + $thread: CmsThread + $filter: PagedFilter! + ) { + collectedUsers(id: $id, thread: $thread, filter: $filter) { + entries { + id + avatar + nickname + } + totalPages + totalCount + pageSize + pageNumber + } + } + """ + @tag :wip + test "guest can get stared user list after collect a post", ~m(guest_conn post user user2)a do + {:ok, _} = CMS.collect_article(:post, post.id, user) + {:ok, _} = CMS.collect_article(:post, post.id, user2) + + variables = %{id: post.id, thread: "POST", filter: %{page: 1, size: 20}} + results = guest_conn |> query_result(@query, variables, "collectedUsers") + + assert results |> is_valid_pagination? + assert results["totalCount"] == 2 + + assert user_exist_in?(user, results["entries"], :string_key) + assert user_exist_in?(user2, results["entries"], :string_key) + end + + @tag :wip + test "guest can get stared user list after collect a job", ~m(guest_conn job user user2)a do + {:ok, _} = CMS.collect_article(:job, job.id, user) + {:ok, _} = CMS.collect_article(:job, job.id, user2) + + variables = %{id: job.id, thread: "JOB", filter: %{page: 1, size: 20}} + results = guest_conn |> query_result(@query, variables, "collectedUsers") + + assert results |> is_valid_pagination? + assert results["totalCount"] == 2 + + assert user_exist_in?(user, results["entries"], :string_key) + assert user_exist_in?(user2, results["entries"], :string_key) + end + end +end diff --git a/test/groupher_server_web/query/cms/job_test.exs b/test/groupher_server_web/query/cms/job_test.exs index 9900fe36b..02ed33028 100644 --- a/test/groupher_server_web/query/cms/job_test.exs +++ b/test/groupher_server_web/query/cms/job_test.exs @@ -37,80 +37,4 @@ defmodule GroupherServer.Test.Query.Job do assert is_valid_kv?(results, "title", :string) assert is_valid_kv?(results, "body", :string) end - - @query """ - query($id: ID!) { - job(id: $id) { - id - favoritedUsers { - nickname - id - } - } - } - """ - test "job have favoritedUsers query field", ~m(user_conn job)a do - variables = %{id: job.id} - results = user_conn |> query_result(@query, variables, "job") - - assert results["id"] == to_string(job.id) - assert is_valid_kv?(results, "favoritedUsers", :list) - end - - @query """ - query($id: ID!) { - job(id: $id) { - id - title - body - viewerHasFavorited - } - } - """ - test "logged user can query viewerHasFavorited field", ~m(user_conn job)a do - variables = %{id: job.id} - - assert user_conn - |> query_result(@query, variables, "job") - |> has_boolen_value?("viewerHasFavorited") - end - - test "unlogged user can not query viewerHasFavorited field", ~m(guest_conn job)a do - variables = %{id: job.id} - - assert guest_conn |> query_get_error?(@query, variables) - end - - alias GroupherServer.Accounts - - @query """ - query($id: ID!) { - job(id: $id) { - id - favoritedCategoryId - } - } - """ - test "login user can get nil job favorited category id", ~m(job)a do - {:ok, user} = db_insert(:user) - user_conn = simu_conn(:user, user) - - variables = %{id: job.id} - result = user_conn |> query_result(@query, variables, "job") - assert result["favoritedCategoryId"] == nil - end - - test "login user can get job favorited category id after favorited", ~m(job)a do - {:ok, user} = db_insert(:user) - user_conn = simu_conn(:user, user) - - test_category = "test category" - {:ok, category} = Accounts.create_favorite_category(user, %{title: test_category}) - {:ok, _favorite_category} = Accounts.set_favorites(user, :job, job.id, category.id) - - variables = %{id: job.id} - result = user_conn |> query_result(@query, variables, "job") - - assert result["favoritedCategoryId"] == to_string(category.id) - end end diff --git a/test/groupher_server_web/query/cms/post_comment_test.exs b/test/groupher_server_web/query/cms/post_comment_test.exs index 39493126a..a33918d6a 100644 --- a/test/groupher_server_web/query/cms/post_comment_test.exs +++ b/test/groupher_server_web/query/cms/post_comment_test.exs @@ -199,119 +199,6 @@ defmodule GroupherServer.Test.Query.PostComment do assert results["totalCount"] == 30 end - test "MOST_LIKES filter should work", ~m(guest_conn post user community)a do - body = "test comment" - - comments = - Enum.reduce(1..10, [], fn _, acc -> - {:ok, value} = - CMS.create_comment(:post, post.id, %{community: community.raw, body: body}, user) - - acc ++ [value] - end) - - [comment_1, _comment_2, comment_3, _comment_last] = comments |> firstn_and_last(3) - {:ok, [user_1, user_2, user_3, user_4, user_5]} = db_insert_multi(:user, 5) - - # comment_3 is most likes - {:ok, _} = CMS.like_comment(:post_comment, comment_3.id, user_1) - {:ok, _} = CMS.like_comment(:post_comment, comment_3.id, user_2) - {:ok, _} = CMS.like_comment(:post_comment, comment_3.id, user_3) - {:ok, _} = CMS.like_comment(:post_comment, comment_3.id, user_4) - {:ok, _} = CMS.like_comment(:post_comment, comment_3.id, user_5) - - {:ok, _} = CMS.like_comment(:post_comment, comment_1.id, user_1) - {:ok, _} = CMS.like_comment(:post_comment, comment_1.id, user_2) - {:ok, _} = CMS.like_comment(:post_comment, comment_1.id, user_3) - {:ok, _} = CMS.like_comment(:post_comment, comment_1.id, user_4) - - variables = %{id: post.id, filter: %{page: 1, size: 10, sort: "MOST_LIKES"}} - results = guest_conn |> query_result(@query, variables, "pagedComments") - entries = results["entries"] - - assert entries |> Enum.at(0) |> Map.get("id") == to_string(comment_3.id) - assert entries |> Enum.at(0) |> Map.get("likesCount") == 5 - - assert entries |> Enum.at(1) |> Map.get("id") == to_string(comment_1.id) - assert entries |> Enum.at(1) |> Map.get("likesCount") == 4 - end - - @query """ - query($id: ID!, $filter: CommentsFilter!) { - pagedComments(id: $id, filter: $filter) { - entries { - id - viewerHasLiked - } - } - } - """ - test "login user can get hasLiked feedBack", ~m(user_conn post user community)a do - body = "test comment" - - {:ok, comment} = - CMS.create_comment(:post, post.id, %{community: community.raw, body: body}, user) - - {:ok, _like} = CMS.like_comment(:post_comment, comment.id, user) - - variables = %{id: post.id, filter: %{page: 1, size: 10}} - results = user_conn |> query_result(@query, variables, "pagedComments") - - found = - results["entries"] |> Enum.filter(&(&1["id"] == to_string(comment.id))) |> List.first() - - assert found["viewerHasLiked"] == false - - own_like_conn = simu_conn(:user, user) - results = own_like_conn |> query_result(@query, variables, "pagedComments") - - found = - results["entries"] |> Enum.filter(&(&1["id"] == to_string(comment.id))) |> List.first() - - assert found["viewerHasLiked"] == true - end - - @query """ - query($id: ID!, $filter: CommentsFilter!) { - pagedComments(id: $id, filter: $filter) { - entries { - id - body - author { - id - nickname - } - likesCount - likes { - id - nickname - } - } - } - } - """ - test "guest user can get likes info", ~m(guest_conn post user community)a do - body = "test comment" - - {:ok, comment} = - CMS.create_comment(:post, post.id, %{community: community.raw, body: body}, user) - - {:ok, _like} = CMS.like_comment(:post_comment, comment.id, user) - - variables = %{id: post.id, filter: %{page: 1, size: 10}} - results = guest_conn |> query_result(@query, variables, "pagedComments") - - found = - results["entries"] |> Enum.filter(&(&1["id"] == to_string(comment.id))) |> List.first() - - author = found |> Map.get("author") - - assert author["id"] == to_string(user.id) - assert found["likesCount"] == 1 - - assert found["likes"] |> Enum.any?(&(&1["id"] == to_string(user.id))) - end - @query """ query($id: ID!, $filter: CommentsFilter!) { pagedComments(id: $id, filter: $filter) { diff --git a/test/groupher_server_web/query/cms/post_test.exs b/test/groupher_server_web/query/cms/post_test.exs index 3d4b9e616..484f935f5 100644 --- a/test/groupher_server_web/query/cms/post_test.exs +++ b/test/groupher_server_web/query/cms/post_test.exs @@ -50,103 +50,4 @@ defmodule GroupherServer.Test.Query.Post do assert is_valid_kv?(results, "title", :string) assert is_valid_kv?(results, "body", :string) end - - @query """ - query($id: ID!) { - post(id: $id) { - id - favoritedUsers { - nickname - id - } - } - } - """ - test "post have favoritedUsers query field", ~m(user_conn post)a do - variables = %{id: post.id} - results = user_conn |> query_result(@query, variables, "post") - - assert results["id"] == to_string(post.id) - assert is_valid_kv?(results, "favoritedUsers", :list) - end - - @query """ - query($id: ID!) { - post(id: $id) { - id - title - body - viewerHasFavorited - } - } - """ - test "logged user can query viewerHasFavorited field", ~m(user_conn post)a do - variables = %{id: post.id} - - assert user_conn - |> query_result(@query, variables, "post") - |> has_boolen_value?("viewerHasFavorited") - end - - test "unlogged user can not query viewerHasFavorited field", ~m(guest_conn post)a do - variables = %{id: post.id} - - assert guest_conn |> query_get_error?(@query, variables, ecode(:account_login)) - end - - @query """ - query($id: ID!) { - post(id: $id) { - id - title - body - viewerHasStarred - } - } - """ - test "logged user can query viewerHasStarred field", ~m(user_conn post)a do - variables = %{id: post.id} - - assert user_conn - |> query_result(@query, variables, "post") - |> has_boolen_value?("viewerHasStarred") - end - - test "unlogged user can not query viewerHasStarred field", ~m(guest_conn post)a do - variables = %{id: post.id} - assert guest_conn |> query_get_error?(@query, variables, ecode(:account_login)) - end - - alias GroupherServer.Accounts - - @query """ - query($id: ID!) { - post(id: $id) { - id - favoritedCategoryId - } - } - """ - test "login user can get nil post favorited category id", ~m(post)a do - {:ok, user} = db_insert(:user) - user_conn = simu_conn(:user, user) - - variables = %{id: post.id} - result = user_conn |> query_result(@query, variables, "post") - assert result["favoritedCategoryId"] == nil - end - - test "login user can get post favorited category id after favorited", ~m(post)a do - {:ok, user} = db_insert(:user) - user_conn = simu_conn(:user, user) - - test_category = "test category" - {:ok, category} = Accounts.create_favorite_category(user, %{title: test_category}) - {:ok, _favorite_category} = Accounts.set_favorites(user, :post, post.id, category.id) - - variables = %{id: post.id} - result = user_conn |> query_result(@query, variables, "post") - - assert result["favoritedCategoryId"] == to_string(category.id) - end end diff --git a/test/groupher_server_web/query/cms/reaction_users_test.exs b/test/groupher_server_web/query/cms/reaction_users_test.exs deleted file mode 100644 index e7bed624b..000000000 --- a/test/groupher_server_web/query/cms/reaction_users_test.exs +++ /dev/null @@ -1,82 +0,0 @@ -defmodule GroupherServer.Test.Query.ReactionUsers do - use GroupherServer.TestTools - - alias GroupherServer.CMS - - setup do - {:ok, post} = db_insert(:post) - {:ok, job} = db_insert(:job) - {:ok, user} = db_insert(:user) - {:ok, user2} = db_insert(:user) - - guest_conn = simu_conn(:guest) - user_conn = simu_conn(:user, user) - - {:ok, ~m(user_conn guest_conn user user2 post job)a} - end - - @query """ - query( - $id: ID! - $thread: ReactThread - $action: ReactAction! - $filter: PagedFilter! - ) { - reactionUsers(id: $id, thread: $thread, action: $action, filter: $filter) { - entries { - id - avatar - nickname - } - totalPages - totalCount - pageSize - pageNumber - } - } - """ - describe "[favrotes users]" do - test "guest can get favroted user list after favrote to a post", - ~m(guest_conn post user user2)a do - {:ok, _} = CMS.reaction(:post, :favorite, post.id, user) - {:ok, _} = CMS.reaction(:post, :favorite, post.id, user2) - - variables = %{id: post.id, thread: "POST", action: "FAVORITE", filter: %{page: 1, size: 20}} - results = guest_conn |> query_result(@query, variables, "reactionUsers") - - assert results |> is_valid_pagination? - assert results["totalCount"] == 2 - assert results["entries"] |> Enum.any?(&(&1["id"] == to_string(user.id))) - assert results["entries"] |> Enum.any?(&(&1["id"] == to_string(user2.id))) - end - - test "guest can get favroted user list after favrote to a job", - ~m(guest_conn job user user2)a do - {:ok, _} = CMS.reaction(:job, :favorite, job.id, user) - {:ok, _} = CMS.reaction(:job, :favorite, job.id, user2) - - variables = %{id: job.id, thread: "JOB", action: "FAVORITE", filter: %{page: 1, size: 20}} - results = guest_conn |> query_result(@query, variables, "reactionUsers") - - assert results |> is_valid_pagination? - assert results["totalCount"] == 2 - assert results["entries"] |> Enum.any?(&(&1["id"] == to_string(user.id))) - assert results["entries"] |> Enum.any?(&(&1["id"] == to_string(user2.id))) - end - end - - describe "[stars users]" do - test "guest can get stared user list after star to a post", ~m(guest_conn post user user2)a do - {:ok, _} = CMS.reaction(:post, :star, post.id, user) - {:ok, _} = CMS.reaction(:post, :star, post.id, user2) - - variables = %{id: post.id, thread: "POST", action: "STAR", filter: %{page: 1, size: 20}} - results = guest_conn |> query_result(@query, variables, "reactionUsers") - - assert results |> is_valid_pagination? - assert results["totalCount"] == 2 - assert results["entries"] |> Enum.any?(&(&1["id"] == to_string(user.id))) - assert results["entries"] |> Enum.any?(&(&1["id"] == to_string(user2.id))) - end - end -end diff --git a/test/groupher_server_web/query/cms/repo_test.exs b/test/groupher_server_web/query/cms/repo_test.exs index f9e4514e9..af596f902 100644 --- a/test/groupher_server_web/query/cms/repo_test.exs +++ b/test/groupher_server_web/query/cms/repo_test.exs @@ -37,80 +37,4 @@ defmodule GroupherServer.Test.Query.Repo do assert is_valid_kv?(results, "title", :string) assert is_valid_kv?(results, "readme", :string) end - - @query """ - query($id: ID!) { - repo(id: $id) { - id - favoritedUsers { - nickname - id - } - } - } - """ - test "repo have favoritedUsers query field", ~m(user_conn repo)a do - variables = %{id: repo.id} - results = user_conn |> query_result(@query, variables, "repo") - - assert results["id"] == to_string(repo.id) - assert is_valid_kv?(results, "favoritedUsers", :list) - end - - @query """ - query($id: ID!) { - repo(id: $id) { - id - title - viewerHasFavorited - favoritedCount - } - } - """ - test "logged user can query viewerHasFavorited field", ~m(user_conn repo)a do - variables = %{id: repo.id} - - assert user_conn - |> query_result(@query, variables, "repo") - |> has_boolen_value?("viewerHasFavorited") - end - - test "unlogged user can not query viewerHasFavorited field", ~m(guest_conn repo)a do - variables = %{id: repo.id} - - assert guest_conn |> query_get_error?(@query, variables) - end - - alias GroupherServer.Accounts - - @query """ - query($id: ID!) { - repo(id: $id) { - id - favoritedCategoryId - } - } - """ - test "login user can get nil repo favorited category id", ~m(repo)a do - {:ok, user} = db_insert(:user) - user_conn = simu_conn(:user, user) - - variables = %{id: repo.id} - result = user_conn |> query_result(@query, variables, "repo") - assert result["favoritedCategoryId"] == nil - end - - test "login user can get repo favorited category id after favorited", ~m(repo)a do - {:ok, user} = db_insert(:user) - user_conn = simu_conn(:user, user) - - test_category = "test category" - {:ok, category} = Accounts.create_favorite_category(user, %{title: test_category}) - {:ok, _favorite_category} = Accounts.set_favorites(user, :repo, repo.id, category.id) - - variables = %{id: repo.id} - result = user_conn |> query_result(@query, variables, "repo") - - assert result["favoritedCategoryId"] == to_string(category.id) - end end diff --git a/test/helper/cache_test.exs b/test/helper/cache_test.exs index 904813f83..4d2db0c2a 100644 --- a/test/helper/cache_test.exs +++ b/test/helper/cache_test.exs @@ -4,42 +4,48 @@ defmodule GroupherServer.Test.Helper.Cache do use GroupherServerWeb.ConnCase, async: true alias Helper.Cache + @pool :common + describe "[cache test]" do + @tag :wip test "cache get unexsit key should get nil" do - assert {:error, nil} = Cache.get("no exsit") - assert {:error, nil} = Cache.get(:no_exsit) + assert {:error, nil} = Cache.get(@pool, "no exsit") + assert {:error, nil} = Cache.get(@pool, :no_exsit) end + @tag :wip test "cache put should work" do - assert {:error, nil} = Cache.get(:data) + assert {:error, nil} = Cache.get(@pool, :data) - assert {:ok, true} = Cache.put(:data, "value") - assert {:ok, "value"} = Cache.get(:data) + assert {:ok, true} = Cache.put(@pool, :data, "value") + assert {:ok, "value"} = Cache.get(@pool, :data) # can override - assert {:ok, true} = Cache.put(:data, :value) - assert {:ok, :value} = Cache.get(:data) + assert {:ok, true} = Cache.put(@pool, :data, :value) + assert {:ok, :value} = Cache.get(@pool, :data) # complex data - assert {:ok, true} = Cache.put("namespace.aaa.bbb", [1, %{a: "2"}]) - assert {:ok, [1, %{a: "2"}]} = Cache.get("namespace.aaa.bbb") + assert {:ok, true} = Cache.put(@pool, "namespace.aaa.bbb", [1, %{a: "2"}]) + assert {:ok, [1, %{a: "2"}]} = Cache.get(@pool, "namespace.aaa.bbb") end + @tag :wip test "cache can be clear" do - assert {:ok, true} = Cache.put(:data, "value") - assert {:ok, "value"} = Cache.get(:data) + assert {:ok, true} = Cache.put(@pool, :data, "value") + assert {:ok, "value"} = Cache.get(@pool, :data) - assert {:ok, _} = Cache.clear_all() - assert {:error, nil} = Cache.get(:data) + assert {:ok, _} = Cache.clear(@pool) + assert {:error, nil} = Cache.get(@pool, :data) end + @tag :wip test "cache expire should work" do - assert {:ok, true} = Cache.put(:data, "value", expire: 1000) - assert {:ok, "value"} = Cache.get(:data) - Process.sleep(900) - assert {:ok, "value"} = Cache.get(:data) - Process.sleep(1200) - assert {:error, nil} = Cache.get(:data) + assert {:ok, true} = Cache.put(@pool, :data, "value", expire_sec: 1) + assert {:ok, "value"} = Cache.get(@pool, :data) + Process.sleep(800) + assert {:ok, "value"} = Cache.get(@pool, :data) + Process.sleep(500) + assert {:error, nil} = Cache.get(@pool, :data) end end end diff --git a/test/helper/orm_test.exs b/test/helper/orm_test.exs index cb0559f5a..c7f3909ad 100644 --- a/test/helper/orm_test.exs +++ b/test/helper/orm_test.exs @@ -1,5 +1,5 @@ -defmodule GroupherServer.Test.Helper.ORMTest do - use GroupherServerWeb.ConnCase, async: true +defmodule GroupherServer.Test.Helper.ORM do + use GroupherServer.TestTools # TODO import Service.Utils move both helper and github import GroupherServer.Support.Factory @@ -19,9 +19,9 @@ defmodule GroupherServer.Test.Helper.ORMTest do {:ok, post: post} end - # def send_email(to \\ "someone") do + # def send_email(to \\ "someone") do # IO.inspect "send email.. #{to}" - # end + # end # describe "orm hooks" do # test "create hooks" do @@ -34,7 +34,7 @@ defmodule GroupherServer.Test.Helper.ORMTest do # ORM.create(User, user_attrs, %{after_success: [&Email.notify_admin/2, [user, :new_register] ] }) # true - # end + # end # end describe "[find/x find_by]" do @@ -43,8 +43,6 @@ defmodule GroupherServer.Test.Helper.ORMTest do assert found.id == post.id assert %Ecto.Association.NotLoaded{} = found.author - assert %Ecto.Association.NotLoaded{} = found.comments - assert %Ecto.Association.NotLoaded{} = found.favorites end test "find/2 fails with {:error, reason} style" do @@ -85,4 +83,32 @@ defmodule GroupherServer.Test.Helper.ORMTest do test "count should work" do assert @posts_count + 1 == ORM.count(Post) end + + describe "[embeds paginator]" do + test "filter should work" do + total_count = 100 + + list = + Enum.reduce(1..total_count, [], fn i, acc -> + acc ++ ["i-#{i}"] + end) + + filter = %{page: 1, size: 30} + result = ORM.embeds_paginater(list, filter) + + assert result |> is_valid_pagination?(:raw) + assert result.total_count == length(list) + assert result.page_number == 1 + assert is_list(result.entries) + assert result.entries |> List.first() == "i-1" + assert result.entries |> List.last() == "i-30" + + filter = %{page: 4, size: 30} + result = ORM.embeds_paginater(list, filter) + + assert result.page_number == 4 + assert result.entries |> List.first() == "i-91" + assert result.entries |> List.last() == "i-100" + end + end end diff --git a/test/support/assert_helper.ex b/test/support/assert_helper.ex index 115a72c74..8f97abe2a 100644 --- a/test/support/assert_helper.ex +++ b/test/support/assert_helper.ex @@ -6,6 +6,7 @@ defmodule GroupherServer.Test.AssertHelper do NOTE: we use POST in query_get, see https://github.com/coderplanets/coderplanets_server/issues/259 """ + import Helper.ErrorCode, only: [ecode: 1] import Phoenix.ConnTest import Helper.Utils, only: [map_key_stringify: 1, get_config: 2] @@ -20,6 +21,10 @@ defmodule GroupherServer.Test.AssertHelper do def non_exsit_id, do: 15_982_398_614 # def page_size, do: @page_size + def is_error?(reason, code) when is_list(reason) and is_atom(code) do + reason |> Keyword.get(:code) == ecode(code) + end + def assert_v(:inner_page_size), do: @inner_page_size def assert_v(:page_size), do: @page_size @@ -198,24 +203,19 @@ defmodule GroupherServer.Test.AssertHelper do @doc "check id is exsit in list of Map structure" @spec exist_in?(Map.t(), [Map.t()]) :: boolean def exist_in?(%{id: id}, list, :string_key) when is_list(list) do - list - |> Enum.filter(fn item -> item["id"] == to_string(id) end) - |> length - |> Kernel.==(1) + list |> Enum.any?(&(&1["id"] == to_string(id))) end def exist_in?(%{id: id}, list) when is_list(list) do - list - |> Enum.filter(fn item -> item.id == id end) - |> length - |> Kernel.==(1) + list |> Enum.any?(&(&1.id == id)) + end + + def user_exist_in?(%{id: id}, list, :string_key) when is_list(list) do + list |> Enum.any?(&(&1["id"] == to_string(id))) end # for embed user situation def user_exist_in?(%{login: login}, list) when is_list(list) do - list - |> Enum.filter(fn item -> item.login == login end) - |> length - |> Kernel.==(1) + list |> Enum.any?(&(&1.login == login)) end end